From: mhooijboer Date: Sun, 11 Feb 2018 17:11:58 +0000 (+0100) Subject: siglent-sds: initial driver implementation for Siglent SDS X-Git-Url: http://sigrok.org/gitweb/?a=commitdiff_plain;h=b33606718cebd776723c18de0a17efbca2d69525;p=libsigrok.git siglent-sds: initial driver implementation for Siglent SDS Implement a first version of the driver for the Siglent SDS1000 and SDS2000 oscilloscopes. [ gsi: This commit corresponds to git 0228126017e6 of marchelh's tree, I adjusted the source to closer match the project's coding style. The conversion was verified by this command: $ git diff --word-diff=color -w -b : : Changes include: Whitespace adjustment, dead code removal, separation of variable declaration and assignment, alpha-sorted includes. Line length was not addressed and not every location got adjusted, to keep the diff minimal and to reduce effort during review of this version. ] --- diff --git a/src/hardware/siglent-sds/api.c b/src/hardware/siglent-sds/api.c index 8e5a8d1f..310c9fd0 100644 --- a/src/hardware/siglent-sds/api.c +++ b/src/hardware/siglent-sds/api.c @@ -18,76 +18,824 @@ */ #include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "libsigrok-internal.h" #include "protocol.h" +#include "scpi.h" -static GSList *scan(struct sr_dev_driver *di, GSList *options) +static const uint32_t scanopts[] = { + SR_CONF_CONN, + SR_CONF_SERIALCOMM +}; + +static const uint32_t devopts[] = { + SR_CONF_OSCILLOSCOPE, + SR_CONF_LOGIC_ANALYZER, + SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_LEVEL | SR_CONF_GET | SR_CONF_SET, + SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET, + SR_CONF_NUM_HDIV | SR_CONF_GET | SR_CONF_LIST, + SR_CONF_SAMPLERATE | SR_CONF_GET, + SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_AVERAGING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +}; + +static const uint32_t analog_devopts[] = { + SR_CONF_NUM_VDIV | SR_CONF_GET, + SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_PROBE_FACTOR | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +}; + +static const uint64_t timebases[][2] = { + /* nanoseconds */ + { 1, 1000000000 }, + { 2, 1000000000 }, + { 5, 1000000000 }, + { 10, 1000000000 }, + { 20, 1000000000 }, + { 50, 1000000000 }, + { 100, 1000000000 }, + { 200, 1000000000 }, + { 500, 1000000000 }, + /* microseconds */ + { 1, 1000000 }, + { 2, 1000000 }, + { 5, 1000000 }, + { 10, 1000000 }, + { 20, 1000000 }, + { 50, 1000000 }, + { 100, 1000000 }, + { 200, 1000000 }, + { 500, 1000000 }, + /* milliseconds */ + { 1, 1000 }, + { 2, 1000 }, + { 5, 1000 }, + { 10, 1000 }, + { 20, 1000 }, + { 50, 1000 }, + { 100, 1000 }, + { 200, 1000 }, + { 500, 1000 }, + /* seconds */ + { 1, 1 }, + { 2, 1 }, + { 5, 1 }, + { 10, 1 }, + { 20, 1 }, + { 50, 1 }, +}; + +static const uint64_t vdivs[][2] = { + /* microvolts */ + { 500, 100000 }, + /* millivolts */ + { 1, 1000 }, + { 2, 1000 }, + { 5, 1000 }, + { 10, 1000 }, + { 20, 1000 }, + { 50, 1000 }, + { 100, 1000 }, + { 200, 1000 }, + { 500, 1000 }, + /* volts */ + { 1, 1 }, + { 2, 1 }, + { 5, 1 }, + { 10, 1 }, + { 20, 1 }, + { 50, 1 }, + { 100, 1 }, +}; + +#define NUM_TIMEBASE ARRAY_SIZE(timebases) +#define NUM_VDIV ARRAY_SIZE(vdivs) + +static const char *trigger_sources[] = { + "CH1", + "CH2", + "Ext", + "Ext /5", + "AC Line", + "D0", + "D1", + "D2", + "D3", + "D4", + "D5", + "D6", + "D7", + "D8", + "D9", + "D10", + "D11", + "D12", + "D13", + "D14", + "D15", +}; + +static const char *trigger_slopes[] = { + "Rising", + "Falling", +}; + +static const char *coupling[] = { + "A1M AC 1 Meg", + "A50 AC 50 Ohm", + "D1M DC 1 Meg", + "D50 DC 50 Ohm", + "GND", +}; + +static const uint64_t probe_factor[] = { + 1, + 2, + 5, + 10, + 20, + 50, + 100, + 200, + 500, + 1000, + 2000, + 5000, + 10000, +}; + +/* Do not change the order of entries */ +static const char *data_sources[] = { + "Display", + "History", +}; + +enum vendor { + SIGLENT, +}; + +enum series { + SDS1000CML, + SDS1000CNL, + SDS1000DL, + SDS1000X, + SDS1000XP, + SDS1000XE, + SDS2000X, +}; + +/* short name, full name, USB Name */ +static const struct siglent_sds_vendor supported_vendors[] = { + [SIGLENT] = { + "Siglent", "Siglent Technologies", "Siglent Technologies Co,. Ltd.", + }, +}; + +#define VENDOR(x) &supported_vendors[x] +/* vendor, series, protocol, max timebase, min vdiv, number of horizontal divs, + * number of vertical divs, live waveform samples, memory buffer samples */ +static const struct siglent_sds_series supported_series[] = { + [SDS1000CML] = { + VENDOR(SIGLENT), "SDS1000CML", NON_SPO_MODEL, + { 50, 1 }, + { 2, 1000 }, 18, 8, 1400363, + }, + [SDS1000CNL] = { + VENDOR(SIGLENT), "SDS1000CNL", NON_SPO_MODEL, + { 50, 1 }, + { 2, 1000 }, 18, 8, 1400363, + }, + [SDS1000DL] = { + VENDOR(SIGLENT), "SDS1000DL", NON_SPO_MODEL, + { 50, 1 }, + { 2, 1000 }, 18, 8, 1400363, + }, + [SDS1000X] = { + VENDOR(SIGLENT), "SDS1000X", SPO_MODEL, + { 50, 1 }, + { 500, 100000 }, 14, 8, 14000363, + }, + [SDS1000XP] = { + VENDOR(SIGLENT), "SDS1000X+", SPO_MODEL, + { 50, 1 }, + { 500, 100000 }, 14, 8, 14000363, + }, + [SDS1000XE] = { + VENDOR(SIGLENT), "SDS1000XE", SPO_MODEL, + { 50, 1 }, + { 500, 100000 }, 14, 8, 14000363, + }, + [SDS2000X] = { + VENDOR(SIGLENT), "SDS2000X", SPO_MODEL, + { 50, 1 }, + { 500, 100000 }, 14, 8, 14000363, + }, +}; + +#define SERIES(x) &supported_series[x] +/* series, model, min timebase, analog channels, digital */ +static const struct siglent_sds_model supported_models[] = { + { SERIES(SDS1000CML), "SDS1152CML", + { 20, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000CML), "SDS1102CML", + { 10, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000CML), "SDS1072CML", + { 5, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000CNL), "SDS1202CNL", + { 20, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000CNL), "SDS1102CNL", + { 10, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000CNL), "SDS1072CNL", + { 5, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000DL), "SDS1202DL", + { 20, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000DL), "SDS1102DL", + { 10, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000DL), "SDS1022DL", + { 5, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000DL), "SDS1052DL", + { 5, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000X), "SDS1102X", + { 2, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000XP), "SDS1102X+", + { 2, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000X), "SDS1202X", + { 2, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000XP), "SDS1202X+", + { 2, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000XE), "SDS1202X-E", + { 1, 1000000000 }, 2, false, 0 }, + { SERIES(SDS1000XE), "SDS1104X-E", + { 1, 1000000000 }, 4, true, 16 }, + { SERIES(SDS1000XE), "SDS1204X-E", + { 1, 1000000000 }, 4, true, 16 }, + { SERIES(SDS2000X), "SDS2072X", + { 2, 1000000000 }, 2, false, 0 }, + { SERIES(SDS2000X), "SDS2074X", + { 2, 1000000000 }, 4, false, 0 }, + { SERIES(SDS2000X), "SDS2102X", + { 2, 1000000000 }, 2, false, 0 }, + { SERIES(SDS2000X), "SDS2104X", + { 2, 1000000000 }, 4, false, 0 }, + { SERIES(SDS2000X), "SDS2202X", + { 2, 1000000000 }, 2, false, 0 }, + { SERIES(SDS2000X), "SDS2204X", + { 2, 1000000000 }, 4, false, 0 }, + { SERIES(SDS2000X), "SDS2302X", + { 2, 1000000000 }, 2, false, 0 }, + { SERIES(SDS2000X), "SDS2304X", + { 2, 1000000000 }, 4, false, 0 }, +}; + +SR_PRIV struct sr_dev_driver siglent_sds_driver_info; + +static void clear_helper(void *priv) +{ + struct dev_context *devc; + + devc = priv; + if (!devc) + return; + g_free(devc->analog_groups); + g_free(devc->enabled_channels); +} + +static int dev_clear(const struct sr_dev_driver *di) +{ + + return std_dev_clear_with_callback(di, clear_helper); +} + +static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi) { - struct drv_context *drvc; - GSList *devices; + struct dev_context *devc; + struct sr_dev_inst *sdi; + struct sr_scpi_hw_info *hw_info; + struct sr_channel *ch; + unsigned int i; + const struct siglent_sds_model *model; + gchar *channel_name; + + sr_info("Device probing decode..."); - (void)options; + /* Setting communication Header Format to OFF*/ + sr_dbg("Setting Communication Headers to off"); + if (sr_scpi_send(scpi, "CHDR OFF") != SR_OK) + return NULL; - devices = NULL; - drvc = di->context; - drvc->instances = NULL; + if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) { + sr_info("Couldn't get IDN response, retrying."); + sr_scpi_close(scpi); + sr_scpi_open(scpi); + if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) { + sr_info("Couldn't get IDN response."); + return NULL; + } + } + + model = NULL; + for (i = 0; i < ARRAY_SIZE(supported_models); i++) { + if (!strcmp(hw_info->model, supported_models[i].name)) { + model = &supported_models[i]; + break; + } + } - /* TODO: scan for devices, either based on a SR_CONF_CONN option - * or on a USB scan. */ + sr_info("Decoded Manufacturer: %s", hw_info->manufacturer); + sr_info("Decoded Model: %s", hw_info->model); - return devices; + if (!model) { + sr_scpi_hw_info_free(hw_info); + return NULL; + } + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sr_dbg("Setting Device Instance Vendor: %s", model->series->vendor->name); + sdi->vendor = g_strdup(model->series->vendor->name); + sr_dbg("Setting Device Instance model: %s", model->name); + sdi->model = g_strdup(model->name); + sr_dbg("Setting Device Instance version: %s", hw_info->firmware_version); + sdi->version = g_strdup(hw_info->firmware_version); + sdi->conn = scpi; + sdi->driver = &siglent_sds_driver_info; + sr_dbg("Setting Device Instance inst_type: SCPI"); + sdi->inst_type = SR_INST_SCPI; + sdi->serial_num = g_strdup(hw_info->serial_number); + devc = g_malloc0(sizeof(struct dev_context)); + devc->limit_frames = 1; + devc->model = model; + sr_dbg("Setting device Context model: %s", devc->model->name); + + sr_scpi_hw_info_free(hw_info); + + devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group *) * + model->analog_channels); + + for (i = 0; i < model->analog_channels; i++) { + channel_name = g_strdup_printf("CH%d", i + 1); + ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_name); + + devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group)); + + devc->analog_groups[i]->name = channel_name; + devc->analog_groups[i]->channels = g_slist_append(NULL, ch); + sdi->channel_groups = g_slist_append(sdi->channel_groups, + devc->analog_groups[i]); + } + + if (devc->model->has_digital) { + devc->digital_group = g_malloc0(sizeof(struct sr_channel_group)); + + for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) { + channel_name = g_strdup_printf("D%d", i); + ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name); + g_free(channel_name); + devc->digital_group->channels = g_slist_append( + devc->digital_group->channels, ch); + } + devc->digital_group->name = g_strdup("LA"); + sdi->channel_groups = g_slist_append(sdi->channel_groups, + devc->digital_group); + } + + for (i = 0; i < NUM_TIMEBASE; i++) { + if (!memcmp(&devc->model->min_timebase, &timebases[i], sizeof(uint64_t[2]))) + devc->timebases = &timebases[i]; + + if (!memcmp(&devc->model->series->max_timebase, &timebases[i], sizeof(uint64_t[2]))) + devc->num_timebases = &timebases[i] - devc->timebases + 1; + } + + for (i = 0; i < NUM_VDIV; i++) { + devc->vdivs = &vdivs[i]; + if (!memcmp(&devc->model->series->min_vdiv, + &vdivs[i], sizeof(uint64_t[2]))) { + devc->vdivs = &vdivs[i]; + devc->num_vdivs = NUM_VDIV - i; + break; + } + } + + devc->buffer = g_malloc(devc->model->series->buffer_samples); + sr_dbg("Setting device Context buffer Size: %i", devc->model->series->buffer_samples); + devc->data = g_malloc(devc->model->series->buffer_samples * sizeof(float)); + + devc->data_source = DATA_SOURCE_SCREEN; + + sdi->priv = devc; + + return sdi; +} + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + + // TODO implement RPC call for LXI device discovery. + return sr_scpi_scan(di->context, options, probe_device); } static int dev_open(struct sr_dev_inst *sdi) { - (void)sdi; + int ret; + struct sr_scpi_dev_inst *scpi = sdi->conn; + + if ((ret = sr_scpi_open(scpi)) < 0) { + sr_err("Failed to open SCPI device: %s.", sr_strerror(ret)); + return SR_ERR; + } - /* TODO: get handle from sdi->conn and open it. */ + if ((ret = siglent_sds_get_dev_cfg(sdi)) < 0) { + sr_err("Failed to get device config: %s.", sr_strerror(ret)); + return SR_ERR; + } + + sdi->status = SR_ST_ACTIVE; return SR_OK; } static int dev_close(struct sr_dev_inst *sdi) { - (void)sdi; - /* TODO: get handle from sdi->conn and close it. */ - - 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) { - int ret; + struct dev_context *devc; + struct sr_channel *ch; + const char *tmp_str; + int analog_channel = -1; + float smallest_diff = INFINITY; + int idx = -1; + unsigned i; - (void)sdi; - (void)data; - (void)cg; + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + /* If a channel group is specified, it must be a valid one. */ + if (cg && !g_slist_find(sdi->channel_groups, cg)) { + sr_err("Invalid channel group specified."); + return SR_ERR; + } + + if (cg) { + ch = g_slist_nth_data(cg->channels, 0); + if (!ch) + return SR_ERR; + if (ch->type == SR_CHANNEL_ANALOG) { + if (ch->name[2] < '1' || ch->name[2] > '4') + return SR_ERR; + analog_channel = ch->name[2] - '1'; + } + } - ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_NUM_HDIV: + *data = g_variant_new_int32(devc->model->series->num_horizontal_divs); + break; + case SR_CONF_NUM_VDIV: + *data = g_variant_new_int32(devc->num_vdivs); + break; + case SR_CONF_LIMIT_FRAMES: + *data = g_variant_new_uint64(devc->limit_frames); + break; + case SR_CONF_DATA_SOURCE: + if (devc->data_source == DATA_SOURCE_SCREEN) + *data = g_variant_new_string("Screen"); + else if (devc->data_source == DATA_SOURCE_HISTORY) + *data = g_variant_new_string("History"); + break; + case SR_CONF_SAMPLERATE: + siglent_sds_get_dev_cfg_horizontal(sdi); + *data = g_variant_new_uint64(devc->sampleRate); + sr_dbg("Sample rate set to %f", devc->sampleRate); + break; + case SR_CONF_TRIGGER_SOURCE: + if (!strcmp(devc->trigger_source, "ACL")) + tmp_str = "AC Line"; + else if (!strcmp(devc->trigger_source, "CHAN1")) + tmp_str = "CH1"; + else if (!strcmp(devc->trigger_source, "CHAN2")) + tmp_str = "CH2"; + else + tmp_str = devc->trigger_source; + *data = g_variant_new_string(tmp_str); + break; + case SR_CONF_TRIGGER_SLOPE: + if (!strncmp(devc->trigger_slope, "POS", 3)) { + tmp_str = "r"; + } else if (!strncmp(devc->trigger_slope, "NEG", 3)) { + tmp_str = "f"; + } else { + sr_dbg("Unknown trigger slope: '%s'.", devc->trigger_slope); + return SR_ERR_NA; + } + *data = g_variant_new_string(tmp_str); + break; + case SR_CONF_TRIGGER_LEVEL: + *data = g_variant_new_double(devc->trigger_level); + break; + case SR_CONF_HORIZ_TRIGGERPOS: + *data = g_variant_new_double(devc->horiz_triggerpos); + break; + case SR_CONF_TIMEBASE: + for (i = 0; i < devc->num_timebases; i++) { + float tb, diff; + + tb = (float)devc->timebases[i][0] / devc->timebases[i][1]; + diff = fabs(devc->timebase - tb); + if (diff < smallest_diff) { + smallest_diff = diff; + idx = i; + } + } + if (idx < 0) { + sr_dbg("Negative timebase index: %d.", idx); + return SR_ERR_NA; + } + *data = g_variant_new("(tt)", devc->timebases[idx][0], + devc->timebases[idx][1]); + break; + case SR_CONF_VDIV: + if (analog_channel < 0) { + sr_dbg("Negative analog channel: %d.", analog_channel); + return SR_ERR_NA; + } + for (i = 0; i < ARRAY_SIZE(vdivs); i++) { + float vdiv, diff; + + vdiv = (float)vdivs[i][0] / vdivs[i][1]; + diff = fabsf(devc->vdiv[analog_channel] - vdiv); + if (diff < smallest_diff) { + smallest_diff = diff; + idx = i; + } + } + if (idx < 0) { + sr_dbg("Negative vdiv index: %d.", idx); + return SR_ERR_NA; + } + *data = g_variant_new("(tt)", vdivs[idx][0], vdivs[idx][1]); + break; + case SR_CONF_COUPLING: + if (analog_channel < 0) { + sr_dbg("Negative analog channel: %d.", analog_channel); + return SR_ERR_NA; + } + *data = g_variant_new_string(devc->coupling[analog_channel]); + break; + case SR_CONF_PROBE_FACTOR: + if (analog_channel < 0) { + sr_dbg("Negative analog channel: %d.", analog_channel); + return SR_ERR_NA; + } + *data = g_variant_new_uint64(devc->attenuation[analog_channel]); + break; default: return SR_ERR_NA; } - return ret; + return SR_OK; } 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; + uint64_t p, q; + double t_dbl; + unsigned int i, j; int ret; + const char *tmp_str; + char buffer[16]; - (void)sdi; - (void)data; - (void)cg; + devc = sdi->priv; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + /* If a channel group is specified, it must be a valid one. */ + if (cg && !g_slist_find(sdi->channel_groups, cg)) { + sr_err("Invalid channel group specified."); + return SR_ERR; + } ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_LIMIT_FRAMES: + devc->limit_frames = g_variant_get_uint64(data); + break; + case SR_CONF_TRIGGER_SLOPE: + tmp_str = g_variant_get_string(data, NULL); + g_free(devc->trigger_slope); + ret = siglent_sds_config_set(sdi, "%s:TRSL %s", devc->trigger_source, devc->trigger_slope); + break; + case SR_CONF_HORIZ_TRIGGERPOS: + t_dbl = g_variant_get_double(data); + if (t_dbl < 0.0 || t_dbl > 1.0) { + sr_err("Invalid horiz. trigger position: %g.", t_dbl); + return SR_ERR; + } + devc->horiz_triggerpos = t_dbl; + /* We have the trigger offset as a percentage of the frame, but + * need to express this in seconds. */ + t_dbl = -(devc->horiz_triggerpos - 0.5) * devc->timebase * devc->num_timebases; + g_ascii_formatd(buffer, sizeof(buffer), "%.6f", t_dbl); + ret = siglent_sds_config_set(sdi, ":TIM:OFFS %s", buffer); + break; + case SR_CONF_TRIGGER_LEVEL: + t_dbl = g_variant_get_double(data); + g_ascii_formatd(buffer, sizeof(buffer), "%.3f", t_dbl); + ret = siglent_sds_config_set(sdi, ":TRIG:EDGE:LEV %s", buffer); + if (ret == SR_OK) + devc->trigger_level = t_dbl; + break; + case SR_CONF_TIMEBASE: + sr_dbg("Setting device Timebase"); + g_variant_get(data, "(tt)", &p, &q); + for (i = 0; i < devc->num_timebases; i++) { + char *cmd; + if (devc->timebases[i][0] == p && devc->timebases[i][1] == q) { + cmd = ""; + devc->timebase = (float) p / q; + switch (q) { + case 1: + cmd = g_strdup_printf("%luS", p); + break; + case 1000: + cmd = g_strdup_printf("%luMS", p); + break; + case 1000000: + cmd = g_strdup_printf("%luUS", p); + break; + case 100000000: + cmd = g_strdup_printf("%luNS", p); + break; + } + sr_dbg("Setting device Timebase: TDIV %s", cmd); + ret = siglent_sds_config_set(sdi, "TDIV %s", cmd); + break; + } + } + if (i == devc->num_timebases) { + sr_err("Invalid timebase index: %d.", i); + ret = SR_ERR_ARG; + } + break; + case SR_CONF_TRIGGER_SOURCE: + tmp_str = g_variant_get_string(data, NULL); + for (i = 0; i < ARRAY_SIZE(trigger_sources); i++) { + if (!strcmp(trigger_sources[i], tmp_str)) { + g_free(devc->trigger_source); + devc->trigger_source = g_strdup(trigger_sources[i]); + if (!strcmp(devc->trigger_source, "AC Line")) + tmp_str = "LINE"; + else if (!strcmp(devc->trigger_source, "CH1")) + tmp_str = "C1"; + else if (!strcmp(devc->trigger_source, "CH2")) + tmp_str = "C2"; + else if (!strcmp(devc->trigger_source, "CH3")) + tmp_str = "C3"; + else if (!strcmp(devc->trigger_source, "CH4")) + tmp_str = "C4"; + else if (!strcmp(devc->trigger_source, "Ext")) + tmp_str = "EX"; + else if (!strcmp(devc->trigger_source, "Ext /5")) + tmp_str = "EX5"; + else + tmp_str = (char *) devc->trigger_source; + ret = siglent_sds_config_set(sdi, "TRSE EDGE,SR,%s,OFF", tmp_str); + break; + } + } + if (i == ARRAY_SIZE(trigger_sources)) { + sr_err("Invalid trigger source index: %d.", i); + ret = SR_ERR_ARG; + } + break; + case SR_CONF_VDIV: + if (!cg) { + sr_err("No channel group specified."); + return SR_ERR_CHANNEL_GROUP; + } + g_variant_get(data, "(tt)", &p, &q); + for (i = 0; i < devc->model->analog_channels; i++) { + char *cmd; + if (cg == devc->analog_groups[i]) { + for (j = 0; j < ARRAY_SIZE(vdivs); j++) { + if (vdivs[j][0] != p || vdivs[j][1] != q) + continue; + cmd = ""; + switch (q) { + case 1: + cmd = g_strdup_printf("%luV", p); + break; + case 1000: + cmd = g_strdup_printf("%luMV", p); + break; + case 100000: + cmd = g_strdup_printf("%luUV", p); + break; + } + return siglent_sds_config_set(sdi, "C%d:VDIV %s", i + 1, + cmd); + } + sr_err("Invalid vdiv index: %d.", j); + return SR_ERR_ARG; + } + } + sr_dbg("Didn't set vdiv, unknown channel(group)."); + return SR_ERR_NA; + case SR_CONF_COUPLING: + if (!cg) { + sr_err("No channel group specified."); + return SR_ERR_CHANNEL_GROUP; + } + tmp_str = g_variant_get_string(data, NULL); + for (i = 0; i < devc->model->analog_channels; i++) { + char cmd[4]; + if (cg == devc->analog_groups[i]) { + for (j = 0; j < ARRAY_SIZE(coupling); j++) { + if (!strcmp(tmp_str, coupling[j])) { + g_free(devc->coupling[i]); + devc->coupling[i] = g_strdup(coupling[j]); + strncpy(cmd, devc->coupling[i], 3); + cmd[3] = 0; + return siglent_sds_config_set(sdi, "C%d:CPL %s", i + 1, + cmd); + } + } + sr_err("Invalid coupling index: %d.", j); + return SR_ERR_ARG; + } + } + sr_dbg("Didn't set coupling, unknown channel(group)."); + return SR_ERR_NA; + case SR_CONF_PROBE_FACTOR: + if (!cg) { + sr_err("No channel group specified."); + return SR_ERR_CHANNEL_GROUP; + } + p = g_variant_get_uint64(data); + for (i = 0; i < devc->model->analog_channels; i++) { + if (cg == devc->analog_groups[i]) { + for (j = 0; j < ARRAY_SIZE(probe_factor); j++) { + if (p == probe_factor[j]) { + devc->attenuation[i] = p; + ret = siglent_sds_config_set(sdi, "C%d:ATTN %"PRIu64, + i + 1, p); + if (ret == SR_OK) + siglent_sds_get_dev_cfg_vertical(sdi); + return ret; + } + } + sr_err("Invalid probe factor: %"PRIu64".", p); + return SR_ERR_ARG; + } + } + sr_dbg("Didn't set probe factor, unknown channel(group)."); + return SR_ERR_NA; + case SR_CONF_DATA_SOURCE: + tmp_str = g_variant_get_string(data, NULL); + if (!strcmp(tmp_str, "Display")) + devc->data_source = DATA_SOURCE_SCREEN; + else if (devc->model->series->protocol >= SPO_MODEL + && !strcmp(tmp_str, "History")) + devc->data_source = DATA_SOURCE_HISTORY; + else { + sr_err("Unknown data source: '%s'.", tmp_str); + return SR_ERR; + } + break; + case SR_CONF_SAMPLERATE: + siglent_sds_get_dev_cfg_horizontal(sdi); + data = g_variant_new_uint64(devc->sampleRate); + break; default: - ret = SR_ERR_NA; + return SR_ERR_NA; } return ret; @@ -96,50 +844,279 @@ 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) { - int ret; + GVariant *tuple, *rational[2]; + GVariantBuilder gvb; + unsigned int i; + struct dev_context *devc = NULL; + + if (key == SR_CONF_SCAN_OPTIONS) { + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t)); + return SR_OK; + } else if (key == SR_CONF_DEVICE_OPTIONS && !cg) { + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts, ARRAY_SIZE(devopts), sizeof(uint32_t)); + return SR_OK; + } - (void)sdi; - (void)data; - (void)cg; + /* Every other option requires a valid device instance. */ + if (!sdi) + return SR_ERR_ARG; + devc = sdi->priv; + + /* If a channel group is specified, it must be a valid one. */ + if (cg && !g_slist_find(sdi->channel_groups, cg)) { + sr_err("Invalid channel group specified."); + return SR_ERR; + } - ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_DEVICE_OPTIONS: + if (!cg) { + sr_err("No channel group specified."); + return SR_ERR_CHANNEL_GROUP; + } + if (cg == devc->digital_group) { + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + NULL, 0, sizeof(uint32_t)); + return SR_OK; + } else { + for (i = 0; i < devc->model->analog_channels; i++) { + if (cg == devc->analog_groups[i]) { + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + analog_devopts, ARRAY_SIZE(analog_devopts), sizeof(uint32_t)); + return SR_OK; + } + } + return SR_ERR_NA; + } + break; + case SR_CONF_COUPLING: + if (!cg) { + sr_err("No channel group specified."); + return SR_ERR_CHANNEL_GROUP; + } + *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling)); + break; + case SR_CONF_PROBE_FACTOR: + if (!cg) { + sr_err("No channel group specified."); + return SR_ERR_CHANNEL_GROUP; + } + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64, + probe_factor, ARRAY_SIZE(probe_factor), sizeof(uint64_t)); + break; + case SR_CONF_VDIV: + if (!devc) + /* Can't know this until we have the exact model. */ + return SR_ERR_ARG; + if (!cg) { + sr_err("No channel group specified."); + return SR_ERR_CHANNEL_GROUP; + } + g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < devc->num_vdivs; i++) { + rational[0] = g_variant_new_uint64(devc->vdivs[i][0]); + rational[1] = g_variant_new_uint64(devc->vdivs[i][1]); + tuple = g_variant_new_tuple(rational, 2); + g_variant_builder_add_value(&gvb, tuple); + } + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_TIMEBASE: + if (!devc) + /* Can't know this until we have the exact model. */ + return SR_ERR_ARG; + if (devc->num_timebases <= 0) + return SR_ERR_NA; + g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < devc->num_timebases; i++) { + rational[0] = g_variant_new_uint64(devc->timebases[i][0]); + rational[1] = g_variant_new_uint64(devc->timebases[i][1]); + tuple = g_variant_new_tuple(rational, 2); + g_variant_builder_add_value(&gvb, tuple); + } + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_TRIGGER_SOURCE: + if (!devc) + /* Can't know this until we have the exact model. */ + return SR_ERR_ARG; + *data = g_variant_new_strv(trigger_sources, + devc->model->has_digital ? ARRAY_SIZE(trigger_sources) : 5); + break; + case SR_CONF_TRIGGER_SLOPE: + *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes)); + break; + case SR_CONF_DATA_SOURCE: + if (!devc) + /* Can't know this until we have the exact model. */ + return SR_ERR_ARG; + switch (devc->model->series->protocol) { + // TODO check what must be done here for the data source buffer sizes + case NON_SPO_MODEL: + *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 1); + break; + case SPO_MODEL: + *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources)); + break; + } + break; + case SR_CONF_NUM_HDIV: + *data = g_variant_new_int32(devc->model->series->num_horizontal_divs); + break; + case SR_CONF_AVERAGING: + //TODO implement averaging. + break; default: return SR_ERR_NA; } - return ret; + return SR_OK; } 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; + struct sr_channel *ch; + struct sr_datafeed_packet packet; + gboolean some_digital; + GSList *l; + GSList *d; - (void)sdi; + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + scpi = sdi->conn; + devc = sdi->priv; + + devc->num_frames = 0; + some_digital = FALSE; + + /* Check if there are any logic channels enabled, if so then enable de MSO, otherwise skip the digital channel setup */ + /* enable and disable channels on the device is very slow and it is faster when checked in a small loop without the actual actions */ + for (d = sdi->channels; d; d = d->next) { + ch = d->data; + if (ch->type == SR_CHANNEL_LOGIC && ch->enabled) { + some_digital = TRUE; + } + } + + for (l = sdi->channels; l; l = l->next) { + ch = l->data; + sr_dbg("handling channel %s", ch->name); + if (ch->type == SR_CHANNEL_ANALOG) { + if (ch->enabled) + devc->enabled_channels = g_slist_append( + devc->enabled_channels, ch); + if (ch->enabled != devc->analog_channels[ch->index]) { + /* Enabled channel is currently disabled, or vice versa. */ + if (siglent_sds_config_set(sdi, "C%d:TRA %s", ch->index + 1, + ch->enabled ? "ON" : "OFF") != SR_OK) + return SR_ERR; + devc->analog_channels[ch->index] = ch->enabled; + } + } else if (ch->type == SR_CHANNEL_LOGIC && some_digital) { + if (ch->enabled) { + /* Turn on LA module if currently off and digital channels are enabled. */ + if (!devc->la_enabled) { + if (siglent_sds_config_set(sdi, "DGST ON") != SR_OK) + return SR_ERR; + g_usleep(630000); + devc->la_enabled = TRUE; + } + devc->enabled_channels = g_slist_append( + devc->enabled_channels, ch); + } + /* Enabled channel is currently disabled, or vice versa. */ + if (siglent_sds_config_set(sdi, "D%d:DGCH %s", ch->index, + ch->enabled ? "ON" : "OFF") != SR_OK) + return SR_ERR; + /* Slowing the command sequence down to let the device handle it */ + g_usleep(630000); + devc->digital_channels[ch->index] = ch->enabled; + } + } + if (!devc->enabled_channels) + return SR_ERR; + /* Turn off LA module if on and no digital channels selected. */ + if (devc->la_enabled && !some_digital) + if (siglent_sds_config_set(sdi, "DGST OFF") != SR_OK) { + devc->la_enabled = FALSE; + g_usleep(500000); + return SR_ERR; + } + + // devc->analog_frame_size = devc->model->series->buffer_samples; + // devc->digital_frame_size = devc->model->series->buffer_samples; + + switch (devc->model->series->protocol) { + case SPO_MODEL: + if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK) + return SR_ERR; + if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK) + return SR_ERR; + break; + case NON_SPO_MODEL: + //TODO implement CML/CNL/DL models + if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK) + return SR_ERR; + if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK) + return SR_ERR; + break; + default: + break; + } + + sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50, + siglent_sds_receive, (void *) sdi); + + std_session_send_df_header(sdi); + + devc->channel_entry = devc->enabled_channels; + + if (siglent_sds_capture_start(sdi) != SR_OK) + return SR_ERR; + + /* Start of first frame. */ + packet.type = SR_DF_FRAME_BEGIN; + sr_session_send(sdi, &packet); return SR_OK; } static int dev_acquisition_stop(struct sr_dev_inst *sdi) { - /* TODO: stop acquisition. */ + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + + devc = sdi->priv; + + if (sdi->status != SR_ST_ACTIVE) { + sr_err("Device inactive, can't stop acquisition."); + return SR_ERR; + } + + std_session_send_df_end(sdi); - (void)sdi; + g_slist_free(devc->enabled_channels); + devc->enabled_channels = NULL; + scpi = sdi->conn; + sr_scpi_source_remove(sdi->session, scpi); return SR_OK; } SR_PRIV struct sr_dev_driver siglent_sds_driver_info = { .name = "siglent-sds", - .longname = "siglent-sds", + .longname = "Siglent SDS1000/SDS2000 Series", .api_version = 1, .init = std_init, .cleanup = std_cleanup, .scan = scan, .dev_list = std_dev_list, - .dev_clear = std_dev_clear, + .dev_clear = dev_clear, .config_get = config_get, .config_set = config_set, .config_list = config_list, diff --git a/src/hardware/siglent-sds/protocol.c b/src/hardware/siglent-sds/protocol.c index 1f67fd77..40f7d104 100644 --- a/src/hardware/siglent-sds/protocol.c +++ b/src/hardware/siglent-sds/protocol.c @@ -1,7 +1,13 @@ /* * This file is part of the libsigrok project. * - * Copyright (C) 2018 mhooijboer + * Siglent implementation: + * Copyright (C) 2016 mhooijboer + * + * The Siglent implementation is based on Rigol driver sources, which are: + * Copyright (C) 2012 Martin Ling + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2013 Mathias Grimmberger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,13 +23,294 @@ * along with this program. If not, see . */ +#define _GNU_SOURCE + #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "libsigrok-internal.h" +#include "scpi.h" #include "protocol.h" -SR_PRIV int siglent_sds_receive_data(int fd, int revents, void *cb_data) +/* Set the next event to wait for in siglent_sds_receive */ +static void siglent_sds_set_wait_event(struct dev_context *devc, enum wait_events event) +{ + + if (event == WAIT_STOP) { + devc->wait_status = 2; + } else { + devc->wait_status = 1; + devc->wait_event = event; + } +} + +/* + * Waiting for a event will return a timeout after 2 to 3 seconds in order + * to not block the application. + */ +static int siglent_sds_event_wait(const struct sr_dev_inst *sdi) +{ + char *buf; + long s; + int out; + struct dev_context *devc; + time_t start; + + if (!(devc = sdi->priv)) + return SR_ERR; + + start = time(NULL); + + s = 10000; /* Sleep time for status refresh */ + if (devc->wait_status == 1) { + do { + if (time(NULL) - start >= 3) { + sr_dbg("Timeout waiting for trigger"); + return SR_ERR_TIMEOUT; + } + + if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK) + return SR_ERR; + sr_atoi(buf, &out); + g_usleep(s); + } while (out == 0); + sr_dbg("Device triggerd"); + if (devc->timebase < 0.51) { + if (devc->timebase > 0.99e-6) { + /* + * Timebase * num hor. divs * 85(%) * 1e6(usecs) / 100 + * -> 85 percent of sweep time + */ + s = (devc->timebase * devc->model->series->num_horizontal_divs * 1000); + sr_spew("Sleeping for %ld usecs after trigger, to let the acq buffer in the device fill", s); + g_usleep(s); + } + + } + } + if (devc->wait_status == 2) { + do { + if (time(NULL) - start >= 3) { + sr_dbg("Timeout waiting for trigger"); + return SR_ERR_TIMEOUT; + } + if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK) + return SR_ERR; + sr_atoi(buf, &out); + g_usleep(s); + /* XXX + * Now this loop condition looks suspicious! A bitwise + * OR of a variable and a non-zero literal should be + * non-zero. Logical AND of several non-zero values + * should be non-zero. Are many parts of the condition + * not taking effect? Was some different condition meant + * to get encoded? This needs review, and adjustment. + */ + } while ((out | DEVICE_STATE_TRIG_RDY && out | DEVICE_STATE_DATA_ACQ) && out | DEVICE_STATE_STOPPED); + sr_dbg("Device triggerd 2"); + siglent_sds_set_wait_event(devc, WAIT_NONE); + } + + return SR_OK; +} + +static int siglent_sds_trigger_wait(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + if (!(devc = sdi->priv)) + return SR_ERR; + return siglent_sds_event_wait(sdi); +} + +/* Wait for scope to got to "Stop" in single shot mode */ +static int siglent_sds_stop_wait(const struct sr_dev_inst *sdi) +{ + + return siglent_sds_event_wait(sdi); +} + +/* Send a configuration setting. */ +SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi, const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = sr_scpi_send_variadic(sdi->conn, format, args); + va_end(args); + + if (ret != SR_OK) { + return SR_ERR; + } + + return SR_OK; +} + +/* Start capturing a new frameset */ +SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + if (!(devc = sdi->priv)) + return SR_ERR; + + switch (devc->model->series->protocol) { + case SPO_MODEL: + if (devc->data_source == DATA_SOURCE_SCREEN) { + char *buf; + int out; + + sr_dbg("Starting data capture for active frameset %" PRIu64 " of %" PRIu64, + devc->num_frames + 1, devc->limit_frames); + if (siglent_sds_config_set(sdi, "ARM") != SR_OK) + return SR_ERR; + if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK) + return SR_ERR; + sr_atoi(buf, &out); + if (out == DEVICE_STATE_TRIG_RDY) { + siglent_sds_set_wait_event(devc, WAIT_TRIGGER); + } else if (out == DEVICE_STATE_TRIG_RDY + 1) { + sr_spew("Device Triggerd"); + siglent_sds_set_wait_event(devc, WAIT_BLOCK); + return SR_OK; + } else { + sr_spew("Device did not enter ARM mode"); + return SR_ERR; + } + } else { //TODO implement History retrieval + unsigned int framecount; + char buf[200]; + int ret; + + sr_dbg("Starting data capture for history frameset"); + if (siglent_sds_config_set(sdi, "FPAR?") != SR_OK) + return SR_ERR; + ret = sr_scpi_read_data(sdi->conn, buf, 200); + if (ret < 0) { + sr_err("Read error while reading data header."); + return SR_ERR; + } + memcpy(&framecount, buf + 40, 4); + if (devc->limit_frames > framecount) { + sr_err("Frame limit higher that frames in buffer of device!"); + } else if (devc->limit_frames == 0) { + devc->limit_frames = framecount; + } + sr_dbg("Starting data capture for history frameset %" PRIu64 " of %" PRIu64, + devc->num_frames + 1, devc->limit_frames); + if (siglent_sds_config_set(sdi, "FRAM %i", devc->num_frames + 1) != SR_OK) + return SR_ERR; + if (siglent_sds_channel_start(sdi) != SR_OK) + return SR_ERR; + siglent_sds_set_wait_event(devc, WAIT_STOP); + } + break; + case NON_SPO_MODEL: + siglent_sds_set_wait_event(devc, WAIT_TRIGGER); + break; + } + + return SR_OK; +} + +/* Start reading data from the current channel */ +SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi) { - const struct sr_dev_inst *sdi; struct dev_context *devc; + struct sr_channel *ch; + + if (!(devc = sdi->priv)) + return SR_ERR; + + ch = devc->channel_entry->data; + + sr_dbg("Starting reading data from channel %d", ch->index + 1); + + switch (devc->model->series->protocol) { + case NON_SPO_MODEL: + case SPO_MODEL: + if (ch->type == SR_CHANNEL_LOGIC) { + if (sr_scpi_send(sdi->conn, "D%d:WF?", + ch->index + 1) != SR_OK) + return SR_ERR; + } else { + if (sr_scpi_send(sdi->conn, "C%d:WF? ALL", + ch->index + 1) != SR_OK) + return SR_ERR; + } + siglent_sds_set_wait_event(devc, WAIT_NONE); + break; + } + + siglent_sds_set_wait_event(devc, WAIT_BLOCK); + + devc->num_channel_bytes = 0; + devc->num_header_bytes = 0; + devc->num_block_bytes = 0; + + return SR_OK; +} + +/* Read the header of a data block */ +static int siglent_sds_read_header(struct sr_dev_inst *sdi, int channelIndex) +{ + struct sr_scpi_dev_inst *scpi = sdi->conn; + struct dev_context *devc = sdi->priv; + char *buf = (char *)devc->buffer; + int ret; + int descLength; + int blockOffset = 15; // Offset for Descriptor block. + long dataLength = 0; + + /* Read header from device */ + ret = sr_scpi_read_data(scpi, buf + devc->num_header_bytes, devc->model->series->buffer_samples); + if (ret < 346) { + sr_err("Read error while reading data header."); + return SR_ERR; + } + sr_dbg("Device returned %i bytes", ret); + devc->num_header_bytes += ret; + buf += blockOffset; //Skip to start Descriptor block + + // Parse WaveDescriptor Header + memcpy(&descLength, buf + 36, 4); // Descriptor block length + memcpy(&dataLength, buf + 60, 4); // Data block length + + devc->vdiv[channelIndex] = 2; + devc->vert_offset[channelIndex] = 0; + devc->blockHeaderSize = descLength + 15; + ret = dataLength; + sr_dbg("Received data block header: '%s' -> block length %d", buf, ret); + return ret; +} + +SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct sr_scpi_dev_inst *scpi; + struct dev_context *devc; + 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; + struct sr_datafeed_logic logic; + int len, i; + struct sr_channel *ch; + gsize expected_data_bytes = 0; + char *memsize; (void)fd; @@ -33,9 +320,399 @@ SR_PRIV int siglent_sds_receive_data(int fd, int revents, void *cb_data) if (!(devc = sdi->priv)) return TRUE; - if (revents == G_IO_IN) { - /* TODO */ + scpi = sdi->conn; + + if (!(revents == G_IO_IN || revents == 0)) + return TRUE; + + memsize = NULL; + if (sr_scpi_get_string(sdi->conn, "MSIZ?", &memsize) != SR_OK) + return SR_ERR; + + switch (devc->wait_event) { + case WAIT_NONE: + break; + case WAIT_TRIGGER: + if (siglent_sds_trigger_wait(sdi) != SR_OK) + return TRUE; + if (siglent_sds_channel_start(sdi) != SR_OK) + return TRUE; + return TRUE; + case WAIT_BLOCK: + if (siglent_sds_channel_start(sdi) != SR_OK) + return TRUE; + break; + case WAIT_STOP: + if (siglent_sds_stop_wait(sdi) != SR_OK) { + return TRUE; + } + if (siglent_sds_channel_start(sdi) != SR_OK) { + return TRUE; + } + return TRUE; + default: + sr_err("BUG: Unknown event target encountered"); + break; + } + + ch = devc->channel_entry->data; + + if (devc->num_block_bytes == 0) { + + if (g_ascii_strcasecmp(memsize, "14M") == 0){ + sr_err("Device memory depth is set to 14Mpts, so please be patient"); + g_usleep(4900000); // Sleep for large memory set + } + sr_dbg("New block header expected"); + len = siglent_sds_read_header(sdi, ch->index); + expected_data_bytes = len; + if (len == 0) + /* Still reading the header. */ + return TRUE; + if (len == -1) { + sr_err("Read error, aborting capture."); + packet.type = SR_DF_FRAME_END; + sr_session_send(sdi, &packet); + sdi->driver->dev_acquisition_stop(sdi); + return TRUE; + } + + if (devc->data_source == DATA_SOURCE_SCREEN + && (unsigned)len < expected_data_bytes) { + sr_dbg("Discarding short data block"); + sr_scpi_read_data(scpi, (char *)devc->buffer, len + 1); + return TRUE; + } + devc->num_block_bytes = len; + devc->num_block_read = 0; + } + + len = devc->num_block_bytes - devc->num_block_read; + if (len > ACQ_BUFFER_SIZE) + len = ACQ_BUFFER_SIZE; + + /*Offset the data block buffer past the IEEE header and Description Header*/ + devc->buffer += devc->blockHeaderSize; + + if (len == -1) { + sr_err("Read error, aborting capture."); + packet.type = SR_DF_FRAME_END; + sr_session_send(sdi, &packet); + sdi->driver->dev_acquisition_stop(sdi); + return TRUE; } + sr_dbg("Received %d bytes.", len); + + devc->num_block_read += len; + + if (ch->type == SR_CHANNEL_ANALOG) { + float vdiv = devc->vdiv[ch->index]; + float offset = devc->vert_offset[ch->index]; + GArray *float_data; + static GArray *data; + float voltage; + float vdivlog; + int digits; + + data = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len); + g_array_append_vals(data, devc->buffer, len); + float_data = g_array_new(FALSE, FALSE, sizeof(float)); + for (i = 0; i < len; i++) { + voltage = (float)g_array_index(data, int8_t, i) / 25; + voltage = ((vdiv * voltage) - offset); + g_array_append_val(float_data, voltage); + } + vdivlog = log10f(vdiv); + digits = -(int) vdivlog + (vdivlog < 0.0); + sr_analog_init(&analog, &encoding, &meaning, &spec, digits); + analog.meaning->channels = g_slist_append(NULL, ch); + analog.num_samples = float_data->len; + analog.data = (float *)float_data->data; + analog.meaning->mq = SR_MQ_VOLTAGE; + analog.meaning->unit = SR_UNIT_VOLT; + analog.meaning->mqflags = 0; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + sr_session_send(sdi, &packet); + g_slist_free(analog.meaning->channels); + g_array_free(data, TRUE); + } else { + logic.length = len; + logic.unitsize = 1; + logic.data = devc->buffer; + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + sr_session_send(sdi, &packet); + } + + if (devc->num_block_read == devc->num_block_bytes) { + sr_dbg("Block has been completed"); + /* Prepare for possible next block */ + sr_dbg("Prepare for possible next block"); + devc->num_header_bytes = 0; + devc->num_block_bytes = 0; + if (devc->data_source != DATA_SOURCE_SCREEN) { + siglent_sds_set_wait_event(devc, WAIT_BLOCK); + } + if (!sr_scpi_read_complete(scpi)) { + sr_err("Read should have been completed"); + packet.type = SR_DF_FRAME_END; + sr_session_send(sdi, &packet); + sdi->driver->dev_acquisition_stop(sdi); + return TRUE; + } + devc->num_block_read = 0; + } else { + sr_dbg("%" PRIu64 " of %" PRIu64 " block bytes read", + devc->num_block_read, devc->num_block_bytes); + } + devc->num_channel_bytes += len; + if (devc->num_channel_bytes < expected_data_bytes) { + /* Don't have the full data for this channel yet, re-run. */ + return TRUE; + } + if (devc->channel_entry->next) { + /* We got the frame for this channel, now get the next channel. */ + devc->channel_entry = devc->channel_entry->next; + siglent_sds_channel_start(sdi); + } else { + /* Done with this frame. */ + packet.type = SR_DF_FRAME_END; + sr_session_send(sdi, &packet); + + if (++devc->num_frames == devc->limit_frames) { + /* Last frame, stop capture. */ + sdi->driver->dev_acquisition_stop(sdi); + } else { + /* Get the next frame, starting with the first channel. */ + devc->channel_entry = devc->enabled_channels; + siglent_sds_capture_start(sdi); + + /* Start of next frame. */ + packet.type = SR_DF_FRAME_BEGIN; + sr_session_send(sdi, &packet); + } + } return TRUE; } + +SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_channel *ch; + char *cmd; + unsigned int i; + int res; + char *response; + gchar **tokens; + int num_tokens; + + devc = sdi->priv; + + /* Analog channel state. */ + for (i = 0; i < devc->model->analog_channels; i++) { + cmd = g_strdup_printf("C%i:TRA?", i + 1); + res = sr_scpi_get_bool(sdi->conn, cmd, &devc->analog_channels[i]); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + ch = g_slist_nth_data(sdi->channels, i); + ch->enabled = devc->analog_channels[i]; + } + sr_dbg("Current analog channel state:"); + for (i = 0; i < devc->model->analog_channels; i++) + sr_dbg("CH%d %s", i + 1, devc->analog_channels[i] ? "On" : "Off"); + + /* Digital channel state. */ + if (devc->model->has_digital) { + gboolean status; + + sr_dbg("Check Logic Analyzer channel state"); + devc->la_enabled = FALSE; + cmd = g_strdup_printf("DGST?"); + res = sr_scpi_get_bool(sdi->conn, cmd, &status); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + sr_dbg("Logic Analyzer status: %s", status ? "On" : "Off"); + if (status) { + devc->la_enabled = TRUE; + for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) { + cmd = g_strdup_printf("D%i:DGCH?", i); + res = sr_scpi_get_bool(sdi->conn, cmd, &devc->digital_channels[i]); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + ch = g_slist_nth_data(sdi->channels, i + devc->model->analog_channels); + ch->enabled = devc->digital_channels[i]; + sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "On" : "Off"); + } + } else { + for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) { + ch = g_slist_nth_data(sdi->channels, i + devc->model->analog_channels); + devc->digital_channels[i] = FALSE; + ch->enabled = devc->digital_channels[i]; + sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "On" : "Off"); + } + } + } + + /* Timebase. */ + if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK) + return SR_ERR; + sr_dbg("Current timebase %g", devc->timebase); + + /* Probe attenuation. */ + for (i = 0; i < devc->model->analog_channels; i++) { + cmd = g_strdup_printf("C%d:ATTN?", i + 1); + res = sr_scpi_get_float(sdi->conn, cmd, &devc->attenuation[i]); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + } + sr_dbg("Current probe attenuation:"); + for (i = 0; i < devc->model->analog_channels; i++) + sr_dbg("CH%d %g", i + 1, devc->attenuation[i]); + + /* Vertical gain and offset. */ + if (siglent_sds_get_dev_cfg_vertical(sdi) != SR_OK) + return SR_ERR; + + /* Coupling. */ + for (i = 0; i < devc->model->analog_channels; i++) { + cmd = g_strdup_printf("C%d:CPL?", i + 1); + res = sr_scpi_get_string(sdi->conn, cmd, &devc->coupling[i]); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + } + + sr_dbg("Current coupling:"); + for (i = 0; i < devc->model->analog_channels; i++) + sr_dbg("CH%d %s", i + 1, devc->coupling[i]); + + /* Trigger source. */ + response = NULL; + tokens = NULL; + if (sr_scpi_get_string(sdi->conn, "TRSE?", &response) != SR_OK) + return SR_ERR; + tokens = g_strsplit(response, ",", 0); + for (num_tokens = 0; tokens[num_tokens] != NULL; num_tokens++); + if (num_tokens < 4) { + sr_dbg("IDN response not according to spec: %80.s.", response); + g_strfreev(tokens); + g_free(response); + return SR_ERR_DATA; + } + g_free(response); + devc->trigger_source = g_strstrip(g_strdup(tokens[2])); + sr_dbg("Current trigger source %s", devc->trigger_source); + + /* TODO Horizontal trigger position. */ + + devc->horiz_triggerpos = 0; + sr_dbg("Current horizontal trigger position %g", devc->horiz_triggerpos); + + /* Trigger slope. */ + cmd = g_strdup_printf("%s:TRSL?", devc->trigger_source); + res = sr_scpi_get_string(sdi->conn, cmd, &devc->trigger_slope); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + sr_dbg("Current trigger slope %s", devc->trigger_slope); + + /* Trigger level. */ + cmd = g_strdup_printf("%s:TRLV?", devc->trigger_source); + res = sr_scpi_get_float(sdi->conn, cmd, &devc->trigger_level); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + sr_dbg("Current trigger level %g", devc->trigger_level); + return SR_OK; +} + +SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + char *cmd; + unsigned int i; + int res; + + devc = sdi->priv; + + /* Vertical gain. */ + for (i = 0; i < devc->model->analog_channels; i++) { + cmd = g_strdup_printf("C%d:VDIV?", i + 1); + res = sr_scpi_get_float(sdi->conn, cmd, &devc->vdiv[i]); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + } + sr_dbg("Current vertical gain:"); + for (i = 0; i < devc->model->analog_channels; i++) + sr_dbg("CH%d %g", i + 1, devc->vdiv[i]); + + /* Vertical offset. */ + for (i = 0; i < devc->model->analog_channels; i++) { + cmd = g_strdup_printf("C%d:OFST?", i + 1); + res = sr_scpi_get_float(sdi->conn, cmd, &devc->vert_offset[i]); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + } + sr_dbg("Current vertical offset:"); + for (i = 0; i < devc->model->analog_channels; i++) + sr_dbg("CH%d %g", i + 1, devc->vert_offset[i]); + + return SR_OK; +} + +SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + char *cmd; + int res; + char *samplePointsString; + float samplerateScope; + float fvalue; + char *first, *concat; + + devc = sdi->priv; + cmd = g_strdup_printf("SANU? C1"); + res = sr_scpi_get_string(sdi->conn, cmd, &samplePointsString); + g_free(cmd); + if (res != SR_OK) + return SR_ERR; + if (strcasestr(samplePointsString, "Mpts") != NULL) { + samplePointsString[strlen(samplePointsString) - 4] = '\0'; + + if (strcasestr(samplePointsString, ".") != NULL) { + first = strtok(samplePointsString, "."); + concat = strcat(first, strtok(NULL, ".")); + if (sr_atof_ascii(concat, &fvalue) != SR_OK || fvalue == 0.0) { + sr_dbg("Invalid float converted from scope response."); + return SR_ERR; + } + } else { + if (sr_atof_ascii(samplePointsString, &fvalue) != SR_OK || fvalue == 0.0) { + sr_dbg("Invalid float converted from scope response."); + return SR_ERR; + } + } + samplerateScope = fvalue * 100000; + } else { + samplePointsString[strlen(samplePointsString) - 4] = '\0'; + if (sr_atof_ascii(samplePointsString, &fvalue) != SR_OK || fvalue == 0.0) { + sr_dbg("Invalid float converted from scope response."); + return SR_ERR; + } + samplerateScope = fvalue * 1000; + } + /* Get the Timebase. */ + if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK) + return SR_ERR; + sr_dbg("Current timebase %g", devc->timebase); + devc->sampleRate = samplerateScope / (devc->timebase * devc->model->series->num_horizontal_divs); + return SR_OK; +} diff --git a/src/hardware/siglent-sds/protocol.h b/src/hardware/siglent-sds/protocol.h index bee75141..198fa757 100644 --- a/src/hardware/siglent-sds/protocol.h +++ b/src/hardware/siglent-sds/protocol.h @@ -20,6 +20,7 @@ #ifndef LIBSIGROK_HARDWARE_SIGLENT_SDS_PROTOCOL_H #define LIBSIGROK_HARDWARE_SIGLENT_SDS_PROTOCOL_H +#include #include #include #include @@ -27,9 +28,137 @@ #define LOG_PREFIX "siglent-sds" +/* Size of acquisition buffers */ +//#define ACQ_BUFFER_SIZE (6000000) +#define ACQ_BUFFER_SIZE (18000000) + +/* Maximum number of samples to retrieve at once. */ +#define ACQ_BLOCK_SIZE (30 * 1000) + +#define MAX_ANALOG_CHANNELS 4 +#define MAX_DIGITAL_CHANNELS 16 + +#define DEVICE_STATE_STOPPED 0 /* Scope is in stopped state */ +#define DEVICE_STATE_DATA_ACQ 1 /* A new signal has been acquired */ +#define DEVICE_STATE_TRIG_RDY 8192 /* Trigger is ready */ + +enum protocol_version { + SPO_MODEL, + NON_SPO_MODEL, +}; + +enum data_format { + FORMAT_IEEE488_2, +}; + +enum data_source { + DATA_SOURCE_SCREEN, + DATA_SOURCE_HISTORY, +}; + +struct siglent_sds_vendor { + const char *name; + const char *full_name; + const char *usb_name; +}; + +struct siglent_sds_series { + const struct siglent_sds_vendor *vendor; + const char *name; + enum protocol_version protocol; + uint64_t max_timebase[2]; + uint64_t min_vdiv[2]; + int num_horizontal_divs; + int num_vertical_divs; + int buffer_samples; +}; + +struct siglent_sds_model { + const struct siglent_sds_series *series; + const char *name; + uint64_t min_timebase[2]; + unsigned int analog_channels; + bool has_digital; + unsigned int digital_channels; +}; + +enum wait_events { + WAIT_NONE, /* Don't wait */ + WAIT_TRIGGER, /* Wait for trigger */ + WAIT_BLOCK, /* Wait for block data (only when reading sample mem) */ + WAIT_STOP, /* Wait for scope stopping (only single shots) */ +}; + +/** Private, per-device-instance driver context. */ struct dev_context { + /* Device model */ + const struct siglent_sds_model *model; + + /* Device properties */ + const uint64_t (*timebases)[2]; + uint64_t num_timebases; + const uint64_t (*vdivs)[2]; + uint64_t num_vdivs; + + /* Channel groups */ + struct sr_channel_group **analog_groups; + struct sr_channel_group *digital_group; + + /* Acquisition settings */ + GSList *enabled_channels; + uint64_t limit_frames; + enum data_source data_source; + uint64_t analog_frame_size; + uint64_t digital_frame_size; + uint64_t num_samples; + long blockHeaderSize; + float sampleRate; + + /* Device settings */ + gboolean analog_channels[MAX_ANALOG_CHANNELS]; + gboolean digital_channels[MAX_DIGITAL_CHANNELS]; + gboolean la_enabled; + float timebase; + float attenuation[MAX_ANALOG_CHANNELS]; + float vdiv[MAX_ANALOG_CHANNELS]; + int vert_reference[MAX_ANALOG_CHANNELS]; + float vert_offset[MAX_ANALOG_CHANNELS]; + char *trigger_source; + float horiz_triggerpos; + char *trigger_slope; + float trigger_level; + char *coupling[MAX_ANALOG_CHANNELS]; + + /* Operational state */ + + /* Number of frames received in total. */ + uint64_t num_frames; + /* GSList entry for the current channel. */ + GSList *channel_entry; + /* Number of bytes received for current channel. */ + uint64_t num_channel_bytes; + /* Number of bytes of block header read */ + uint64_t num_header_bytes; + /* Number of bytes in current data block, if 0 block header expected */ + uint64_t num_block_bytes; + /* Number of data block bytes already read */ + uint64_t num_block_read; + /* What to wait for in *_receive */ + enum wait_events wait_event; + /* Trigger/block copying/stop waiting status */ + int wait_status; + /* Acq buffers used for reading from the scope and sending data to app */ + unsigned char *buffer; + float *data; }; -SR_PRIV int siglent_sds_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi, + const char *format, ...); +SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi); +SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi); +SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data); +SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi); +SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi); +SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi); #endif