* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <config.h>
+#include <string.h>
#include "protocol.h"
-static GSList *scan(struct sr_dev_driver *di, GSList *options)
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
+ 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,
+};
+
+static const struct scpi_command cmdset_agilent[] = {
+ { DMM_CMD_SETUP_REMOTE, "\n", },
+ { DMM_CMD_SETUP_FUNC, "CONF:%s", },
+ { DMM_CMD_QUERY_FUNC, "CONF?", },
+ { DMM_CMD_START_ACQ, "MEAS", },
+ { DMM_CMD_STOP_ACQ, "ABORT", },
+ { DMM_CMD_QUERY_VALUE, "READ?", },
+ { DMM_CMD_QUERY_PREC, "CONF?", },
+ ALL_ZERO,
+};
+
+static const struct mqopt_item mqopts_agilent_5digit[] = {
+ { SR_MQ_VOLTAGE, SR_MQFLAG_DC, "VOLT:DC", "VOLT ", NO_DFLT_PREC, },
+ { SR_MQ_VOLTAGE, SR_MQFLAG_AC, "VOLT:AC", "VOLT:AC ", NO_DFLT_PREC, },
+ { SR_MQ_CURRENT, SR_MQFLAG_DC, "CURR:DC", "CURR ", NO_DFLT_PREC, },
+ { SR_MQ_CURRENT, SR_MQFLAG_AC, "CURR:AC", "CURR:AC ", NO_DFLT_PREC, },
+ { SR_MQ_RESISTANCE, 0, "RES", "RES ", NO_DFLT_PREC, },
+ { SR_MQ_RESISTANCE, SR_MQFLAG_FOUR_WIRE, "FRES", "FRES ", NO_DFLT_PREC, },
+ { SR_MQ_CONTINUITY, 0, "CONT", "CONT", -1, },
+ { SR_MQ_CAPACITANCE, 0, "CAP", "CAP ", NO_DFLT_PREC, },
+ { SR_MQ_VOLTAGE, SR_MQFLAG_DC | SR_MQFLAG_DIODE, "DIOD", "DIOD", -4, },
+ { SR_MQ_TEMPERATURE, 0, "TEMP", "TEMP ", NO_DFLT_PREC, },
+ { SR_MQ_FREQUENCY, 0, "FREQ", "FREQ ", NO_DFLT_PREC, },
+};
+
+SR_PRIV const struct scpi_dmm_model models[] = {
+ {
+ "Agilent", "34405A",
+ 1, 5, cmdset_agilent, ARRAY_AND_SIZE(mqopts_agilent_5digit),
+ scpi_dmm_get_meas_agilent,
+ },
+};
+
+static const struct scpi_dmm_model *is_compatible(const char *vendor, const char *model)
{
- struct drv_context *drvc;
- GSList *devices;
+ size_t i;
+ const struct scpi_dmm_model *entry;
+
+ for (i = 0; i < ARRAY_SIZE(models); i++) {
+ entry = &models[i];
+ if (!entry->vendor || !entry->model)
+ continue;
+ if (strcmp(vendor, entry->vendor) != 0)
+ continue;
+ if (strcmp(model, entry->model) != 0)
+ continue;
+ return entry;
+ }
- (void)options;
+ return NULL;
+}
- devices = NULL;
- drvc = di->context;
- drvc->instances = NULL;
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
+{
+ struct sr_scpi_hw_info *hw_info;
+ int ret;
+ const char *vendor;
+ const struct scpi_dmm_model *model;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ size_t i;
+ gchar *channel_name;
+
+ ret = sr_scpi_get_hw_id(scpi, &hw_info);
+ scpi_dmm_cmd_delay(scpi);
+ if (ret != SR_OK) {
+ sr_info("Could not get IDN response.");
+ return NULL;
+ }
+ vendor = sr_vendor_alias(hw_info->manufacturer);
+ model = is_compatible(vendor, hw_info->model);
+ if (!model) {
+ sr_scpi_hw_info_free(hw_info);
+ return NULL;
+ }
- /* TODO: scan for devices, either based on a SR_CONF_CONN option
- * or on a USB scan. */
+ sdi = g_malloc0(sizeof(*sdi));
+ sdi->vendor = g_strdup(hw_info->manufacturer);
+ sdi->model = g_strdup(hw_info->model);
+ sdi->version = g_strdup(hw_info->firmware_version);
+ sdi->serial_num = g_strdup(hw_info->serial_number);
+ sdi->conn = scpi;
+ sdi->driver = &scpi_dmm_driver_info;
+ sdi->inst_type = SR_INST_SCPI;
+ sr_scpi_hw_info_free(hw_info);
+
+ devc = g_malloc0(sizeof(*devc));
+ sdi->priv = devc;
+ devc->num_channels = model->num_channels;
+ devc->cmdset = model->cmdset;
+ devc->model = model;
+
+ for (i = 0; i < devc->num_channels; i++) {
+ channel_name = g_strdup_printf("P%zu", i + 1);
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, channel_name);
+ }
+
+ return sdi;
+}
- return devices;
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ return sr_scpi_scan(di->context, options, probe_device);
}
static int dev_open(struct sr_dev_inst *sdi)
{
- (void)sdi;
+ struct sr_scpi_dev_inst *scpi;
+ int ret;
- /* TODO: get handle from sdi->conn and open it. */
+ scpi = sdi->conn;
+ ret = sr_scpi_open(scpi);
+ if (ret < 0) {
+ sr_err("Failed to open SCPI device: %s.", sr_strerror(ret));
+ return SR_ERR;
+ }
return SR_OK;
}
static int dev_close(struct sr_dev_inst *sdi)
{
- (void)sdi;
+ struct sr_scpi_dev_inst *scpi;
- /* TODO: get handle from sdi->conn and close it. */
+ scpi = sdi->conn;
+ if (!scpi)
+ return SR_ERR_BUG;
- return SR_OK;
+ sr_dbg("DIAG: sdi->status %d.", sdi->status - SR_ST_NOT_FOUND);
+ if (sdi->status <= SR_ST_INACTIVE)
+ return SR_OK;
+
+ return sr_scpi_close(scpi);
}
static int config_get(uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
+ struct dev_context *devc;
+ enum sr_mq mq;
+ enum sr_mqflag mqflag;
+ GVariant *arr[2];
int ret;
- (void)sdi;
- (void)data;
(void)cg;
- ret = SR_OK;
+ devc = sdi->priv;
+
switch (key) {
- /* TODO */
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ return sr_sw_limits_config_get(&devc->limits, key, data);
+ case SR_CONF_MEASURED_QUANTITY:
+ ret = scpi_dmm_get_mq(sdi, &mq, &mqflag, NULL);
+ if (ret != SR_OK)
+ return ret;
+ arr[0] = g_variant_new_uint32(mq);
+ arr[1] = g_variant_new_uint64(mqflag);
+ *data = g_variant_new_tuple(arr, ARRAY_SIZE(arr));
+ return SR_OK;
default:
return SR_ERR_NA;
}
-
- return ret;
}
static int config_set(uint32_t key, GVariant *data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- int ret;
+ struct dev_context *devc;
+ enum sr_mq mq;
+ enum sr_mqflag mqflag;
+ GVariant *tuple_child;
- (void)sdi;
- (void)data;
(void)cg;
- ret = SR_OK;
+ devc = sdi->priv;
+
switch (key) {
- /* TODO */
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ return sr_sw_limits_config_set(&devc->limits, key, data);
+ case SR_CONF_MEASURED_QUANTITY:
+ tuple_child = g_variant_get_child_value(data, 0);
+ mq = g_variant_get_uint32(tuple_child);
+ tuple_child = g_variant_get_child_value(data, 1);
+ mqflag = g_variant_get_uint64(tuple_child);
+ g_variant_unref(tuple_child);
+ return scpi_dmm_set_mq(sdi, mq, mqflag);
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
-
- return ret;
}
static int config_list(uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- int ret;
+ struct dev_context *devc;
+ GVariant *gvar, *arr[2];
+ GVariantBuilder gvb;
+ size_t i;
- (void)sdi;
- (void)data;
(void)cg;
- ret = SR_OK;
+ devc = sdi ? sdi->priv : NULL;
+
switch (key) {
- /* TODO */
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_MEASURED_QUANTITY:
+ /* TODO Use std_gvar_measured_quantities() when available. */
+ if (!devc)
+ return SR_ERR_ARG;
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < devc->model->mqopt_size; i++) {
+ arr[0] = g_variant_new_uint32(devc->model->mqopts[i].mq);
+ arr[1] = g_variant_new_uint64(devc->model->mqopts[i].mqflag);
+ gvar = g_variant_new_tuple(arr, ARRAY_SIZE(arr));
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+ *data = g_variant_builder_end(&gvb);
+ return SR_OK;
default:
+ (void)devc;
return SR_ERR_NA;
}
-
- return ret;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
{
- /* TODO: configure hardware, reset acquisition state, set up
- * callbacks and send header packet. */
+ struct sr_scpi_dev_inst *scpi;
+ struct dev_context *devc;
+ int ret;
+ const char *command;
+
+ scpi = sdi->conn;
+ devc = sdi->priv;
+
+ ret = scpi_dmm_get_mq(sdi, &devc->start_acq_mq.curr_mq,
+ &devc->start_acq_mq.curr_mqflag, NULL);
+ if (ret != SR_OK)
+ return ret;
+
+ command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_START_ACQ);
+ if (command && *command) {
+ ret = sr_scpi_send(scpi, command);
+ scpi_dmm_cmd_delay(scpi);
+ if (ret != SR_OK)
+ return ret;
+ }
- (void)sdi;
+ sr_sw_limits_acquisition_start(&devc->limits);
+ ret = std_session_send_df_header(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 10,
+ scpi_dmm_receive_data, (void *)sdi);
+ if (ret != SR_OK)
+ return ret;
return SR_OK;
}
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- /* TODO: stop acquisition. */
+ struct sr_scpi_dev_inst *scpi;
+ struct dev_context *devc;
+ const char *command;
+
+ scpi = sdi->conn;
+ devc = sdi->priv;
- (void)sdi;
+ command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_STOP_ACQ);
+ if (command && *command) {
+ (void)sr_scpi_send(scpi, command);
+ scpi_dmm_cmd_delay(scpi);
+ }
+ sr_scpi_source_remove(sdi->session, scpi);
+
+ std_session_send_df_end(sdi);
return SR_OK;
}
.dev_acquisition_stop = dev_acquisition_stop,
.context = NULL,
};
-
SR_REGISTER_DEV_DRIVER(scpi_dmm_driver_info);
*/
#include <config.h>
+#include <math.h>
+#include <string.h>
#include "protocol.h"
+#define WITH_CMD_DELAY 0 /* TODO See which devices need delays. */
+
+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);
+}
+
+SR_PRIV const struct mqopt_item *scpi_dmm_lookup_mq_number(
+ const struct sr_dev_inst *sdi, enum sr_mq mq, enum sr_mqflag flag)
+{
+ struct dev_context *devc;
+ size_t i;
+ const struct mqopt_item *item;
+
+ devc = sdi->priv;
+ for (i = 0; i < devc->model->mqopt_size; i++) {
+ item = &devc->model->mqopts[i];
+ if (item->mq != mq || item->mqflag != flag)
+ continue;
+ return item;
+ }
+
+ return NULL;
+}
+
+SR_PRIV const struct mqopt_item *scpi_dmm_lookup_mq_text(
+ const struct sr_dev_inst *sdi, const char *text)
+{
+ struct dev_context *devc;
+ size_t i;
+ const struct mqopt_item *item;
+
+ devc = sdi->priv;
+ for (i = 0; i < devc->model->mqopt_size; i++) {
+ item = &devc->model->mqopts[i];
+ if (!item->scpi_func_query || !item->scpi_func_query[0])
+ continue;
+ if (!g_str_has_prefix(text, item->scpi_func_query))
+ continue;
+ return item;
+ }
+
+ return NULL;
+}
+
+SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi,
+ enum sr_mq *mq, enum sr_mqflag *flag, char **rsp)
+{
+ struct dev_context *devc;
+ const char *command;
+ char *response;
+ const char *have;
+ int ret;
+ const struct mqopt_item *item;
+
+ devc = sdi->priv;
+ if (mq)
+ *mq = 0;
+ if (flag)
+ *flag = 0;
+ if (rsp)
+ *rsp = NULL;
+
+ 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)
+ return SR_ERR_NA;
+ have = response;
+ if (*have == '"')
+ have++;
+
+ ret = SR_ERR_NA;
+ item = scpi_dmm_lookup_mq_text(sdi, have);
+ if (item) {
+ if (mq)
+ *mq = item->mq;
+ if (flag)
+ *flag = item->mqflag;
+ ret = SR_OK;
+ }
+
+ if (rsp) {
+ *rsp = response;
+ response = NULL;
+ }
+ g_free(response);
+
+ return ret;
+}
+
+SR_PRIV int scpi_dmm_set_mq(const struct sr_dev_inst *sdi,
+ enum sr_mq mq, enum sr_mqflag flag)
+{
+ struct dev_context *devc;
+ const struct mqopt_item *item;
+ const char *mode, *command;
+ int ret;
+
+ devc = sdi->priv;
+ item = scpi_dmm_lookup_mq_number(sdi, mq, flag);
+ if (!item)
+ return SR_ERR_NA;
+
+ mode = item->scpi_func_setup;
+ command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_SETUP_FUNC);
+ ret = sr_scpi_send(sdi->conn, command, mode);
+ scpi_dmm_cmd_delay(sdi->conn);
+
+ return ret;
+}
+
+SR_PRIV int scpi_dmm_get_meas_agilent(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;
+ char **fields;
+ size_t count;
+ char prec_text[20];
+ const struct mqopt_item *item;
+ int prec_exp;
+ const char *command;
+ char *response;
+ gboolean use_double;
+ int sig_digits, val_exp;
+ int digits;
+ enum sr_unit unit;
+
+ 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);
+ if (ret != SR_OK) {
+ g_free(mode_response);
+ return ret;
+ }
+ if (!mode_response)
+ return SR_ERR;
+ if (!mq) {
+ g_free(mode_response);
+ return +1;
+ }
+
+ /*
+ * Get the last comma separated field of the function query
+ * response, or fallback to the model's default precision for
+ * the current function. This copes with either of these cases:
+ * VOLT +1.00000E-01,+1.00000E-06
+ * DIOD
+ * TEMP THER,5000,+1.00000E+00,+1.00000E-01
+ */
+ p = sr_scpi_unquote_string(mode_response);
+ fields = g_strsplit(p, ",", 0);
+ count = g_strv_length(fields);
+ if (count >= 2) {
+ snprintf(prec_text, sizeof(prec_text),
+ "%s", fields[count - 1]);
+ p = prec_text;
+ } 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;
+ }
+ }
+ g_strfreev(fields);
+
+ /*
+ * Need to extract the exponent value ourselves, since a strtod()
+ * call will "eat" the exponent, too. Strip space, strip sign,
+ * strip float number (without! exponent), check for exponent
+ * and get exponent value. Accept absence of Esnn suffixes.
+ */
+ while (p && *p && g_ascii_isspace(*p))
+ p++;
+ if (p && *p && (*p == '+' || *p == '-'))
+ p++;
+ while (p && *p && g_ascii_isdigit(*p))
+ p++;
+ if (p && *p && *p == '.')
+ p++;
+ while (p && *p && g_ascii_isdigit(*p))
+ p++;
+ ret = SR_OK;
+ if (!p || !*p)
+ prec_exp = 0;
+ else if (*p != 'e' && *p != 'E')
+ ret = SR_ERR_DATA;
+ else
+ ret = sr_atoi(++p, &prec_exp);
+ g_free(mode_response);
+ if (ret != SR_OK)
+ return ret;
+
+ /*
+ * Get the measurement value. Make sure to strip trailing space
+ * or else number conversion may fail in fatal ways. Detect OL
+ * conditions. Determine the measurement's precision: Count the
+ * number of significant digits before the period, and get the
+ * exponent's value.
+ *
+ * The text presentation of values is like this:
+ * +1.09450000E-01
+ * Skip space/sign, count digits before the period, skip to the
+ * exponent, get exponent value.
+ *
+ * TODO Can sr_parse_rational() return the exponent for us? In
+ * addition to providing a precise rational value instead of a
+ * float that's an approximation of the received value? Can the
+ * 'analog' struct that we fill in carry rationals?
+ *
+ * Use double precision FP here during conversion. Optionally
+ * downgrade to single precision later to reduce the amount of
+ * logged information.
+ */
+ 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);
+ 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;
+ if (info->d_value > +9e37) {
+ info->d_value = +INFINITY;
+ } else if (info->d_value < -9e37) {
+ 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;
+ /*
+ * TODO Come up with the most appropriate 'digits' calculation.
+ * This implementation assumes that either the device provides
+ * the resolution with the query for the meter's function, or
+ * the driver uses a fallback text pretending the device had
+ * provided it. This works with supported Agilent devices.
+ *
+ * An alternative may be to assume a given digits count which
+ * depends on the device, and adjust that count based on the
+ * value's significant digits and exponent. But this approach
+ * fails if devices change their digits count depending on
+ * modes or user requests, and also fails when e.g. devices
+ * with "100000 counts" can provide values between 100000 and
+ * 120000 in either 4 or 5 digits modes, depending on the most
+ * recent trend of the values. This less robust approach should
+ * only be taken if the mode inquiry won't yield the resolution
+ * (as e.g. DIOD does on 34405A, though we happen to know the
+ * fixed resolution for this very mode on this very model).
+ *
+ * For now, let's keep the prepared code path for the second
+ * approach in place, should some Agilent devices need it yet
+ * benefit from re-using most of the remaining acquisition
+ * routine.
+ */
+#if 1
+ digits = -prec_exp;
+#else
+ digits = devc->model->digits;
+ digits -= sig_digits;
+ digits -= val_exp;
+#endif
+
+ /*
+ * 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->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;
+ 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:
+ unit = SR_UNIT_CELSIUS;
+ break;
+ case SR_MQ_FREQUENCY:
+ unit = SR_UNIT_HERTZ;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ analog->meaning->unit = unit;
+ analog->spec->spec_digits = digits;
+
+ return SR_OK;
+}
+
+/* Strictly speaking this is a timer controlled poll routine. */
SR_PRIV int scpi_dmm_receive_data(int fd, int revents, void *cb_data)
{
- const struct sr_dev_inst *sdi;
+ struct sr_dev_inst *sdi;
+ struct sr_scpi_dev_inst *scpi;
struct dev_context *devc;
+ struct scpi_dmm_acq_info *info;
+ gboolean sent_sample;
+ size_t ch;
+ struct sr_channel *channel;
+ int ret;
(void)fd;
+ (void)revents;
- if (!(sdi = cb_data))
+ sdi = cb_data;
+ if (!sdi)
return TRUE;
-
- if (!(devc = sdi->priv))
+ scpi = sdi->conn;
+ devc = sdi->priv;
+ if (!scpi || !devc)
return TRUE;
+ info = &devc->run_acq_info;
+
+ sent_sample = FALSE;
+ ret = SR_OK;
+ for (ch = 0; ch < devc->num_channels; ch++) {
+ /* Check the channel's enabled status. */
+ channel = g_slist_nth_data(sdi->channels, ch);
+ if (!channel->enabled)
+ continue;
+
+ /*
+ * Prepare an analog measurement value. Note that digits
+ * will get updated later.
+ */
+ info->packet.type = SR_DF_ANALOG;
+ info->packet.payload = &info->analog[ch];
+ sr_analog_init(&info->analog[ch], &info->encoding[ch],
+ &info->meaning[ch], &info->spec[ch], 0);
+
+ /* Just check OPC before sending another request. */
+ scpi_dmm_cmd_delay(sdi->conn);
- if (revents == G_IO_IN) {
- /* TODO */
+ /*
+ * Have the model take and interpret a measurement. Lack
+ * of support is pointless, failed retrieval/conversion
+ * is considered fatal. The routine will fill in the
+ * 'analog' details, except for channel name and sample
+ * count (assume one value per channel).
+ *
+ * Note that non-zero non-negative return codes signal
+ * that the channel's data shell get skipped in this
+ * iteration over the channels. This copes with devices
+ * or modes where channels may provide data at different
+ * rates.
+ */
+ if (!devc->model->get_measurement) {
+ ret = SR_ERR_NA;
+ break;
+ }
+ ret = devc->model->get_measurement(sdi, ch);
+ if (ret > 0)
+ continue;
+ if (ret != SR_OK)
+ break;
+
+ /* Send the packet that was filled in by the model's routine. */
+ info->analog[ch].num_samples = 1;
+ info->analog[ch].meaning->channels = g_slist_append(NULL, channel);
+ sr_session_send(sdi, &info->packet);
+ g_slist_free(info->analog[ch].meaning->channels);
+ sent_sample = TRUE;
+ }
+ if (sent_sample)
+ sr_sw_limits_update_samples_read(&devc->limits, 1);
+ if (ret != SR_OK) {
+ /* Stop acquisition upon communication or data errors. */
+ sr_dev_acquisition_stop(sdi);
+ return TRUE;
}
+ if (sr_sw_limits_check(&devc->limits))
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#ifndef LIBSIGROK_HARDWARE_SCPI_DMM_PROTOCOL_H
#define LIBSIGROK_HARDWARE_SCPI_DMM_PROTOCOL_H
-#include <stdint.h>
+#include <config.h>
#include <glib.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
+#include "scpi.h"
#define LOG_PREFIX "scpi-dmm"
+#define SCPI_DMM_MAX_CHANNELS 1
+
+enum scpi_dmm_cmdcode {
+ DMM_CMD_SETUP_REMOTE,
+ DMM_CMD_SETUP_FUNC,
+ DMM_CMD_QUERY_FUNC,
+ DMM_CMD_START_ACQ,
+ DMM_CMD_STOP_ACQ,
+ DMM_CMD_QUERY_VALUE,
+ DMM_CMD_QUERY_PREC,
+};
+
+struct mqopt_item {
+ enum sr_mq mq;
+ enum sr_mqflag mqflag;
+ const char *scpi_func_setup;
+ const char *scpi_func_query;
+ int default_precision;
+};
+#define NO_DFLT_PREC -99
+
+struct scpi_dmm_model {
+ const char *vendor;
+ const char *model;
+ size_t num_channels;
+ ssize_t digits;
+ const struct scpi_command *cmdset;
+ const struct mqopt_item *mqopts;
+ size_t mqopt_size;
+ int (*get_measurement)(const struct sr_dev_inst *sdi, size_t ch);
+};
+
struct dev_context {
+ size_t num_channels;
+ const struct scpi_command *cmdset;
+ const struct scpi_dmm_model *model;
+ struct sr_sw_limits limits;
+ struct {
+ enum sr_mq curr_mq;
+ enum sr_mqflag curr_mqflag;
+ } start_acq_mq;
+ struct scpi_dmm_acq_info {
+ float f_value;
+ double d_value;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog[SCPI_DMM_MAX_CHANNELS];
+ struct sr_analog_encoding encoding[SCPI_DMM_MAX_CHANNELS];
+ struct sr_analog_meaning meaning[SCPI_DMM_MAX_CHANNELS];
+ struct sr_analog_spec spec[SCPI_DMM_MAX_CHANNELS];
+ } run_acq_info;
};
+SR_PRIV void scpi_dmm_cmd_delay(struct sr_scpi_dev_inst *scpi);
+SR_PRIV const struct mqopt_item *scpi_dmm_lookup_mq_number(
+ const struct sr_dev_inst *sdi, enum sr_mq mq, enum sr_mqflag flag);
+SR_PRIV const struct mqopt_item *scpi_dmm_lookup_mq_text(
+ const struct sr_dev_inst *sdi, const char *text);
+SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi,
+ enum sr_mq *mq, enum sr_mqflag *flag, char **rsp);
+SR_PRIV int scpi_dmm_set_mq(const struct sr_dev_inst *sdi,
+ enum sr_mq mq, enum sr_mqflag flag);
+SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch);
SR_PRIV int scpi_dmm_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV struct sr_dev_driver scpi_dmm_driver_info;
+
#endif