X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fscpi-dmm%2Fprotocol.c;h=d0cafd829f05d82463e1a91b8f388f65e76b3af8;hb=HEAD;hp=08c79b4a19be39bcffaf619b5e10c8c2e8ed5dbf;hpb=3cdad416e4231796e3b31380116404796622c6f5;p=libsigrok.git diff --git a/src/hardware/scpi-dmm/protocol.c b/src/hardware/scpi-dmm/protocol.c index 08c79b4a..a13db841 100644 --- a/src/hardware/scpi-dmm/protocol.c +++ b/src/hardware/scpi-dmm/protocol.c @@ -28,7 +28,9 @@ SR_PRIV void scpi_dmm_cmd_delay(struct sr_scpi_dev_inst *scpi) { if (WITH_CMD_DELAY) g_usleep(WITH_CMD_DELAY * 1000); - sr_scpi_get_opc(scpi); + + if (!scpi->no_opc_command) + sr_scpi_get_opc(scpi); } SR_PRIV const struct mqopt_item *scpi_dmm_lookup_mq_number( @@ -70,7 +72,8 @@ SR_PRIV const struct mqopt_item *scpi_dmm_lookup_mq_text( } SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi, - enum sr_mq *mq, enum sr_mqflag *flag, char **rsp) + enum sr_mq *mq, enum sr_mqflag *flag, char **rsp, + const struct mqopt_item **mqitem) { struct dev_context *devc; const char *command; @@ -86,17 +89,21 @@ SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi, *flag = 0; if (rsp) *rsp = NULL; + if (mqitem) + *mqitem = NULL; + scpi_dmm_cmd_delay(sdi->conn); command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_QUERY_FUNC); if (!command || !*command) return SR_ERR_NA; response = NULL; ret = sr_scpi_get_string(sdi->conn, command, &response); - scpi_dmm_cmd_delay(sdi->conn); if (ret != SR_OK) return ret; - if (!response || !*response) + if (!response || !*response) { + g_free(response); return SR_ERR_NA; + } have = response; if (*have == '"') have++; @@ -108,7 +115,11 @@ SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi, *mq = item->mq; if (flag) *flag = item->mqflag; + if (mqitem) + *mqitem = item; ret = SR_OK; + } else { + sr_warn("Unknown measurement quantity: %s", have); } if (rsp) { @@ -135,10 +146,127 @@ SR_PRIV int scpi_dmm_set_mq(const struct sr_dev_inst *sdi, mode = item->scpi_func_setup; command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_SETUP_FUNC); + scpi_dmm_cmd_delay(sdi->conn); ret = sr_scpi_send(sdi->conn, command, mode); + if (ret != SR_OK) + return ret; + if (item->drv_flags & FLAG_CONF_DELAY) + g_usleep(devc->model->conf_delay_us); + + return SR_OK; +} + +SR_PRIV const char *scpi_dmm_get_range_text(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + int ret; + const struct mqopt_item *mqitem; + gboolean is_auto; + char *response, *pos; + double range; + int digits; + + devc = sdi->priv; + + ret = scpi_dmm_get_mq(sdi, NULL, NULL, NULL, &mqitem); + if (ret != SR_OK) + return NULL; + if (!mqitem || !mqitem->scpi_func_setup) + return NULL; + if (mqitem->drv_flags & FLAG_NO_RANGE) + return NULL; + scpi_dmm_cmd_delay(sdi->conn); + ret = sr_scpi_cmd(sdi, devc->cmdset, 0, NULL, + DMM_CMD_QUERY_RANGE_AUTO, mqitem->scpi_func_setup); + if (ret != SR_OK) + return NULL; + ret = sr_scpi_get_bool(sdi->conn, NULL, &is_auto); + if (ret != SR_OK) + return NULL; + if (is_auto) + return "auto"; - return ret; + /* + * Get the response into a text buffer. The range value may be + * followed by a precision value separated by comma. Common text + * to number conversion support code may assume that the input + * text spans to the end of the text, need not accept trailing + * text which is not part of a number. + */ + scpi_dmm_cmd_delay(sdi->conn); + ret = sr_scpi_cmd(sdi, devc->cmdset, 0, NULL, + DMM_CMD_QUERY_RANGE, mqitem->scpi_func_setup); + if (ret != SR_OK) + return NULL; + response = NULL; + ret = sr_scpi_get_string(sdi->conn, NULL, &response); + if (ret != SR_OK) { + g_free(response); + return NULL; + } + pos = strchr(response, ','); + if (pos) + *pos = '\0'; + ret = sr_atod_ascii_digits(response, &range, &digits); + g_free(response); + if (ret != SR_OK) + return NULL; + snprintf(devc->range_text, sizeof(devc->range_text), "%lf", range); + return devc->range_text; +} + +SR_PRIV int scpi_dmm_set_range_from_text(const struct sr_dev_inst *sdi, + const char *range) +{ + struct dev_context *devc; + int ret; + const struct mqopt_item *item; + gboolean is_auto; + + devc = sdi->priv; + + if (!range || !*range) + return SR_ERR_ARG; + + ret = scpi_dmm_get_mq(sdi, NULL, NULL, NULL, &item); + if (ret != SR_OK) + return ret; + if (!item || !item->scpi_func_setup) + return SR_ERR_ARG; + if (item->drv_flags & FLAG_NO_RANGE) + return SR_ERR_NA; + + is_auto = g_ascii_strcasecmp(range, "auto") == 0; + scpi_dmm_cmd_delay(sdi->conn); + ret = sr_scpi_cmd(sdi, devc->cmdset, 0, NULL, DMM_CMD_SETUP_RANGE, + item->scpi_func_setup, is_auto ? "AUTO" : range); + if (ret != SR_OK) + return ret; + if (item->drv_flags & FLAG_CONF_DELAY) + g_usleep(devc->model->conf_delay_us); + + return SR_OK; +} + +SR_PRIV GVariant *scpi_dmm_get_range_text_list(const struct sr_dev_inst *sdi) +{ + GVariantBuilder gvb; + GVariant *list; + + (void)sdi; + + g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY); + /* TODO + * Add more items _when_ the connected device supports a fixed + * or known set of ranges. The Agilent protocol is flexible and + * tolerant, set requests accept any value, and the device will + * use an upper limit which is at least the specified value. + * The values are communicated as mere numbers without units. + */ + list = g_variant_builder_end(&gvb); + + return list; } SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) @@ -163,6 +291,7 @@ SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) int sig_digits, val_exp; int digits; enum sr_unit unit; + double limit; scpi = sdi->conn; devc = sdi->priv; @@ -173,7 +302,7 @@ SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) * Get the meter's current mode, keep the response around. * Skip the measurement if the mode is uncertain. */ - ret = scpi_dmm_get_mq(sdi, &mq, &mqflag, &mode_response); + ret = scpi_dmm_get_mq(sdi, &mq, &mqflag, &mode_response, &item); if (ret != SR_OK) { g_free(mode_response); return ret; @@ -200,17 +329,14 @@ SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) snprintf(prec_text, sizeof(prec_text), "%s", fields[count - 1]); p = prec_text; + } else if (!item) { + p = NULL; + } else if (item->default_precision == NO_DFLT_PREC) { + p = NULL; } else { - item = scpi_dmm_lookup_mq_number(sdi, mq, mqflag); - if (!item) { - p = NULL; - } else if (item->default_precision == NO_DFLT_PREC) { - p = NULL; - } else { - snprintf(prec_text, sizeof(prec_text), - "1e%d", item->default_precision); - p = prec_text; - } + snprintf(prec_text, sizeof(prec_text), + "1e%d", item->default_precision); + p = prec_text; } g_strfreev(fields); @@ -265,12 +391,12 @@ SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_QUERY_VALUE); if (!command || !*command) return SR_ERR_NA; - ret = sr_scpi_get_string(scpi, command, &response); scpi_dmm_cmd_delay(scpi); + ret = sr_scpi_get_string(scpi, command, &response); if (ret != SR_OK) return ret; g_strstrip(response); - use_double = devc->model->digits > 6; + use_double = devc->model->digits >= 6; ret = sr_atod_ascii(response, &info->d_value); if (ret != SR_OK) { g_free(response); @@ -278,9 +404,10 @@ SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) } if (!response) return SR_ERR; - if (info->d_value > +9e37) { + limit = 9e37; + if (info->d_value > +limit) { info->d_value = +INFINITY; - } else if (info->d_value < -9e37) { + } else if (info->d_value < -limit) { info->d_value = -INFINITY; } else { p = response; @@ -353,12 +480,6 @@ SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) analog->data = &info->f_value; analog->encoding->unitsize = sizeof(info->f_value); } - analog->encoding->is_float = TRUE; -#ifdef WORDS_BIGENDIAN - analog->encoding->is_bigendian = TRUE; -#else - analog->encoding->is_bigendian = FALSE; -#endif analog->encoding->digits = digits; analog->meaning->mq = mq; analog->meaning->mqflags = mqflag; @@ -382,6 +503,211 @@ SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) case SR_MQ_FREQUENCY: unit = SR_UNIT_HERTZ; break; + case SR_MQ_TIME: + unit = SR_UNIT_SECOND; + break; + default: + return SR_ERR_NA; + } + analog->meaning->unit = unit; + analog->spec->spec_digits = digits; + + return SR_OK; +} + +SR_PRIV int scpi_dmm_get_meas_gwinstek(const struct sr_dev_inst *sdi, size_t ch) +{ + struct sr_scpi_dev_inst *scpi; + struct dev_context *devc; + struct scpi_dmm_acq_info *info; + struct sr_datafeed_analog *analog; + int ret; + enum sr_mq mq; + enum sr_mqflag mqflag; + char *mode_response; + const char *p; + const struct mqopt_item *item; + const char *command; + char *response; + gboolean use_double; + double limit; + int sig_digits, val_exp; + int digits; + enum sr_unit unit; + int mmode; + + scpi = sdi->conn; + devc = sdi->priv; + info = &devc->run_acq_info; + analog = &info->analog[ch]; + + /* + * Get the meter's current mode, keep the response around. + * Skip the measurement if the mode is uncertain. + */ + ret = scpi_dmm_get_mq(sdi, &mq, &mqflag, &mode_response, &item); + if (ret != SR_OK) { + g_free(mode_response); + return ret; + } + if (!mode_response) + return SR_ERR; + if (!mq) { + g_free(mode_response); + return +1; + } + mmode = atoi(mode_response); + g_free(mode_response); + + /* + * Get the current reading from the meter. + */ + scpi_dmm_cmd_delay(scpi); + command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_QUERY_VALUE); + if (!command || !*command) + return SR_ERR_NA; + scpi_dmm_cmd_delay(scpi); + ret = sr_scpi_get_string(scpi, command, &response); + if (ret != SR_OK) + return ret; + g_strstrip(response); + use_double = devc->model->digits > 6; + ret = sr_atod_ascii(response, &info->d_value); + if (ret != SR_OK) { + g_free(response); + return ret; + } + if (!response) + return SR_ERR; + limit = 9e37; + if (devc->model->infinity_limit != 0.0) + limit = devc->model->infinity_limit; + if (info->d_value >= +limit) { + info->d_value = +INFINITY; + } else if (info->d_value <= -limit) { + info->d_value = -INFINITY; + } else { + p = response; + while (p && *p && g_ascii_isspace(*p)) + p++; + if (p && *p && (*p == '-' || *p == '+')) + p++; + sig_digits = 0; + while (p && *p && g_ascii_isdigit(*p)) { + sig_digits++; + p++; + } + if (p && *p && *p == '.') + p++; + while (p && *p && g_ascii_isdigit(*p)) + p++; + ret = SR_OK; + if (!p || !*p) + val_exp = 0; + else if (*p != 'e' && *p != 'E') + ret = SR_ERR_DATA; + else + ret = sr_atoi(++p, &val_exp); + } + g_free(response); + if (ret != SR_OK) + return ret; + + /* + * Make sure we report "INFINITY" when meter displays "0L". + */ + switch (mmode) { + case 7: + case 16: + /* In resitance modes 0L reads as 1.20000E8 or 1.99999E8. */ + limit = 1.2e8; + if (strcmp(devc->model->model, "GDM8255A") == 0) + limit = 1.99999e8; + if (info->d_value >= limit) + info->d_value = +INFINITY; + break; + case 13: + /* In continuity mode 0L reads as 1.20000E3. */ + if (info->d_value >= 1.2e3) + info->d_value = +INFINITY; + break; + case 17: + /* In diode mode 0L reads as 1.00000E0. */ + if (info->d_value == 1.0e0) + info->d_value = +INFINITY; + break; + } + + /* + * Calculate 'digits' based on the result of the optional + * precision reading which was done at acquisition start. + * The GW-Instek manual gives the following information + * regarding the resolution: + * + * Type Digit + * -------- ------ + * Slow 5 1/2 + * Medium 4 1/2 + * Fast 3 1/2 + */ + digits = devc->model->digits; + if (devc->precision && *devc->precision) { + if (g_str_has_prefix(devc->precision, "Slow")) + digits = 6; + else if (g_str_has_prefix(devc->precision, "Mid")) + digits = 5; + else if (g_str_has_prefix(devc->precision, "Fast")) + digits = 4; + else + sr_info("Unknown precision: '%s'", devc->precision); + } + + /* + * Fill in the 'analog' description: value, encoding, meaning. + * Callers will fill in the sample count, and channel name, + * and will send out the packet. + */ + if (use_double) { + analog->data = &info->d_value; + analog->encoding->unitsize = sizeof(info->d_value); + } else { + info->f_value = info->d_value; + analog->data = &info->f_value; + analog->encoding->unitsize = sizeof(info->f_value); + } + analog->encoding->digits = digits; + analog->meaning->mq = mq; + analog->meaning->mqflags = mqflag; + switch (mq) { + case SR_MQ_VOLTAGE: + unit = SR_UNIT_VOLT; + break; + case SR_MQ_CURRENT: + unit = SR_UNIT_AMPERE; + break; + case SR_MQ_RESISTANCE: + case SR_MQ_CONTINUITY: + unit = SR_UNIT_OHM; + break; + case SR_MQ_CAPACITANCE: + unit = SR_UNIT_FARAD; + break; + case SR_MQ_TEMPERATURE: + switch (mmode) { + case 15: + unit = SR_UNIT_FAHRENHEIT; + break; + case 9: + default: + unit = SR_UNIT_CELSIUS; + } + break; + case SR_MQ_FREQUENCY: + unit = SR_UNIT_HERTZ; + break; + case SR_MQ_TIME: + unit = SR_UNIT_SECOND; + break; default: return SR_ERR_NA; }