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);