]> sigrok.org Git - libsigrok.git/commitdiff
rigol-dg: Initial Rigol DG1000z driver implementation.
authorTimo Kokkonen <redacted>
Sun, 6 Sep 2020 05:06:30 +0000 (22:06 -0700)
committerGerhard Sittig <redacted>
Mon, 28 Sep 2020 07:08:37 +0000 (09:08 +0200)
This implements support for Rigol DG1000z series digital signal
generators. Driver provides basic control via channel groups
("1", and "2"). Acquisition returns data from the built-in
frequency counter.

Supported models: DG1022Z, DG1032Z, DG1062Z

[ gsi: added some coding style adjustment ]

README.devices
src/hardware/rigol-dg/api.c
src/hardware/rigol-dg/protocol.c
src/hardware/rigol-dg/protocol.h

index 1183b460ac045a8bbcca7f60bc8bf5624f5d69de..a6ab698b2ea257b82fdbf557922dac61df1636ff 100644 (file)
@@ -136,6 +136,7 @@ The following drivers/devices do not need any firmware upload:
  - pce-322a
  - pipistrello-ols
  - rdtech-dps
+ - rigol-dg
  - rigol-ds
  - rohde-schwarz-sme-0x
  - scpi-dmm
index b3f089bab9a657c57c694bf03d3df9d5ad2aef9c..3ae40bcf72aa0f5086bb01988f5ee9113b2a7749 100644 (file)
  */
 
 #include <config.h>
+#include <string.h>
+#include "scpi.h"
 #include "protocol.h"
 
 static struct sr_dev_driver rigol_dg_driver_info;
 
-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_SIGNAL_GENERATOR,
+};
+
+static const uint32_t dg1000z_devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t dg1000z_devopts_cg[] = {
+       SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_PATTERN_MODE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_AMPLITUDE | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_OFFSET | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_PHASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_DUTY_CYCLE | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const double phase_min_max_step[] = { 0.0, 360.0, 0.001 };
+
+#define WAVEFORM_DEFAULT WFO_FREQUENCY | WFO_AMPLITUDE | WFO_OFFSET | WFO_PHASE
+
+static const struct waveform_spec dg1022z_waveforms[] = {
+       { "SIN",   WF_SINE,     1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT },
+       { "SQU",   WF_SQUARE,   1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE },
+       { "RAMP",  WF_RAMP,     1.0E-6, 0.5E+6, 1.0E-6, WAVEFORM_DEFAULT },
+       { "PULSE", WF_PULSE,    1.0E-6, 1.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE },
+       { "USER",  WF_ARB,      1.0E-6, 1.0E+7, 1.0E-6, WAVEFORM_DEFAULT },
+       { "NOISE", WF_NOISE,    2.5E+7, 2.5E+7, 0.0E-0, WFO_AMPLITUDE | WFO_OFFSET },
+       { "DC",    WF_DC,       0.0E-0, 0.0E+0, 0.0E-0, WFO_OFFSET },
+};
+
+static const struct channel_spec dg1022z_channels[] = {
+       { "CH1",  ARRAY_AND_SIZE(dg1022z_waveforms) },
+       { "CH2",  ARRAY_AND_SIZE(dg1022z_waveforms) },
+};
+
+static const struct waveform_spec dg1032z_waveforms[] = {
+       { "SIN",   WF_SINE,     1.0E-6, 3.0E+7, 1.0E-6, WAVEFORM_DEFAULT },
+       { "SQU",   WF_SQUARE,   1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE },
+       { "RAMP",  WF_RAMP,     1.0E-6, 0.5E+6, 1.0E-6, WAVEFORM_DEFAULT },
+       { "PULSE", WF_PULSE,    1.0E-6, 1.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE },
+       { "USER",  WF_ARB,      1.0E-6, 1.0E+7, 1.0E-6, WAVEFORM_DEFAULT },
+       { "NOISE", WF_NOISE,    3.0E+7, 3.0E+7, 0.0E-0, WFO_AMPLITUDE | WFO_OFFSET },
+       { "DC",    WF_DC,       0.0E-0, 0.0E+0, 0.0E-0, WFO_OFFSET },
+};
+
+static const struct channel_spec dg1032z_channels[] = {
+       { "CH1",  ARRAY_AND_SIZE(dg1032z_waveforms) },
+       { "CH2",  ARRAY_AND_SIZE(dg1032z_waveforms) },
+};
+
+static const struct waveform_spec dg1062z_waveforms[] = {
+       { "SIN",   WF_SINE,     1.0E-6, 6.0E+7, 1.0E-6, WAVEFORM_DEFAULT },
+       { "SQU",   WF_SQUARE,   1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE },
+       { "RAMP",  WF_RAMP,     1.0E-6, 1.0E+6, 1.0E-6, WAVEFORM_DEFAULT },
+       { "PULSE", WF_PULSE,    1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE },
+       { "USER",  WF_ARB,      1.0E-6, 2.0E+7, 1.0E-6, WAVEFORM_DEFAULT },
+       { "NOISE", WF_NOISE,    6.0E+7, 6.0E+7, 0.0E-0, WFO_AMPLITUDE | WFO_OFFSET },
+       { "DC",    WF_DC,       0.0E-0, 0.0E+0, 0.0E-0, WFO_OFFSET },
+};
+
+static const struct channel_spec dg1062z_channels[] = {
+       { "CH1",  ARRAY_AND_SIZE(dg1062z_waveforms) },
+       { "CH2",  ARRAY_AND_SIZE(dg1062z_waveforms) },
+};
+
+static const struct scpi_command cmdset_dg1000z[] = {
+       { PSG_CMD_SETUP_LOCAL, "SYST:KLOC:STATE OFF", },
+/*     { PSG_CMD_SELECT_CHANNEL, "SYST:CHAN:CUR CH%s", }, */
+       { PSG_CMD_GET_CHANNEL, "SYST:CHAN:CUR?", },
+       { PSG_CMD_GET_ENABLED, "OUTP%s:STATE?", },
+       { PSG_CMD_SET_ENABLE, "OUTP%s:STATE ON", },
+       { PSG_CMD_SET_DISABLE, "OUTP%s:STATE OFF", },
+       { PSG_CMD_GET_SOURCE, "SOUR%s:APPL?", },
+       { PSG_CMD_SET_SOURCE, "SOUR%s:APPL:%s", },
+       { PSG_CMD_GET_FREQUENCY, "SOUR%s:FREQ?", },
+       { PSG_CMD_SET_FREQUENCY, "SOUR%s:FREQ %f", },
+       { PSG_CMD_GET_AMPLITUDE, "SOUR%s:VOLT?", },
+       { PSG_CMD_SET_AMPLITUDE, "SOUR%s:VOLT %f", },
+       { PSG_CMD_GET_OFFSET, "SOUR%s:VOLT:OFFS?", },
+       { PSG_CMD_SET_OFFSET, "SOUR%s:VOLT:OFFS %f", },
+       { PSG_CMD_GET_PHASE, "SOUR%s:PHAS?", },
+       { PSG_CMD_SET_PHASE, "SOUR%s:PHAS %f", },
+       { PSG_CMD_GET_DCYCL_PULSE, "SOUR%s:FUNC:PULS:DCYC?", },
+       { PSG_CMD_SET_DCYCL_PULSE, "SOUR%s:FUNC:PULS:DCYC %f", },
+       { PSG_CMD_GET_DCYCL_SQUARE, "SOUR%s:FUNC:SQU:DCYC?", },
+       { PSG_CMD_SET_DCYCL_SQUARE, "SOUR%s:FUNC:SQU:DCYC %f", },
+       { PSG_CMD_COUNTER_GET_ENABLED, "COUN:STAT?", },
+       { PSG_CMD_COUNTER_SET_ENABLE, "COUN:STAT ON", },
+       { PSG_CMD_COUNTER_SET_DISABLE, "COUN:STAT OFF", },
+       { PSG_CMD_COUNTER_MEASURE, "COUN:MEAS?", },
+       ALL_ZERO
+};
+
+static const struct device_spec device_models[] = {
+       { "Rigol Technologies", "DG1022Z",
+               ARRAY_AND_SIZE(dg1000z_devopts),
+               ARRAY_AND_SIZE(dg1000z_devopts_cg),
+               ARRAY_AND_SIZE(dg1022z_channels),
+               cmdset_dg1000z,
+       },
+       { "Rigol Technologies", "DG1032Z",
+               ARRAY_AND_SIZE(dg1000z_devopts),
+               ARRAY_AND_SIZE(dg1000z_devopts_cg),
+               ARRAY_AND_SIZE(dg1032z_channels),
+               cmdset_dg1000z,
+       },
+       { "Rigol Technologies", "DG1062Z",
+               ARRAY_AND_SIZE(dg1000z_devopts),
+               ARRAY_AND_SIZE(dg1000z_devopts_cg),
+               ARRAY_AND_SIZE(dg1062z_channels),
+               cmdset_dg1000z,
+       },
+};
+
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 {
-       struct drv_context *drvc;
-       GSList *devices;
+       struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+       struct sr_scpi_hw_info *hw_info;
+       const struct device_spec *device;
+       const struct scpi_command *cmdset;
+       struct sr_channel *ch;
+       struct sr_channel_group *cg;
+       const char *command;
+       unsigned int i, ch_idx;
+       char tmp[16];
+
+       sdi = NULL;
+       devc = NULL;
+       hw_info = NULL;
+
+       if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK)
+               goto error;
+
+       device = NULL;
+       for (i = 0; i < ARRAY_SIZE(device_models); i++) {
+               if (g_ascii_strcasecmp(hw_info->manufacturer,
+                               device_models[i].vendor) != 0)
+                       continue;
+               if (g_ascii_strcasecmp(hw_info->model,
+                               device_models[i].model) != 0)
+                       continue;
+               device = &device_models[i];
+               cmdset = device_models[i].cmdset;
+               break;
+       }
+       if (!device)
+               goto error;
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       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 = &rigol_dg_driver_info;
+       sdi->inst_type = SR_INST_SCPI;
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       devc->cmdset = cmdset;
+       devc->device = device;
+       devc->ch_status = g_malloc0(sizeof(struct channel_status) *
+                       (device->num_channels + 1));
+       sr_sw_limits_init(&devc->limits);
+       sdi->priv = devc;
+
+       /* Create channel group and channel for each device channel. */
+       ch_idx = 0;
+       for (i = 0; i < device->num_channels; i++) {
+               ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE,
+                               device->channels[i].name);
+               cg = g_malloc0(sizeof(struct sr_channel_group));
+               snprintf(tmp, sizeof(tmp), "%u", i + 1);
+               cg->name = g_strdup(tmp);
+               cg->channels = g_slist_append(cg->channels, ch);
+
+               sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+       }
 
-       (void)options;
+       /* Create channels for the frequency counter output. */
+       ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, "FREQ1");
+       ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, "PERIOD1");
+       ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, "DUTY1");
+       ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, "WIDTH1");
+
+       /* Put device back to "local" mode, in case only a scan was done... */
+       command = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_SETUP_LOCAL);
+       if (command && *command) {
+               sr_scpi_get_opc(scpi);
+               sr_scpi_send(scpi, command);
+       }
+
+       sr_scpi_hw_info_free(hw_info);
 
-       devices = NULL;
-       drvc = di->context;
-       drvc->instances = NULL;
+       return sdi;
 
-       /* TODO: scan for devices, either based on a SR_CONF_CONN option
-        * or on a USB scan. */
+error:
+       sr_scpi_hw_info_free(hw_info);
+       g_free(devc);
+       sr_dev_inst_free(sdi);
 
-       return devices;
+       return NULL;
 }
 
-static int dev_open(struct sr_dev_inst *sdi)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       (void)sdi;
-
-       /* TODO: get handle from sdi->conn and open it. */
+       return sr_scpi_scan(di->context, options, probe_device);
+}
 
-       return SR_OK;
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       return sr_scpi_open(sdi->conn);
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
-       (void)sdi;
-
-       /* TODO: get handle from sdi->conn and close it. */
+       struct dev_context *devc;
+       struct sr_scpi_dev_inst *scpi;
+       const char *command;
+
+       devc = sdi->priv;
+       scpi = sdi->conn;
+       if (!scpi)
+               return SR_ERR_BUG;
+
+       /* Put unit back to "local" mode. */
+       command = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_SETUP_LOCAL);
+       if (command && *command) {
+               sr_scpi_get_opc(scpi);
+               sr_scpi_send(scpi, command);
+       }
 
-       return SR_OK;
+       return sr_scpi_close(sdi->conn);
 }
 
 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;
+       struct sr_scpi_dev_inst *scpi;
+       struct sr_channel *ch;
+       struct channel_status *ch_status;
+       const struct sr_key_info *kinfo;
+       uint32_t cmd;
        int ret;
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       if (!sdi || !data)
+               return SR_ERR_ARG;
 
+       devc = sdi->priv;
+       scpi = sdi->conn;
        ret = SR_OK;
-       switch (key) {
-       /* TODO */
-       default:
-               return SR_ERR_NA;
+       kinfo = sr_key_info_get(SR_KEY_CONFIG, key);
+
+       if (!cg) {
+               switch (key) {
+               case SR_CONF_LIMIT_SAMPLES:
+               case SR_CONF_LIMIT_MSEC:
+                       ret = sr_sw_limits_config_get(&devc->limits, key, data);
+                       break;
+               default:
+                       sr_dbg("%s: Unsupported key: %d (%s)", __func__,
+                               (int)key, (kinfo ? kinfo->name : "unknown"));
+                       ret = SR_ERR_NA;
+                       break;
+               }
+       } else {
+               ch = cg->channels->data;
+               ch_status = &devc->ch_status[ch->index];
+
+               switch (key) {
+               case SR_CONF_ENABLED:
+                       sr_scpi_get_opc(scpi);
+                       ret = sr_scpi_cmd_resp(sdi, devc->cmdset,
+                               PSG_CMD_SELECT_CHANNEL, cg->name, data,
+                               G_VARIANT_TYPE_BOOLEAN, PSG_CMD_GET_ENABLED,
+                               cg->name);
+                       break;
+               case SR_CONF_PATTERN_MODE:
+                       if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK) {
+                               *data = g_variant_new_string(
+                                        rigol_dg_waveform_to_string(
+                                                ch_status->wf));
+                       }
+                       break;
+               case SR_CONF_OUTPUT_FREQUENCY:
+                       if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK)
+                               *data = g_variant_new_double(ch_status->freq);
+                       break;
+               case SR_CONF_AMPLITUDE:
+                       if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK)
+                               *data = g_variant_new_double(ch_status->ampl);
+                       break;
+               case SR_CONF_OFFSET:
+                       if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK)
+                               *data = g_variant_new_double(ch_status->offset);
+                       break;
+               case SR_CONF_PHASE:
+                       if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK)
+                               *data = g_variant_new_double(ch_status->phase);
+                       break;
+               case SR_CONF_DUTY_CYCLE:
+                       if (ch_status->wf == WF_SQUARE) {
+                               cmd = PSG_CMD_GET_DCYCL_SQUARE;
+                       } else if (ch_status->wf == WF_PULSE) {
+                               cmd = PSG_CMD_GET_DCYCL_PULSE;
+                       } else {
+                               ret = SR_ERR_NA;
+                               break;
+                       }
+                       sr_scpi_get_opc(scpi);
+                       ret = sr_scpi_cmd_resp(sdi, devc->cmdset,
+                               PSG_CMD_SELECT_CHANNEL, cg->name, data,
+                               G_VARIANT_TYPE_DOUBLE, cmd, cg->name);
+                       break;
+               default:
+                       sr_dbg("%s: Unsupported (cg) key: %d (%s)", __func__,
+                               (int)key, (kinfo ? kinfo->name : "unknown"));
+                       ret = SR_ERR_NA;
+                       break;
+               }
        }
 
        return ret;
@@ -79,17 +361,128 @@ static int config_get(uint32_t key, GVariant **data,
 static int config_set(uint32_t key, GVariant *data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
+       struct dev_context *devc;
+       struct sr_scpi_dev_inst *scpi;
+       struct sr_channel *ch;
+       const struct channel_spec *ch_spec;
+       struct channel_status *ch_status;
+       const struct sr_key_info *kinfo;
        int ret;
+       uint32_t cmd;
+       const char *mode, *mode_name, *new_mode;
+       unsigned int i;
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       if (!data || !sdi)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+       scpi = sdi->conn;
+       kinfo = sr_key_info_get(SR_KEY_CONFIG, key);
 
        ret = SR_OK;
-       switch (key) {
-       /* TODO */
-       default:
-               ret = SR_ERR_NA;
+
+       if (!cg) {
+               switch (key) {
+               case SR_CONF_LIMIT_MSEC:
+               case SR_CONF_LIMIT_SAMPLES:
+                       ret = sr_sw_limits_config_set(&devc->limits, key, data);
+                       break;
+               default:
+                       sr_dbg("%s: Unsupported key: %d (%s)", __func__,
+                               (int)key, (kinfo ? kinfo->name : "unknown"));
+                       ret = SR_ERR_NA;
+                       break;
+               }
+       } else {
+               ch = cg->channels->data;
+               ch_spec = &devc->device->channels[ch->index];
+               ch_status = &devc->ch_status[ch->index];
+
+               if ((ret = rigol_dg_get_channel_state(sdi, cg)) != SR_OK)
+                       return ret;
+               sr_scpi_get_opc(scpi);
+
+               switch (key) {
+               case SR_CONF_ENABLED:
+                       if (g_variant_get_boolean(data))
+                               cmd = PSG_CMD_SET_ENABLE;
+                       else
+                               cmd = PSG_CMD_SET_DISABLE;
+                       ret = sr_scpi_cmd(sdi, devc->cmdset,
+                               PSG_CMD_SELECT_CHANNEL, cg->name, cmd, cg->name);
+                       break;
+               case SR_CONF_PATTERN_MODE:
+                       ret = SR_ERR_NA;
+                       new_mode = NULL;
+                       mode = g_variant_get_string(data, NULL);
+                       for (i = 0; i < ch_spec->num_waveforms; i++) {
+                               mode_name = rigol_dg_waveform_to_string(
+                                               ch_spec->waveforms[i].waveform);
+                               if (g_ascii_strncasecmp(mode, mode_name,
+                                               strlen(mode_name)) == 0)
+                                       new_mode = ch_spec->waveforms[i].name;
+                       }
+                       if (new_mode)
+                               ret = sr_scpi_cmd(sdi, devc->cmdset,
+                                       PSG_CMD_SELECT_CHANNEL, cg->name,
+                                       PSG_CMD_SET_SOURCE, cg->name, new_mode);
+                       break;
+               case SR_CONF_OUTPUT_FREQUENCY:
+                       ret = SR_ERR_NA;
+                       if (!(ch_status->wf_spec->opts & WFO_FREQUENCY))
+                               break;
+                       ret = sr_scpi_cmd(sdi, devc->cmdset,
+                               PSG_CMD_SELECT_CHANNEL, cg->name,
+                               PSG_CMD_SET_FREQUENCY, cg->name,
+                               g_variant_get_double(data));
+                       break;
+               case SR_CONF_AMPLITUDE:
+                       ret = SR_ERR_NA;
+                       if (!(ch_status->wf_spec->opts & WFO_AMPLITUDE))
+                               break;
+                       ret = sr_scpi_cmd(sdi, devc->cmdset,
+                               PSG_CMD_SELECT_CHANNEL, cg->name,
+                               PSG_CMD_SET_AMPLITUDE, cg->name,
+                               g_variant_get_double(data));
+                       break;
+               case SR_CONF_OFFSET:
+                       ret = SR_ERR_NA;
+                       if (!(ch_status->wf_spec->opts & WFO_OFFSET))
+                               break;
+                       ret = sr_scpi_cmd(sdi, devc->cmdset,
+                               PSG_CMD_SELECT_CHANNEL, cg->name,
+                               PSG_CMD_SET_OFFSET, cg->name,
+                               g_variant_get_double(data));
+                       break;
+               case SR_CONF_PHASE:
+                       ret = SR_ERR_NA;
+                       if (!(ch_status->wf_spec->opts & WFO_PHASE))
+                               break;
+                       ret = sr_scpi_cmd(sdi, devc->cmdset,
+                               PSG_CMD_SELECT_CHANNEL, cg->name,
+                               PSG_CMD_SET_PHASE, cg->name,
+                               g_variant_get_double(data));
+                       break;
+               case SR_CONF_DUTY_CYCLE:
+                       ret = SR_ERR_NA;
+                       if (!(ch_status->wf_spec->opts & WFO_DUTY_CYCLE))
+                               break;
+                       if (ch_status->wf == WF_SQUARE)
+                               cmd = PSG_CMD_SET_DCYCL_SQUARE;
+                       else if (ch_status->wf == WF_PULSE)
+                               cmd = PSG_CMD_SET_DCYCL_PULSE;
+                       else
+                               break;
+                       ret = sr_scpi_cmd(sdi, devc->cmdset,
+                               PSG_CMD_SELECT_CHANNEL, cg->name,
+                               cmd, cg->name, g_variant_get_double(data));
+                       break;
+               default:
+                       sr_dbg("%s: Unsupported key: %d (%s)", __func__,
+                               (int)key, (kinfo ? kinfo->name : "unknown"));
+                       ret = SR_ERR_NA;
+                       break;
+               }
        }
 
        return ret;
@@ -98,44 +491,169 @@ static int config_set(uint32_t key, GVariant *data,
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
+       struct dev_context *devc;
+       struct sr_channel *ch;
+       const struct channel_spec *ch_spec;
+       const struct waveform_spec *wf_spec;
+       struct channel_status *ch_status;
+       GVariantBuilder *b;
+       unsigned int i;
+       double fspec[3];
+
+       devc = NULL;
+       if (sdi)
+               devc = sdi->priv;
+
+       if (!cg) {
+               switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
+               case SR_CONF_DEVICE_OPTIONS:
+                       return std_opts_config_list(key, data, sdi, cg,
+                               ARRAY_AND_SIZE(scanopts),
+                               ARRAY_AND_SIZE(drvopts),
+                               (devc && devc->device) ? devc->device->devopts : NULL,
+                               (devc && devc->device) ? devc->device->num_devopts : 0);
+               default:
+                       return SR_ERR_NA;
+               }
+       } else {
+               if (!devc || !devc->device)
+                       return SR_ERR_ARG;
+               ch = cg->channels->data;
+               ch_spec = &devc->device->channels[ch->index];
+               ch_status = &devc->ch_status[ch->index];
+
+               switch(key) {
+               case SR_CONF_DEVICE_OPTIONS:
+                       *data = std_gvar_array_u32(devc->device->devopts_cg,
+                                       devc->device->num_devopts_cg);
+                       break;
+               case SR_CONF_PATTERN_MODE:
+                       b = g_variant_builder_new(G_VARIANT_TYPE("as"));
+                       for (i = 0; i < ch_spec->num_waveforms; i++) {
+                               g_variant_builder_add(b, "s",
+                                       rigol_dg_waveform_to_string(
+                                               ch_spec->waveforms[i].waveform));
+                       }
+                       *data = g_variant_new("as", b);
+                       g_variant_builder_unref(b);
+                       break;
+               case SR_CONF_OUTPUT_FREQUENCY:
+                       /*
+                        * Frequency range depends on the currently active
+                        * wave form.
+                        */
+                       if (rigol_dg_get_channel_state(sdi, cg) != SR_OK)
+                               return SR_ERR_NA;
+                       wf_spec = rigol_dg_get_waveform_spec(ch_spec,
+                                       ch_status->wf);
+                       if (!wf_spec)
+                               return SR_ERR_BUG;
+                       fspec[0] = wf_spec->freq_min;
+                       fspec[1] = wf_spec->freq_max;
+                       fspec[2] = wf_spec->freq_step;
+                       *data = std_gvar_min_max_step_array(fspec);
+                       break;
+               case SR_CONF_PHASE:
+                       *data = std_gvar_min_max_step_array(phase_min_max_step);
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_scpi_dev_inst *scpi;
+       const char *cmd;
+       char *response;
        int ret;
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       if (!sdi)
+               return SR_ERR_ARG;
 
+       devc = sdi->priv;
+       scpi = sdi->conn;
+       response = NULL;
        ret = SR_OK;
-       switch (key) {
-       /* TODO */
-       default:
-               return SR_ERR_NA;
+
+       if (!scpi)
+               return SR_ERR_BUG;
+
+       cmd = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_COUNTER_GET_ENABLED);
+       if (cmd && *cmd) {
+               /* Check if counter is currently enabled. */
+               ret = sr_scpi_get_string(scpi, cmd, &response);
+               if (ret != SR_OK)
+                       return SR_ERR_NA;
+               if (g_ascii_strncasecmp(response, "RUN", strlen("RUN")) == 0)
+                       devc->counter_enabled = TRUE;
+               else
+                       devc->counter_enabled = FALSE;
+
+               if (!devc->counter_enabled) {
+                       /* Enable counter if it was not already running. */
+                       cmd = sr_scpi_cmd_get(devc->cmdset,
+                                       PSG_CMD_COUNTER_SET_ENABLE);
+                       if (!cmd)
+                               return SR_ERR_BUG;
+                       sr_scpi_get_opc(scpi);
+                       ret = sr_scpi_send(scpi, cmd);
+               }
+       }
+
+       if (ret == SR_OK) {
+               sr_sw_limits_acquisition_start(&devc->limits);
+               ret = std_session_send_df_header(sdi);
+               if (ret == SR_OK) {
+                       ret = sr_scpi_source_add(sdi->session, scpi,
+                               G_IO_IN, 100, rigol_dg_receive_data,
+                               (void *)sdi);
+               }
        }
 
+       g_free(response);
+
        return ret;
 }
 
-static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       /* TODO: configure hardware, reset acquisition state, set up
-        * callbacks and send header packet. */
+       struct dev_context *devc;
+       struct sr_scpi_dev_inst *scpi;
+       const char *cmd;
+       int ret;
 
-       (void)sdi;
+       if (!sdi)
+               return SR_ERR_ARG;
 
-       return SR_OK;
-}
+       devc = sdi->priv;
+       scpi = sdi->conn;
+       ret = SR_OK;
 
-static int dev_acquisition_stop(struct sr_dev_inst *sdi)
-{
-       /* TODO: stop acquisition. */
+       cmd = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_COUNTER_SET_DISABLE);
+       if (cmd && *cmd && !devc->counter_enabled) {
+               /*
+                * If counter was not running when acquisiton started,
+                * turn it off now...
+                */
+               sr_scpi_get_opc(scpi);
+               ret = sr_scpi_send(scpi, cmd);
+       }
 
-       (void)sdi;
+       sr_scpi_source_remove(sdi->session, scpi);
+       std_session_send_df_end(sdi);
 
-       return SR_OK;
+       return ret;
 }
 
 static struct sr_dev_driver rigol_dg_driver_info = {
        .name = "rigol-dg",
-       .longname = "rigol-dg",
+       .longname = "Rigol DG Series",
        .api_version = 1,
        .init = std_init,
        .cleanup = std_cleanup,
index 01794280c08f9ecba2fea60577f6a33356ac51df..47bf9446f48139aeba8d2698978d8f1f6a8f7532 100644 (file)
  */
 
 #include <config.h>
+#include <string.h>
+#include "scpi.h"
 #include "protocol.h"
 
+SR_PRIV const char *rigol_dg_waveform_to_string(enum waveform_type type)
+{
+       switch (type) {
+       case WF_DC:
+               return "DC";
+       case WF_SINE:
+               return "Sine";
+       case WF_SQUARE:
+               return "Square";
+       case WF_RAMP:
+               return "Ramp";
+       case WF_PULSE:
+               return "Pulse";
+       case WF_NOISE:
+               return "Noise";
+       case WF_ARB:
+               return "Arb";
+       }
+
+       return "Unknown";
+}
+
+SR_PRIV const struct waveform_spec *rigol_dg_get_waveform_spec(
+       const struct channel_spec *ch, enum waveform_type wf)
+{
+       const struct waveform_spec *spec;
+       unsigned int i;
+
+       spec = NULL;
+       for (i = 0; i < ch->num_waveforms; i++) {
+               if (ch->waveforms[i].waveform == wf) {
+                       spec = &ch->waveforms[i];
+                       break;
+               }
+       }
+
+       return spec;
+}
+
+SR_PRIV int rigol_dg_get_channel_state(const struct sr_dev_inst *sdi,
+       const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       struct sr_scpi_dev_inst *scpi;
+       struct sr_channel *ch;
+       struct channel_status *ch_status;
+       const char *command;
+       GVariant *data;
+       gchar *response, **params;
+       const gchar *s;
+       enum waveform_type wf;
+       double freq, ampl, offset, phase;
+       int ret;
+
+       devc = sdi->priv;
+       scpi = sdi->conn;
+       data = NULL;
+       params = NULL;
+       response = NULL;
+       ret = SR_ERR_NA;
+
+       if (!sdi || !cg)
+               return SR_ERR_BUG;
+
+       ch = cg->channels->data;
+       ch_status = &devc->ch_status[ch->index];
+
+       command = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_GET_SOURCE);
+       if (command && *command) {
+               sr_scpi_get_opc(scpi);
+               ret = sr_scpi_cmd_resp(sdi, devc->cmdset,
+                       PSG_CMD_SELECT_CHANNEL, cg->name, &data,
+                       G_VARIANT_TYPE_STRING, PSG_CMD_GET_SOURCE, cg->name);
+               if (ret != SR_OK)
+                       goto done;
+               response = g_variant_dup_string(data, NULL);
+               g_strstrip(response);
+               s = sr_scpi_unquote_string(response);
+               sr_spew("Channel state: '%s'", s);
+               params = g_strsplit(s, ",", 0);
+               if (!params[0])
+                       goto done;
+
+               /* First parameter is the waveform type */
+               if (!(s = params[0]))
+                       goto done;
+               if (g_ascii_strncasecmp(s, "SIN", strlen("SIN")) == 0)
+                       wf = WF_SINE;
+               else if (g_ascii_strncasecmp(s, "SQU", strlen("SQU")) == 0)
+                       wf = WF_SQUARE;
+               else if (g_ascii_strncasecmp(s, "RAMP", strlen("RAMP")) == 0)
+                       wf = WF_RAMP;
+               else if (g_ascii_strncasecmp(s, "PULSE", strlen("PULSE")) == 0)
+                       wf = WF_PULSE;
+               else if (g_ascii_strncasecmp(s, "NOISE", strlen("NOISE")) == 0)
+                       wf = WF_NOISE;
+               else if (g_ascii_strncasecmp(s, "USER", strlen("USER")) == 0)
+                       wf = WF_ARB;
+               else if (g_ascii_strncasecmp(s, "DC", strlen("DC")) == 0)
+                       wf = WF_DC;
+               else
+                       goto done;
+               ch_status->wf = wf;
+               ch_status->wf_spec = rigol_dg_get_waveform_spec(
+                               &devc->device->channels[ch->index], wf);
+
+               /* Second parameter if the frequency (or "DEF" if not applicable) */
+               if (!(s = params[1]))
+                       goto done;
+               freq = g_ascii_strtod(s, NULL);
+               ch_status->freq = freq;
+
+               /* Third parameter if the amplitude (or "DEF" if not applicable) */
+               if (!(s = params[2]))
+                       goto done;
+               ampl = g_ascii_strtod(s, NULL);
+               ch_status->ampl = ampl;
+
+               /* Fourth parameter if the offset (or "DEF" if not applicable) */
+               if (!(s = params[3]))
+                       goto done;
+               offset = g_ascii_strtod(s, NULL);
+               ch_status->offset = offset;
+
+               /* Fifth parameter if the phase (or "DEF" if not applicable) */
+               if (!(s = params[4]))
+                       goto done;
+               phase = g_ascii_strtod(s, NULL);
+               ch_status->phase = phase;
+
+               ret = SR_OK;
+       }
+
+done:
+       g_variant_unref(data);
+       g_free(response);
+       g_strfreev(params);
+       return ret;
+}
+
+static void rigol_dg_send_channel_value(const struct sr_dev_inst *sdi,
+               struct sr_channel *ch, double value, enum sr_mq mq,
+               enum sr_unit unit, int digits)
+{
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_analog analog;
+       struct sr_analog_encoding encoding;
+       struct sr_analog_meaning meaning;
+       struct sr_analog_spec spec;
+       double val;
+
+       val = value;
+       sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+       analog.meaning->channels = g_slist_append(NULL, ch);
+       analog.num_samples = 1;
+       analog.data = &val;
+       analog.encoding->unitsize = sizeof(val);
+       analog.encoding->is_float = TRUE;
+       analog.encoding->digits = digits;
+       analog.meaning->mq = mq;
+       analog.meaning->unit = unit;
+
+       packet.type = SR_DF_ANALOG;
+       packet.payload = &analog;
+       sr_session_send(sdi, &packet);
+       g_slist_free(analog.meaning->channels);
+}
+
 SR_PRIV int rigol_dg_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;
+       const char *cmd, *s;
+       char *response, **params;
+       double meas[5];
+       GSList *l;
+       int i, start_idx, ret;
 
        (void)fd;
+       (void)revents;
+       response = NULL;
+       params = NULL;
 
-       if (!(sdi = cb_data))
+       sdi = cb_data;
+       if (!sdi)
+               return TRUE;
+       scpi = sdi->conn;
+       devc = sdi->priv;
+       if (!scpi || !devc)
                return TRUE;
 
-       if (!(devc = sdi->priv))
+       cmd = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_COUNTER_MEASURE);
+       if (!cmd || !*cmd)
                return TRUE;
 
-       if (revents == G_IO_IN) {
-               /* TODO */
+       sr_scpi_get_opc(scpi);
+       ret = sr_scpi_get_string(scpi, cmd, &response);
+       if (ret != SR_OK) {
+               sr_info("Error getting measurement from counter: %d", ret);
+               sr_dev_acquisition_stop(sdi);
+               return TRUE;
+       }
+       g_strstrip(response);
+
+       /*
+        * Parse measurement string:
+        *  frequency, period, duty cycle, width+, width-
+        */
+       params = g_strsplit(response, ",", 0);
+       for (i = 0; i < 5; i++) {
+               if (!(s = params[i]))
+                       goto done;
+               meas[i] = g_ascii_strtod(s, NULL);
        }
+       sr_spew("%s: freq=%.10E, period=%.10E, duty=%.10E, width+=%.10E,"
+               "width-=%.10E", __func__,
+               meas[0], meas[1], meas[2], meas[3], meas[4]);
+
+       std_session_send_df_frame_begin(sdi);
+       start_idx = devc->device->num_channels;
+
+       /* Frequency */
+       l = g_slist_nth(sdi->channels, start_idx++);
+       rigol_dg_send_channel_value(sdi, l->data, meas[0], SR_MQ_FREQUENCY,
+               SR_UNIT_HERTZ, 10);
+
+       /* Period */
+       l = g_slist_nth(sdi->channels, start_idx++);
+       rigol_dg_send_channel_value(sdi, l->data, meas[1], SR_MQ_TIME,
+               SR_UNIT_SECOND, 10);
+
+       /* Duty Cycle */
+       l = g_slist_nth(sdi->channels, start_idx++);
+       rigol_dg_send_channel_value(sdi, l->data, meas[2], SR_MQ_DUTY_CYCLE,
+               SR_UNIT_PERCENTAGE, 3);
+
+       /* Pulse Width */
+       l = g_slist_nth(sdi->channels, start_idx++);
+       rigol_dg_send_channel_value(sdi, l->data, meas[3], SR_MQ_PULSE_WIDTH,
+               SR_UNIT_SECOND, 10);
+
+       std_session_send_df_frame_end(sdi);
+       sr_sw_limits_update_samples_read(&devc->limits, 1);
+
+       if (sr_sw_limits_check(&devc->limits))
+               sr_dev_acquisition_stop(sdi);
 
+done:
+       g_free(response);
+       g_strfreev(params);
        return TRUE;
 }
index db8b463ace6aa5101070c16cf7a18f69dbe6fd46..bff7c2089e339dde067d5a51b0331f27f57e2289 100644 (file)
 
 #define LOG_PREFIX "rigol-dg"
 
+enum psg_commands {
+       PSG_CMD_SETUP_REMOTE,
+       PSG_CMD_SETUP_LOCAL,
+       PSG_CMD_SELECT_CHANNEL,
+       PSG_CMD_GET_CHANNEL,
+       PSG_CMD_GET_ENABLED,
+       PSG_CMD_SET_ENABLE,
+       PSG_CMD_SET_DISABLE,
+       PSG_CMD_GET_SOURCE,
+       PSG_CMD_SET_SOURCE,
+       PSG_CMD_SET_FREQUENCY,
+       PSG_CMD_GET_FREQUENCY,
+       PSG_CMD_SET_AMPLITUDE,
+       PSG_CMD_GET_AMPLITUDE,
+       PSG_CMD_GET_OFFSET,
+       PSG_CMD_SET_OFFSET,
+       PSG_CMD_GET_PHASE,
+       PSG_CMD_SET_PHASE,
+       PSG_CMD_GET_DCYCL_PULSE,
+       PSG_CMD_SET_DCYCL_PULSE,
+       PSG_CMD_GET_DCYCL_SQUARE,
+       PSG_CMD_SET_DCYCL_SQUARE,
+       PSG_CMD_COUNTER_GET_ENABLED,
+       PSG_CMD_COUNTER_SET_ENABLE,
+       PSG_CMD_COUNTER_SET_DISABLE,
+       PSG_CMD_COUNTER_MEASURE,
+};
+
+enum waveform_type {
+       WF_DC = 0,
+       WF_SINE,
+       WF_SQUARE,
+       WF_RAMP,
+       WF_PULSE,
+       WF_NOISE,
+       WF_ARB,
+};
+
+enum waveform_options {
+       WFO_FREQUENCY = 1,
+       WFO_AMPLITUDE = 2,
+       WFO_OFFSET = 4,
+       WFO_PHASE = 8,
+       WFO_DUTY_CYCLE = 16,
+};
+
+struct waveform_spec {
+       const char *name;
+       enum waveform_type waveform;
+       double freq_min;
+       double freq_max;
+       double freq_step;
+       uint32_t opts;
+};
+
+struct channel_spec {
+       const char *name;
+       const struct waveform_spec *waveforms;
+       uint32_t num_waveforms;
+};
+
+struct channel_status {
+       enum waveform_type wf;
+       const struct waveform_spec *wf_spec;
+       double freq;
+       double ampl;
+       double offset;
+       double phase;
+};
+
+struct device_spec {
+       const char *vendor;
+       const char *model;
+       const uint32_t *devopts;
+       const uint32_t num_devopts;
+       const uint32_t *devopts_cg;
+       const uint32_t num_devopts_cg;
+       const struct channel_spec *channels;
+       const uint32_t num_channels;
+       const struct scpi_command *cmdset;
+};
+
 struct dev_context {
+       const struct scpi_command *cmdset;
+       const struct device_spec *device;
+       struct channel_status *ch_status;
+       struct sr_sw_limits limits;
+       gboolean counter_enabled;
 };
 
+SR_PRIV const char *rigol_dg_waveform_to_string(enum waveform_type type);
+SR_PRIV const struct waveform_spec *rigol_dg_get_waveform_spec(
+               const struct channel_spec *ch, enum waveform_type wf);
+SR_PRIV int rigol_dg_get_channel_state(const struct sr_dev_inst *sdi,
+               const struct sr_channel_group *cg);
 SR_PRIV int rigol_dg_receive_data(int fd, int revents, void *cb_data);
 
 #endif