]> sigrok.org Git - libsigrok.git/commitdiff
scpi-dmm: add support to get/set range on Agilent protocol using meters
authorGerhard Sittig <redacted>
Thu, 20 May 2021 20:00:47 +0000 (22:00 +0200)
committerGerhard Sittig <redacted>
Sat, 22 May 2021 06:35:11 +0000 (08:35 +0200)
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
src/hardware/scpi-dmm/protocol.c
src/hardware/scpi-dmm/protocol.h

index 9819eb0098238dbd5b55191b34104e4e6b49324a..32156243af63cb7c199d0ef96ba52e5e3f5b02c8 100644 (file)
@@ -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;
index 279aadf56410cc78213c6d51361f4df8a129f1e1..8c7b3373dc90f97143d1f473119e178dfae01e58 100644 (file)
@@ -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;
index 6af9496940aab6d62b53d33741fa2080b79b878e..8146c4555402511f8532dc479778e4472e1f9b80 100644 (file)
@@ -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);