From a0418c20d8eebb4ed127b04256e1b7a1dc2bdc7e Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Thu, 20 May 2021 22:00:47 +0200 Subject: [PATCH] scpi-dmm: add support to get/set range on Agilent protocol using meters Add support to get and set the auto/manual range on Agilent protocol speaking devices (34405A, 34465A). The range values are mere numbers without a unit or MQ associated with them. Support to list available ranges is prepared but not used (not needed on these meters). Common logic could open code the lists of ranges from the model description if desired in a future implementation. --- src/hardware/scpi-dmm/api.c | 46 ++++++++++++- src/hardware/scpi-dmm/protocol.c | 108 +++++++++++++++++++++++++++++++ src/hardware/scpi-dmm/protocol.h | 13 ++++ 3 files changed, 165 insertions(+), 2 deletions(-) diff --git a/src/hardware/scpi-dmm/api.c b/src/hardware/scpi-dmm/api.c index 9819eb00..32156243 100644 --- a/src/hardware/scpi-dmm/api.c +++ b/src/hardware/scpi-dmm/api.c @@ -39,6 +39,15 @@ static const uint32_t devopts_generic[] = { SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, }; +static const uint32_t devopts_generic_range[] = { + SR_CONF_CONTINUOUS, + SR_CONF_CONN | SR_CONF_GET, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, + SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_RANGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +}; + static const struct scpi_command cmdset_agilent[] = { { DMM_CMD_SETUP_REMOTE, "\n", }, { DMM_CMD_SETUP_FUNC, "CONF:%s", }, @@ -47,6 +56,10 @@ static const struct scpi_command cmdset_agilent[] = { { DMM_CMD_STOP_ACQ, "ABORT", }, { DMM_CMD_QUERY_VALUE, "READ?", }, { DMM_CMD_QUERY_PREC, "CONF?", }, + { DMM_CMD_QUERY_RANGE_AUTO, "%s:RANGE:AUTO?", }, + { DMM_CMD_QUERY_RANGE, "%s:RANGE?", }, + { DMM_CMD_SETUP_RANGE_AUTO, "%s:RANGE:AUTO ON", }, + { DMM_CMD_SETUP_RANGE, "%s:RANGE %s", }, ALL_ZERO, }; @@ -190,8 +203,9 @@ SR_PRIV const struct scpi_dmm_model models[] = { "Agilent", "34405A", 1, 5, cmdset_agilent, ARRAY_AND_SIZE(mqopts_agilent_34405a), scpi_dmm_get_meas_agilent, - ARRAY_AND_SIZE(devopts_generic), + ARRAY_AND_SIZE(devopts_generic_range), 0, 0, FALSE, + scpi_dmm_get_range_text, scpi_dmm_set_range_from_text, NULL, }, { "Agilent", "34410A", @@ -199,6 +213,7 @@ SR_PRIV const struct scpi_dmm_model models[] = { scpi_dmm_get_meas_agilent, ARRAY_AND_SIZE(devopts_generic), 0, 0, FALSE, + NULL, NULL, NULL, }, { "GW", "GDM8251A", @@ -206,6 +221,7 @@ SR_PRIV const struct scpi_dmm_model models[] = { scpi_dmm_get_meas_gwinstek, ARRAY_AND_SIZE(devopts_generic), 1000 * 2500, 0, FALSE, + NULL, NULL, NULL, }, { "GW", "GDM8255A", @@ -213,6 +229,7 @@ SR_PRIV const struct scpi_dmm_model models[] = { scpi_dmm_get_meas_gwinstek, ARRAY_AND_SIZE(devopts_generic), 1000 * 2500, 0, FALSE, + NULL, NULL, NULL, }, { "GWInstek", "GDM9060", @@ -220,6 +237,7 @@ SR_PRIV const struct scpi_dmm_model models[] = { scpi_dmm_get_meas_agilent, ARRAY_AND_SIZE(devopts_generic), 0, 0, FALSE, + NULL, NULL, NULL, }, { "GWInstek", "GDM9061", @@ -227,6 +245,7 @@ SR_PRIV const struct scpi_dmm_model models[] = { scpi_dmm_get_meas_agilent, ARRAY_AND_SIZE(devopts_generic), 0, 0, FALSE, + NULL, NULL, NULL, }, { "HP", "34401A", @@ -235,13 +254,15 @@ SR_PRIV const struct scpi_dmm_model models[] = { ARRAY_AND_SIZE(devopts_generic), /* 34401A: typ. 1020ms for AC readings (default is 1000ms). */ 1000 * 1500, 0, FALSE, + NULL, NULL, NULL, }, { "Keysight", "34465A", 1, 6, cmdset_agilent, ARRAY_AND_SIZE(mqopts_agilent_34405a), scpi_dmm_get_meas_agilent, - ARRAY_AND_SIZE(devopts_generic), + ARRAY_AND_SIZE(devopts_generic_range), 0, 0, FALSE, + scpi_dmm_get_range_text, scpi_dmm_set_range_from_text, NULL, }, { "OWON", "XDM2041", @@ -249,6 +270,7 @@ SR_PRIV const struct scpi_dmm_model models[] = { scpi_dmm_get_meas_gwinstek, ARRAY_AND_SIZE(devopts_generic), 0, 1e9, TRUE, + NULL, NULL, NULL, }, }; @@ -413,6 +435,7 @@ static int config_get(uint32_t key, GVariant **data, enum sr_mqflag mqflag; GVariant *arr[2]; int ret; + const char *range; (void)cg; @@ -435,6 +458,14 @@ static int config_get(uint32_t key, GVariant **data, arr[1] = g_variant_new_uint64(mqflag); *data = g_variant_new_tuple(arr, ARRAY_SIZE(arr)); return SR_OK; + case SR_CONF_RANGE: + if (!devc || !devc->model->get_range_text) + return SR_ERR_NA; + range = devc->model->get_range_text(sdi); + if (!range || !*range) + return SR_ERR_NA; + *data = g_variant_new_string(range); + return SR_OK; default: return SR_ERR_NA; } @@ -447,6 +478,7 @@ static int config_set(uint32_t key, GVariant *data, enum sr_mq mq; enum sr_mqflag mqflag; GVariant *tuple_child; + const char *range; (void)cg; @@ -464,6 +496,11 @@ static int config_set(uint32_t key, GVariant *data, mqflag = g_variant_get_uint64(tuple_child); g_variant_unref(tuple_child); return scpi_dmm_set_mq(sdi, mq, mqflag); + case SR_CONF_RANGE: + if (!devc || !devc->model->set_range_from_text) + return SR_ERR_NA; + range = g_variant_get_string(data, NULL); + return devc->model->set_range_from_text(sdi, range); default: return SR_ERR_NA; } @@ -503,6 +540,11 @@ static int config_list(uint32_t key, GVariant **data, } *data = g_variant_builder_end(&gvb); return SR_OK; + case SR_CONF_RANGE: + if (!devc || !devc->model->get_range_text_list) + return SR_ERR_NA; + *data = devc->model->get_range_text_list(sdi); + return SR_OK; default: (void)devc; return SR_ERR_NA; diff --git a/src/hardware/scpi-dmm/protocol.c b/src/hardware/scpi-dmm/protocol.c index 279aadf5..8c7b3373 100644 --- a/src/hardware/scpi-dmm/protocol.c +++ b/src/hardware/scpi-dmm/protocol.c @@ -154,6 +154,114 @@ SR_PRIV int scpi_dmm_set_mq(const struct sr_dev_inst *sdi, 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; + + 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"; + + /* + * 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; + + is_auto = g_ascii_strcasecmp(range, "auto") == 0; + scpi_dmm_cmd_delay(sdi->conn); + ret = sr_scpi_cmd(sdi, devc->cmdset, 0, NULL, + is_auto ? DMM_CMD_SETUP_RANGE_AUTO : DMM_CMD_SETUP_RANGE, + item->scpi_func_setup, is_auto ? "" : range); + if (ret != SR_OK) + return ret; + + 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) { struct sr_scpi_dev_inst *scpi; diff --git a/src/hardware/scpi-dmm/protocol.h b/src/hardware/scpi-dmm/protocol.h index 6af94969..8146c455 100644 --- a/src/hardware/scpi-dmm/protocol.h +++ b/src/hardware/scpi-dmm/protocol.h @@ -41,6 +41,10 @@ enum scpi_dmm_cmdcode { DMM_CMD_QUERY_VALUE, DMM_CMD_QUERY_PREC, DMM_CMD_SETUP_LOCAL, + DMM_CMD_QUERY_RANGE_AUTO, + DMM_CMD_QUERY_RANGE, + DMM_CMD_SETUP_RANGE_AUTO, + DMM_CMD_SETUP_RANGE, }; struct mqopt_item { @@ -66,6 +70,10 @@ struct scpi_dmm_model { unsigned int read_timeout_us; /* If zero, use default from src/scpi/scpi.c. */ float infinity_limit; /* If zero, use default from protocol.c */ gboolean check_opc; + const char *(*get_range_text)(const struct sr_dev_inst *sdi); + int (*set_range_from_text)(const struct sr_dev_inst *sdi, + const char *range); + GVariant *(*get_range_text_list)(const struct sr_dev_inst *sdi); }; struct dev_context { @@ -87,6 +95,7 @@ struct dev_context { struct sr_analog_spec spec[SCPI_DMM_MAX_CHANNELS]; } run_acq_info; gchar *precision; + char range_text[32]; }; SR_PRIV void scpi_dmm_cmd_delay(struct sr_scpi_dev_inst *scpi); @@ -99,6 +108,10 @@ SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi, const struct mqopt_item **mqitem); SR_PRIV int scpi_dmm_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq, enum sr_mqflag flag); +SR_PRIV const char *scpi_dmm_get_range_text(const struct sr_dev_inst *sdi); +SR_PRIV int scpi_dmm_set_range_from_text(const struct sr_dev_inst *sdi, + const char *range); +SR_PRIV GVariant *scpi_dmm_get_range_text_list(const struct sr_dev_inst *sdi); SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch); SR_PRIV int scpi_dmm_get_meas_gwinstek(const struct sr_dev_inst *sdi, size_t ch); SR_PRIV int scpi_dmm_receive_data(int fd, int revents, void *cb_data); -- 2.30.2