X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=hardware%2Ffluke-dmm%2Ffluke.c;h=edb6734adbd06166a1204cbd02012c16f77e060f;hb=85ca913cae0d9514949db8f638ecb4ef7bca706e;hp=809509755a8357e4e2b22b0a28b11c74aae606d3;hpb=d38d2ef0ce0e4ec49369e6cbfac616d9b1065c38;p=libsigrok.git diff --git a/hardware/fluke-dmm/fluke.c b/hardware/fluke-dmm/fluke.c index 80950975..edb6734a 100644 --- a/hardware/fluke-dmm/fluke.c +++ b/hardware/fluke-dmm/fluke.c @@ -1,5 +1,5 @@ /* - * This file is part of the sigrok project. + * This file is part of the libsigrok project. * * Copyright (C) 2012 Bert Vermeulen * @@ -17,34 +17,165 @@ * along with this program. If not, see . */ -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include "config.h" -#include "fluke-dmm.h" #include #include #include #include +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" +#include "fluke-dmm.h" + +static struct sr_datafeed_analog *handle_qm_18x(const struct sr_dev_inst *sdi, + char **tokens) +{ + struct sr_datafeed_analog *analog; + float fvalue; + char *e, *u; + gboolean is_oor; + + if (strcmp(tokens[0], "QM") || !tokens[1]) + return NULL; + + if ((e = strstr(tokens[1], "Out of range"))) { + is_oor = TRUE; + fvalue = -1; + while(*e && *e != '.') + e++; + } else { + is_oor = FALSE; + /* Delimit the float, since sr_atof_ascii() wants only + * a valid float here. */ + e = tokens[1]; + while(*e && *e != ' ') + e++; + *e++ = '\0'; + if (sr_atof_ascii(tokens[1], &fvalue) != SR_OK || fvalue == 0.0) { + /* Happens all the time, when switching modes. */ + sr_dbg("Invalid float."); + return NULL; + } + } + while(*e && *e == ' ') + e++; + + if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog)))) + return NULL; + if (!(analog->data = g_try_malloc(sizeof(float)))) + return NULL; + analog->channels = sdi->channels; + analog->num_samples = 1; + if (is_oor) + *analog->data = NAN; + else + *analog->data = fvalue; + analog->mq = -1; + + if ((u = strstr(e, "V DC")) || (u = strstr(e, "V AC"))) { + analog->mq = SR_MQ_VOLTAGE; + analog->unit = SR_UNIT_VOLT; + if (!is_oor && e[0] == 'm') + *analog->data /= 1000; + /* This catches "V AC", "V DC" and "V AC+DC". */ + if (strstr(u, "AC")) + analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS; + if (strstr(u, "DC")) + analog->mqflags |= SR_MQFLAG_DC; + } else if ((u = strstr(e, "dBV")) || (u = strstr(e, "dBm"))) { + analog->mq = SR_MQ_VOLTAGE; + if (u[2] == 'm') + analog->unit = SR_UNIT_DECIBEL_MW; + else + analog->unit = SR_UNIT_DECIBEL_VOLT; + analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS; + } else if ((u = strstr(e, "Ohms"))) { + analog->mq = SR_MQ_RESISTANCE; + analog->unit = SR_UNIT_OHM; + if (is_oor) + *analog->data = INFINITY; + else if (e[0] == 'k') + *analog->data *= 1000; + else if (e[0] == 'M') + *analog->data *= 1000000; + } else if (!strcmp(e, "nS")) { + analog->mq = SR_MQ_CONDUCTANCE; + analog->unit = SR_UNIT_SIEMENS; + *analog->data /= 1e+9; + } else if ((u = strstr(e, "Farads"))) { + analog->mq = SR_MQ_CAPACITANCE; + analog->unit = SR_UNIT_FARAD; + if (!is_oor) { + if (e[0] == 'm') + *analog->data /= 1e+3; + else if (e[0] == 'u') + *analog->data /= 1e+6; + else if (e[0] == 'n') + *analog->data /= 1e+9; + } + } else if ((u = strstr(e, "Deg C")) || (u = strstr(e, "Deg F"))) { + analog->mq = SR_MQ_TEMPERATURE; + if (u[4] == 'C') + analog->unit = SR_UNIT_CELSIUS; + else + analog->unit = SR_UNIT_FAHRENHEIT; + } else if ((u = strstr(e, "A AC")) || (u = strstr(e, "A DC"))) { + analog->mq = SR_MQ_CURRENT; + analog->unit = SR_UNIT_AMPERE; + /* This catches "A AC", "A DC" and "A AC+DC". */ + if (strstr(u, "AC")) + analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS; + if (strstr(u, "DC")) + analog->mqflags |= SR_MQFLAG_DC; + if (!is_oor) { + if (e[0] == 'm') + *analog->data /= 1e+3; + else if (e[0] == 'u') + *analog->data /= 1e+6; + } + } else if ((u = strstr(e, "Hz"))) { + analog->mq = SR_MQ_FREQUENCY; + analog->unit = SR_UNIT_HERTZ; + if (e[0] == 'k') + *analog->data *= 1e+3; + } else if (!strcmp(e, "%")) { + analog->mq = SR_MQ_DUTY_CYCLE; + analog->unit = SR_UNIT_PERCENTAGE; + } else if ((u = strstr(e, "ms"))) { + analog->mq = SR_MQ_PULSE_WIDTH; + analog->unit = SR_UNIT_SECOND; + *analog->data /= 1e+3; + } + if (analog->mq == -1) { + /* Not a valid measurement. */ + g_free(analog->data); + g_free(analog); + analog = NULL; + } + + return analog; +} -static struct sr_datafeed_analog *handle_qm_v2(const struct sr_dev_inst *sdi, +static struct sr_datafeed_analog *handle_qm_28x(const struct sr_dev_inst *sdi, char **tokens) { struct sr_datafeed_analog *analog; float fvalue; - char *eptr; - (void)sdi; - fvalue = strtof(tokens[0], &eptr); - if (fvalue == 0.0 && eptr == tokens[0]) { - sr_err("fluke-dmm: invalid float"); + if (!tokens[1]) + return NULL; + + if (sr_atof_ascii(tokens[0], &fvalue) != SR_OK || fvalue == 0.0) { + sr_err("Invalid float '%s'.", tokens[0]); return NULL; } - analog = g_try_malloc0(sizeof(struct sr_datafeed_analog)); + if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog)))) + return NULL; + if (!(analog->data = g_try_malloc(sizeof(float)))) + return NULL; + analog->channels = sdi->channels; analog->num_samples = 1; - analog->data = g_try_malloc(sizeof(float)); *analog->data = fvalue; analog->mq = -1; @@ -61,15 +192,22 @@ static struct sr_datafeed_analog *handle_qm_v2(const struct sr_dev_inst *sdi, *analog->data = NAN; } else analog->mq = -1; - } if (!strcmp(tokens[1], "CEL") || !strcmp(tokens[1], "FAR")) { + } else if (!strcmp(tokens[1], "dBV") || !strcmp(tokens[1], "dBm")) { + analog->mq = SR_MQ_VOLTAGE; + if (tokens[1][2] == 'm') + analog->unit = SR_UNIT_DECIBEL_MW; + else + analog->unit = SR_UNIT_DECIBEL_VOLT; + analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS; + } else if (!strcmp(tokens[1], "CEL") || !strcmp(tokens[1], "FAR")) { if (!strcmp(tokens[2], "NORMAL")) { analog->mq = SR_MQ_TEMPERATURE; if (tokens[1][0] == 'C') - analog->unit = SR_UNIT_CELSIUS; + analog->unit = SR_UNIT_CELSIUS; else analog->unit = SR_UNIT_FAHRENHEIT; } - } if (!strcmp(tokens[1], "OHM")) { + } else if (!strcmp(tokens[1], "OHM")) { if (!strcmp(tokens[3], "NONE")) { analog->mq = SR_MQ_RESISTANCE; analog->unit = SR_UNIT_OHM; @@ -86,7 +224,7 @@ static struct sr_datafeed_analog *handle_qm_v2(const struct sr_dev_inst *sdi, analog->unit = SR_UNIT_BOOLEAN; *analog->data = 1.0; } - } if (!strcmp(tokens[1], "F") + } else if (!strcmp(tokens[1], "F") && !strcmp(tokens[2], "NORMAL") && !strcmp(tokens[3], "NONE")) { analog->mq = SR_MQ_CAPACITANCE; @@ -94,7 +232,6 @@ static struct sr_datafeed_analog *handle_qm_v2(const struct sr_dev_inst *sdi, } else if (!strcmp(tokens[1], "AAC") || !strcmp(tokens[1], "ADC")) { analog->mq = SR_MQ_CURRENT; analog->unit = SR_UNIT_AMPERE; - analog->mqflags = SR_MQFLAG_RMS; if (!strcmp(tokens[2], "NORMAL")) { if (tokens[1][1] == 'A') { analog->mqflags |= SR_MQFLAG_AC; @@ -108,18 +245,17 @@ static struct sr_datafeed_analog *handle_qm_v2(const struct sr_dev_inst *sdi, } if (!strcmp(tokens[1], "Hz") && !strcmp(tokens[2], "NORMAL")) { analog->mq = SR_MQ_FREQUENCY; analog->unit = SR_UNIT_HERTZ; - } if (!strcmp(tokens[1], "PCT") && !strcmp(tokens[2], "NORMAL")) { + } else if (!strcmp(tokens[1], "PCT") && !strcmp(tokens[2], "NORMAL")) { analog->mq = SR_MQ_DUTY_CYCLE; analog->unit = SR_UNIT_PERCENTAGE; - } if (!strcmp(tokens[1], "S") && !strcmp(tokens[2], "NORMAL")) { + } else if (!strcmp(tokens[1], "S") && !strcmp(tokens[2], "NORMAL")) { analog->mq = SR_MQ_PULSE_WIDTH; analog->unit = SR_UNIT_SECOND; - } if (!strcmp(tokens[1], "SIE") && !strcmp(tokens[2], "NORMAL")) { + } else if (!strcmp(tokens[1], "SIE") && !strcmp(tokens[2], "NORMAL")) { analog->mq = SR_MQ_CONDUCTANCE; analog->unit = SR_UNIT_SIEMENS; } - if (analog->mq == -1) { /* Not a valid measurement. */ g_free(analog->data); @@ -130,20 +266,166 @@ static struct sr_datafeed_analog *handle_qm_v2(const struct sr_dev_inst *sdi, return analog; } +static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens) +{ + struct dev_context *devc; + int meas_type, meas_unit, meas_char, i; + + /* Make sure we have 7 valid tokens. */ + for (i = 0; tokens[i] && i < 7; i++); + if (i != 7) + return; + + if (strcmp(tokens[1], "1")) + /* Invalid measurement. */ + return; + + if (strcmp(tokens[2], "3")) + /* Only interested in input from the meter mode source. */ + return; + + devc = sdi->priv; + + /* Measurement type 11 == absolute, 19 = relative */ + meas_type = strtol(tokens[0], NULL, 10); + if (meas_type != 11 && meas_type != 19) + /* Device is in some mode we don't support. */ + return; + + /* We might get metadata for absolute and relative mode (if the device + * is in relative mode). In that case, relative takes precedence. */ + if (meas_type == 11 && devc->meas_type == 19) + return; + + meas_unit = strtol(tokens[3], NULL, 10); + if (meas_unit == 0) + /* Device is turned off. Really. */ + return; + meas_char = strtol(tokens[4], NULL, 10); + + devc->mq = devc->unit = -1; + devc->mqflags = 0; + switch (meas_unit) { + case 1: + devc->mq = SR_MQ_VOLTAGE; + devc->unit = SR_UNIT_VOLT; + if (meas_char == 1) + devc->mqflags |= SR_MQFLAG_DC; + else if (meas_char == 2) + devc->mqflags |= SR_MQFLAG_AC; + else if (meas_char == 3) + devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC; + else if (meas_char == 15) + devc->mqflags |= SR_MQFLAG_DIODE; + break; + case 2: + devc->mq = SR_MQ_CURRENT; + devc->unit = SR_UNIT_AMPERE; + if (meas_char == 1) + devc->mqflags |= SR_MQFLAG_DC; + else if (meas_char == 2) + devc->mqflags |= SR_MQFLAG_AC; + else if (meas_char == 3) + devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC; + break; + case 3: + if (meas_char == 1) { + devc->mq = SR_MQ_RESISTANCE; + devc->unit = SR_UNIT_OHM; + } else if (meas_char == 16) { + devc->mq = SR_MQ_CONTINUITY; + devc->unit = SR_UNIT_BOOLEAN; + } + break; + case 12: + devc->mq = SR_MQ_TEMPERATURE; + devc->unit = SR_UNIT_CELSIUS; + break; + case 13: + devc->mq = SR_MQ_TEMPERATURE; + devc->unit = SR_UNIT_FAHRENHEIT; + break; + default: + sr_dbg("unknown unit: %d", meas_unit); + } + if (devc->mq == -1 && devc->unit == -1) + return; + + /* If we got here, we know how to interpret the measurement. */ + devc->meas_type = meas_type; + if (meas_type == 11) + /* Absolute meter reading. */ + devc->is_relative = FALSE; + else if (!strcmp(tokens[0], "19")) + /* Relative meter reading. */ + devc->is_relative = TRUE; + +} + +static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens) +{ + struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + float fvalue; + + if (!strcmp(tokens[0], "9.9E+37")) { + /* An invalid measurement shows up on the display as "OL", but + * comes through like this. Since comparing 38-digit floats + * is rather problematic, we'll cut through this here. */ + fvalue = NAN; + } else { + if (sr_atof_ascii(tokens[0], &fvalue) != SR_OK || fvalue == 0.0) { + sr_err("Invalid float '%s'.", tokens[0]); + return; + } + } + + devc = sdi->priv; + if (devc->mq == -1 || devc->unit == -1) + /* Don't have valid metadata yet. */ + return; + + + if (devc->mq == SR_MQ_RESISTANCE && isnan(fvalue)) + fvalue = INFINITY; + else if (devc->mq == SR_MQ_CONTINUITY) { + if (isnan(fvalue)) + fvalue = 0.0; + else + fvalue = 1.0; + } + + analog.channels = sdi->channels; + analog.num_samples = 1; + analog.data = &fvalue; + analog.mq = devc->mq; + analog.unit = devc->unit; + analog.mqflags = 0; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + sr_session_send(devc->cb_data, &packet); + devc->num_samples++; + +} + static void handle_line(const struct sr_dev_inst *sdi) { struct dev_context *devc; + struct sr_serial_dev_inst *serial; struct sr_datafeed_packet packet; struct sr_datafeed_analog *analog; - char **tokens; + int num_tokens, n, i; + char cmd[16], **tokens; devc = sdi->priv; - sr_spew("fluke-dmm: received line '%s' (%d)", devc->buf, devc->buflen); + serial = sdi->conn; + sr_spew("Received line '%s' (%d).", devc->buf, devc->buflen); if (devc->buflen == 1) { if (devc->buf[0] != '0') { /* Not just a CMD_ACK from the query command. */ - sr_dbg("fluke-dmm: got CMD_ACK '%c'", devc->buf[0]); + sr_dbg("Got CMD_ACK '%c'.", devc->buf[0]); devc->expect_response = FALSE; } devc->buflen = 0; @@ -152,24 +434,43 @@ static void handle_line(const struct sr_dev_inst *sdi) analog = NULL; tokens = g_strsplit(devc->buf, ",", 0); - if (devc->profile->model == FLUKE_187) { - if (!strcmp(tokens[0], "QM")) { - /* Yes, this is the response to the query. */ + if (tokens[0]) { + if (devc->profile->model == FLUKE_187 || devc->profile->model == FLUKE_189) { devc->expect_response = FALSE; - sr_spew("187 line"); - /* TODO */ - devc->buflen = 0; + analog = handle_qm_18x(sdi, tokens); + } else if (devc->profile->model == FLUKE_287) { + devc->expect_response = FALSE; + analog = handle_qm_28x(sdi, tokens); + } else if (devc->profile->model == FLUKE_190) { + devc->expect_response = FALSE; + for (num_tokens = 0; tokens[num_tokens]; num_tokens++); + if (num_tokens >= 7) { + /* Response to QM: this is a comma-separated list of + * fields with metadata about the measurement. This + * format can return multiple sets of metadata, + * split into sets of 7 tokens each. */ + devc->meas_type = 0; + for (i = 0; i < num_tokens; i += 7) + handle_qm_19x_meta(sdi, tokens + i); + if (devc->meas_type) { + /* Slip the request in now, before the main + * timer loop asks for metadata again. */ + n = sprintf(cmd, "QM %d\r", devc->meas_type); + if (serial_write(serial, cmd, n) == -1) + sr_err("Unable to send QM (measurement): %s.", + strerror(errno)); + } + } else { + /* Response to QM measurement request. */ + handle_qm_19x_data(sdi, tokens); + } } - } else if (devc->profile->model == FLUKE_287) { - devc->expect_response = FALSE; - analog = handle_qm_v2(sdi, tokens); } g_strfreev(tokens); devc->buflen = 0; if (analog) { /* Got a measurement. */ - sr_dbg("val %f", *analog->data); packet.type = SR_DF_ANALOG; packet.payload = analog; sr_session_send(devc->cb_data, &packet); @@ -182,21 +483,25 @@ static void handle_line(const struct sr_dev_inst *sdi) SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data) { - const struct sr_dev_inst *sdi; + struct sr_dev_inst *sdi; struct dev_context *devc; + struct sr_serial_dev_inst *serial; int len; int64_t now, elapsed; + (void)fd; + if (!(sdi = cb_data)) return TRUE; if (!(devc = sdi->priv)) return TRUE; + serial = sdi->conn; if (revents == G_IO_IN) { /* Serial data arrived. */ while(FLUKEDMM_BUFSIZE - devc->buflen - 1 > 0) { - len = serial_read(fd, devc->buf + devc->buflen, 1); + len = serial_read(serial, devc->buf + devc->buflen, 1); if (len < 1) break; devc->buflen++; @@ -209,7 +514,7 @@ SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data) } } - if (devc->num_samples >= devc->limit_samples) { + if (devc->limit_samples && devc->num_samples >= devc->limit_samples) { sdi->driver->dev_acquisition_stop(sdi, cb_data); return TRUE; } @@ -217,18 +522,15 @@ SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data) now = g_get_monotonic_time() / 1000; elapsed = now - devc->cmd_sent_at; /* Send query command at poll_period interval, or after 1 second - * has elapsed. This will make it recover from any out-of-sync - * or temporary disconnect issues. */ + * has elapsed. This will make it easier to recover from any + * out-of-sync or temporary disconnect issues. */ if ((devc->expect_response == FALSE && elapsed > devc->profile->poll_period) - || elapsed > 1000) { - sr_spew("fluke-dmm: sending QM"); - if (serial_write(devc->serial->fd, "QM\r", 3) == -1) - sr_err("fluke-dmm: unable to send QM: %s", strerror(errno)); + || elapsed > devc->profile->timeout) { + if (serial_write(serial, "QM\r", 3) == -1) + sr_err("Unable to send QM: %s.", strerror(errno)); devc->cmd_sent_at = now; devc->expect_response = TRUE; } return TRUE; } - -