From 1fe616743215db1c98b638b58a625f7374f76af6 Mon Sep 17 00:00:00 2001 From: Matti Eiden Date: Sat, 6 Feb 2021 01:33:17 +0200 Subject: [PATCH 1/4] siglent-sds: Add new e-series models: SDS1104X-U, SDS2202X-E, SDS2352X-E --- src/hardware/siglent-sds/api.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/hardware/siglent-sds/api.c b/src/hardware/siglent-sds/api.c index 76955e6da..79e5bdceb 100644 --- a/src/hardware/siglent-sds/api.c +++ b/src/hardware/siglent-sds/api.c @@ -170,7 +170,9 @@ enum series { SDS1000X, SDS1000XP, SDS1000XE, + SDS1000XU, SDS2000X, + SDS2000XE, }; /* short name, full name */ @@ -194,8 +196,12 @@ static const struct siglent_sds_series supported_series[] = { { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, [SDS1000XE] = {VENDOR(SIGLENT), "SDS1000XE", ESERIES, { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + [SDS1000XU] = {VENDOR(SIGLENT), "SDS1000XU", ESERIES, + { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, [SDS2000X] = {VENDOR(SIGLENT), "SDS2000X", SPO_MODEL, { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + [SDS2000XE] = {VENDOR(SIGLENT), "SDS2000XE", ESERIES, + { 50, 1 }, { 500, 100000 }, 14, 8, 28000363}, }; #define SERIES(x) &supported_series[x] @@ -219,6 +225,7 @@ static const struct siglent_sds_model supported_models[] = { { 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(SDS1000XU), "SDS1104X-U", { 1, 1000000000 }, 4, FALSE, 0}, { SERIES(SDS2000X), "SDS2072X", { 2, 1000000000 }, 2, FALSE, 0 }, { SERIES(SDS2000X), "SDS2074X", { 2, 1000000000 }, 4, FALSE, 0 }, { SERIES(SDS2000X), "SDS2102X", { 2, 1000000000 }, 2, FALSE, 0 }, @@ -227,6 +234,8 @@ static const struct siglent_sds_model supported_models[] = { { SERIES(SDS2000X), "SDS2204X", { 2, 1000000000 }, 4, FALSE, 0 }, { SERIES(SDS2000X), "SDS2302X", { 2, 1000000000 }, 2, FALSE, 0 }, { SERIES(SDS2000X), "SDS2304X", { 2, 1000000000 }, 4, FALSE, 0 }, + { SERIES(SDS2000XE), "SDS2202X-E", { 2, 1000000000 }, 2, FALSE, 0 }, + { SERIES(SDS2000XE), "SDS2352X-E", { 2, 1000000000 }, 2, FALSE, 0 }, }; static struct sr_dev_driver siglent_sds_driver_info; From e4eb1481d393f8c4887e2c04a2a9a628c89f2c0c Mon Sep 17 00:00:00 2001 From: Matti Eiden Date: Sat, 6 Feb 2021 00:11:32 +0200 Subject: [PATCH 2/4] siglent-sds: Rewrite eseries acquisition logic This commit re-implements the whole acquisition logic for the Siglent E-series (SDS1000X-E, SDS1000X-U, SDS2000X-E). The acquisition is based on a state machine to keep the logic simple and robust. Additionally this improves maintainability as the E-series acquisition logic is now isolated from the older models which follow a partially different logic and have other constraints when compared to the E-series. --- src/hardware/siglent-sds/api.c | 112 +++++- src/hardware/siglent-sds/protocol.c | 567 ++++++++++++++++++++++------ src/hardware/siglent-sds/protocol.h | 45 ++- 3 files changed, 584 insertions(+), 140 deletions(-) diff --git a/src/hardware/siglent-sds/api.c b/src/hardware/siglent-sds/api.c index 79e5bdceb..ed14707bf 100644 --- a/src/hardware/siglent-sds/api.c +++ b/src/hardware/siglent-sds/api.c @@ -54,6 +54,14 @@ static const uint32_t devopts[] = { SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, }; +static const uint32_t eseries_devopts[] = { + SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_LIST, + 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, +}; + static const uint32_t devopts_cg_analog[] = { SR_CONF_NUM_VDIV | SR_CONF_GET, SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, @@ -159,6 +167,13 @@ static const char *data_sources[] = { "History", }; +/* Do not change the order of entries. */ +static const char *eseries_data_sources[] = { + "Single", + "History", + "Read-only", +}; + enum vendor { SIGLENT, }; @@ -304,6 +319,7 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi) devc = g_malloc0(sizeof(struct dev_context)); devc->limit_frames = 1; devc->model = model; + devc->acq_error = FALSE; sr_scpi_hw_info_free(hw_info); @@ -352,7 +368,14 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi) 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; + switch (devc->model->series->protocol) { + case ESERIES: + devc->data_source = DATA_SOURCE_SINGLE; + break; + default: + devc->data_source = DATA_SOURCE_SCREEN; + break; + } sdi->priv = devc; @@ -432,16 +455,27 @@ static int config_get(uint32_t key, GVariant **data, *data = g_variant_new_uint64(devc->limit_frames); break; case SR_CONF_DATA_SOURCE: - if (devc->data_source == DATA_SOURCE_SCREEN) + switch (devc->data_source) { + case DATA_SOURCE_SCREEN: *data = g_variant_new_string("Screen"); - else if (devc->data_source == DATA_SOURCE_HISTORY) + break; + case DATA_SOURCE_HISTORY: *data = g_variant_new_string("History"); + break; + case DATA_SOURCE_SINGLE: + *data = g_variant_new_string("Single"); + break; + case DATA_SOURCE_READ_ONLY: + *data = g_variant_new_string("Read-only"); + break; + } break; case SR_CONF_SAMPLERATE: siglent_sds_get_dev_cfg_horizontal(sdi); *data = g_variant_new_uint64(devc->samplerate); break; case SR_CONF_TRIGGER_SOURCE: + // TODO support CH3, CH4? if (!strcmp(devc->trigger_source, "ACL")) tmp_str = "AC Line"; else if (!strcmp(devc->trigger_source, "CHAN1")) @@ -608,6 +642,7 @@ static int config_set(uint32_t key, GVariant *data, g_free(cmd); return ret; case SR_CONF_TRIGGER_SOURCE: + // TODO should only set channels that are enabled? if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 0) return SR_ERR_ARG; g_free(devc->trigger_source); @@ -679,14 +714,31 @@ static int config_set(uint32_t key, GVariant *data, return ret; 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; + switch (devc->model->series->protocol) { + case ESERIES: + if (!strcmp(tmp_str, "Single")) + devc->data_source = DATA_SOURCE_SINGLE; + else if (!strcmp(tmp_str, "History")) + devc->data_source = DATA_SOURCE_HISTORY; + else if (!strcmp(tmp_str, "Read-only")) + devc->data_source = DATA_SOURCE_READ_ONLY; + else { + sr_err("Unknown data source: '%s'.", tmp_str); + return SR_ERR; + } + break; + + default: + 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; } break; case SR_CONF_SAMPLERATE: @@ -718,10 +770,16 @@ static int config_list(uint32_t key, GVariant **data, switch (key) { case SR_CONF_SCAN_OPTIONS: case SR_CONF_DEVICE_OPTIONS: - if (!cg) + if (!cg) { + if (devc && devc->model->series->protocol == ESERIES) { + return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, eseries_devopts); + } return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts); + } + if (!devc) return SR_ERR_ARG; + if (cg == devc->digital_group) { *data = std_gvar_array_u32(NULL, 0); return SR_OK; @@ -759,6 +817,7 @@ static int config_list(uint32_t key, GVariant **data, *data = std_gvar_tuple_array(devc->timebases, devc->num_timebases); break; case SR_CONF_TRIGGER_SOURCE: + // TODO ESERIES list only channels that are enabled if (!devc) /* Can't know this until we have the exact model. */ return SR_ERR_ARG; @@ -778,9 +837,11 @@ static int config_list(uint32_t key, GVariant **data, *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 1); break; case SPO_MODEL: - case ESERIES: *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources)); break; + case ESERIES: + *data = g_variant_new_strv(ARRAY_AND_SIZE(eseries_data_sources)); + break; } break; case SR_CONF_NUM_HDIV: @@ -865,7 +926,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) // devc->analog_frame_size = devc->model->series->buffer_samples; // devc->digital_frame_size = devc->model->series->buffer_samples; - siglent_sds_get_dev_cfg_horizontal(sdi); switch (devc->model->series->protocol) { case SPO_MODEL: @@ -889,16 +949,30 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) default: break; } + switch (devc->model->series->protocol) { + case ESERIES: + devc->acq_state = ACQ_SETUP; + devc->retry_count = 0; + sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 10, + siglent_sds_eseries_receive, (void *) sdi); + break; + default: + sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 7000, + siglent_sds_receive, (void *) sdi); + break; + } - sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 7000, - 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; + switch (devc->model->series->protocol) { + case ESERIES: + break; + default: + if (siglent_sds_capture_start(sdi) != SR_OK) + return SR_ERR; + } /* Start of first frame. */ std_session_send_df_frame_begin(sdi); diff --git a/src/hardware/siglent-sds/protocol.c b/src/hardware/siglent-sds/protocol.c index d59e52cbc..a44b47356 100644 --- a/src/hardware/siglent-sds/protocol.c +++ b/src/hardware/siglent-sds/protocol.c @@ -208,61 +208,15 @@ SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi) siglent_sds_set_wait_event(devc, WAIT_STOP); } break; - case ESERIES: - 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); - g_free(buf); - if (out == DEVICE_STATE_TRIG_RDY) { - siglent_sds_set_wait_event(devc, WAIT_TRIGGER); - } else if (out == DEVICE_STATE_DATA_TRIG_RDY) { - sr_spew("Device triggered."); - 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 than 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; - } + default: + sr_err("Unsupported protocol for sds_capture_start"); + return SR_ERR; + } return SR_OK; } @@ -288,17 +242,9 @@ SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi) return SR_ERR; siglent_sds_set_wait_event(devc, WAIT_NONE); break; - case ESERIES: - if (ch->type == SR_CHANNEL_ANALOG) { - 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); - if (sr_scpi_read_begin(sdi->conn) != SR_OK) - return TRUE; - siglent_sds_set_wait_event(devc, WAIT_BLOCK); - break; + default: + sr_err("Unsupported protocol in sds_channel_start"); + return SR_ERR; } devc->num_channel_bytes = 0; @@ -526,14 +472,9 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) if (sr_scpi_read_begin(scpi) != SR_OK) return TRUE; break; - case ESERIES: - /* The newer models (ending with the E) have faster CPUs but still need time when a slow timebase is selected. */ - if (sr_scpi_read_begin(scpi) != SR_OK) - return TRUE; - wait = ((devc->timebase * devc->model->series->num_horizontal_divs) * 100000); - sr_dbg("Waiting %.f0 ms for device to prepare the output buffers", wait / 1000); - g_usleep(wait); - break; + default: + sr_err("Unsupported protocol"); + return SR_ERR; } sr_dbg("New block with header expected."); @@ -777,45 +718,54 @@ SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi) sr_dbg("CH%d %s", i + 1, devc->coupling[i]); /* Trigger source. */ + // TODO ESERIES response = NULL; tokens = NULL; - if (sr_scpi_get_string(sdi->conn, "TRSE?", &response) != SR_OK) - return SR_ERR; - tokens = g_strsplit(response, ",", 0); - num_tokens = g_strv_length(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. */ - response = ""; - trigger_pos = 0; - // if (sr_scpi_get_string(sdi->conn, g_strdup_printf("%s:TRDL?", devc->trigger_source), &response) != SR_OK) - // return SR_ERR; - // len = strlen(response); - len = strlen(tokens[4]); - if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "us")) { - trigger_pos = atof(tokens[4]) / SR_GHZ(1); - sr_dbg("Current trigger position us %s.", tokens[4] ); - } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "ns")) { - trigger_pos = atof(tokens[4]) / SR_MHZ(1); - sr_dbg("Current trigger position ms %s.", tokens[4] ); - } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "ms")) { - trigger_pos = atof(tokens[4]) / SR_KHZ(1); - sr_dbg("Current trigger position ns %s.", tokens[4] ); - } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "s")) { - trigger_pos = atof(tokens[4]); - sr_dbg("Current trigger position s %s.", tokens[4] ); - }; - devc->horiz_triggerpos = trigger_pos; - - sr_dbg("Current horizontal trigger position %.10f.", devc->horiz_triggerpos); + switch (devc->model->series->protocol) { + case ESERIES: + sr_dbg("Skip setting trigger source for eseries"); + devc->trigger_source = "LINE"; + devc->trigger_level = 0; + devc->trigger_slope = 0; + break; + default: + if (sr_scpi_get_string(sdi->conn, "TRSE?", &response) != SR_OK) + return SR_ERR; + tokens = g_strsplit(response, ",", 0); + num_tokens = g_strv_length(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. */ + response = ""; + trigger_pos = 0; + // if (sr_scpi_get_string(sdi->conn, g_strdup_printf("%s:TRDL?", devc->trigger_source), &response) != SR_OK) + // return SR_ERR; + // len = strlen(response); + len = strlen(tokens[4]); + if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "us")) { + trigger_pos = atof(tokens[4]) / SR_GHZ(1); + sr_dbg("Current trigger position us %s.", tokens[4] ); + } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "ns")) { + trigger_pos = atof(tokens[4]) / SR_MHZ(1); + sr_dbg("Current trigger position ms %s.", tokens[4] ); + } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "ms")) { + trigger_pos = atof(tokens[4]) / SR_KHZ(1); + sr_dbg("Current trigger position ns %s.", tokens[4] ); + } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "s")) { + trigger_pos = atof(tokens[4]); + sr_dbg("Current trigger position s %s.", tokens[4] ); + }; + devc->horiz_triggerpos = trigger_pos; + + sr_dbg("Current horizontal trigger position %.10f.", devc->horiz_triggerpos); /* Trigger slope. */ cmd = g_strdup_printf("%s:TRSL?", devc->trigger_source); @@ -827,16 +777,17 @@ SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi) return SR_ERR; sr_dbg("Current trigger slope: %s.", devc->trigger_slope); - /* Trigger level, only when analog channel. */ - if (g_str_has_prefix(tokens[2], "C")) { - 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); + /* Trigger level, only when analog channel. */ + if (g_str_has_prefix(tokens[2], "C")) { + 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); + } + break; } - return SR_OK; } @@ -946,3 +897,389 @@ SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi) return SR_OK; } + + +SR_PRIV int siglent_sds_eseries_receive(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + + if (!(sdi = cb_data)) + return TRUE; + + if (!(devc = sdi->priv)) + return TRUE; + + if (!(revents == G_IO_IN || revents == 0)) + return TRUE; + + switch (devc->acq_state) { + case ACQ_SETUP: + return siglent_sds_eseries_acq_setup(sdi, devc); + case ACQ_WAIT_STOP: + return siglent_sds_eseries_acq_wait_stop(sdi, devc); + case ACQ_REQUEST_WAVEFORM: + return siglent_sds_eseries_acq_request_waveform(sdi, devc); + case ACQ_READ_WAVEFORM_HEADER: + return siglent_sds_eseries_acq_read_waveform_header(sdi, devc); + case ACQ_READ_WAVEFORM_DATA: + return siglent_sds_eseries_acq_read_waveform_data(sdi, devc); + case ACQ_FINALIZE_WAVEFORM: + return siglent_sds_eseries_acq_finalize_waveform(sdi, devc); + case ACQ_DONE: + return siglent_sds_eseries_stop_acquisition(sdi); + } +} + +SR_PRIV int siglent_sds_eseries_acq_setup(const struct sr_dev_inst *sdi, struct dev_context *devc) { + char *buf; + int frame_count; + + switch(devc->data_source) { + case DATA_SOURCE_SINGLE: + sr_dbg("Single shot capture"); + devc->limit_frames = 1; + if (siglent_sds_config_set(sdi, "TRMD SINGLE") != SR_OK) + return siglent_sds_eseries_acq_error(devc); + + break; + case DATA_SOURCE_HISTORY: + sr_dbg("History capture"); + // If current state is stopped, we don't want to close history + // because it resumes run mode! + if (sr_scpi_get_string(sdi->conn, ":TRMD?", &buf) != SR_OK) + return siglent_sds_eseries_acq_error(devc); + if (g_strcmp0(buf, "STOP") == 0) { + devc->close_history = FALSE; + + } else { + devc->close_history = TRUE; + } + g_free(buf); + + // Enable history mode if necessary + if (sr_scpi_get_string(sdi->conn, ":HSMD?", &buf) != SR_OK) { + return siglent_sds_eseries_acq_error(devc); + } + if (g_strcmp0(buf, "OFF") == 0) { + if (siglent_sds_config_set(sdi, ":HSMD ON") != SR_OK) { + return siglent_sds_eseries_acq_error(devc); + } + } else { + // History is already open, so we can't trust FRAM? to get the total frame count + // Set FRAM to silly big number and it will be set to the max frame + if (siglent_sds_config_set(sdi, ":FRAM 10000000") != SR_OK) { + return siglent_sds_eseries_acq_error(devc); + } + } + g_free(buf); + + if (sr_scpi_get_int(sdi->conn, ":FRAM?", &frame_count) != SR_OK) { + return siglent_sds_eseries_acq_error(devc); + } + + if (frame_count < 1) { + sr_err("Number of frames less than 1"); + return siglent_sds_eseries_acq_error(devc); + } + + devc->limit_frames = (uint64_t) frame_count; + + // TODO can we somehow configure which frames to capture? + // Not fun to wait 100000 frames being transfered + if (siglent_sds_config_set(sdi, ":FRAM 1") != SR_OK) + return siglent_sds_eseries_acq_error(devc); + + sr_dbg("Start history capture with %d frames", frame_count); + break; + + case DATA_SOURCE_READ_ONLY: + sr_dbg("Read-only capture"); + devc->limit_frames = 1; + break; + + default: + sr_err("Unknown capture mode"); + return siglent_sds_eseries_acq_error(devc); + } + devc->acq_state = ACQ_WAIT_STOP; + return TRUE; +} + + +SR_PRIV int siglent_sds_eseries_acq_wait_stop(const struct sr_dev_inst *sdi, struct dev_context *devc) { + char *response; + if (sr_scpi_get_string(sdi->conn, "TRMD?", &response) != SR_OK) + return siglent_sds_eseries_acq_error(devc); + + if (!g_strcmp0(response, "STOP")) { + // User could tweak the scope to get a trigger + // while we are waiting for the device to stop + // So check horizontal and vertical cfgs always after stop. + siglent_sds_get_dev_cfg_vertical(sdi); + siglent_sds_get_dev_cfg_horizontal(sdi); + devc->acq_state = ACQ_REQUEST_WAVEFORM; + } + + g_free(response); + return TRUE; +} + +SR_PRIV int siglent_sds_eseries_acq_request_waveform(const struct sr_dev_inst *sdi, struct dev_context *devc) { + struct sr_channel *ch; + ch = devc->channel_entry->data; + + if (ch->type == SR_CHANNEL_ANALOG) { + if (sr_scpi_send(sdi->conn, "C%d:WF? DAT2", + ch->index + 1) != SR_OK) + return siglent_sds_eseries_acq_error(devc); + if (sr_scpi_read_begin(sdi->conn) != SR_OK) + return siglent_sds_eseries_acq_error(devc); + } else { + sr_err("Digital acquisition is not supported"); + return siglent_sds_eseries_acq_error(devc); + } + devc->acq_state = ACQ_READ_WAVEFORM_HEADER; + return TRUE; +} + +SR_PRIV int siglent_sds_eseries_acq_read_waveform_header(const struct sr_dev_inst *sdi, struct dev_context *devc) { + char *buf; + int len; + buf = (char *) devc->buffer; + + // Read signature + len = sr_scpi_read_data(sdi->conn, buf, 7); + buf[7] = '\0'; + if (len != 7 || g_strcmp0(buf, "DAT2,#9") != 0) { + sr_err("Bad header signature"); + return siglent_sds_eseries_acq_error(devc); + } + + // Read waveform length + len = sr_scpi_read_data(sdi->conn, buf, 9); + buf[9] = '\0'; + if (len != 9 || sr_atoi(buf, (int *) &devc->num_samples) != SR_OK) { + sr_err("Failed to read waveform length"); + return siglent_sds_eseries_acq_error(devc); + } + + sr_dbg("Waveform sample count: %" PRIu64, devc->num_samples); + devc->num_block_bytes = 0; + devc->num_block_read = 0; + + devc->acq_state = ACQ_READ_WAVEFORM_DATA; + return TRUE; +} + + +SR_PRIV int siglent_sds_eseries_acq_read_waveform_data(const struct sr_dev_inst *sdi, struct dev_context *devc) { + struct sr_channel *ch; + int len; + + ch = devc->channel_entry->data; + len = 0; + uint64_t bytes_available = devc->num_samples - devc->num_block_bytes + 2; // Add 2 for the linefeeds + guint loop_bytes_read = 0; + char *buf; + buf = (char *) devc->buffer; + + do { + len = sr_scpi_read_data(sdi->conn, (char *)buf, (int) (devc->num_samples - devc->num_block_bytes + 2)); // Add 2 for the linefeeds + if (len == -1) { + if (loop_bytes_read > 0) { + sr_dbg("Read error, passing data forward"); + break; + } + if (devc->retry_count > 10) { + sr_err("Read error and retry count exceeded, aborting capture"); + siglent_sds_eseries_stop_acquisition(sdi); + return siglent_sds_eseries_acq_error(devc); + } + devc->retry_count++; + g_usleep(10000); + return TRUE; + } else if (len == 0) { + sr_dbg("Empty read"); + if (devc->retry_count > 10) { + sr_err("Empty read and retry count exceeded, aborting capture"); + siglent_sds_eseries_stop_acquisition(sdi); + return siglent_sds_eseries_acq_error(devc); + } + devc->retry_count++; + g_usleep(10000); + } else if (len == 2 && devc->num_block_bytes == 0) { + // Waveform contains only two linefeeds. Probably a bug in hardware. + sr_err("Promised waveform was missing."); + siglent_sds_eseries_stop_acquisition(sdi); + return siglent_sds_eseries_acq_error(devc); + } + devc->retry_count = 0; + loop_bytes_read += (guint) len; + devc->num_block_bytes += (unsigned long) len; + buf += len; + devc->num_block_read++; + sr_dbg("Received block: %d, %d bytes.", devc->num_block_read, len); + } while (loop_bytes_read < MIN(102400, bytes_available)); + + // The waveform ends in two linefeed bytes, we don't want to pass those onwards as analog data. + // Ignore bytes that exceed num_samples + sr_dbg("loop_bytes_read prewrap: %d", loop_bytes_read); + if (devc->num_block_bytes > devc->num_samples) { + loop_bytes_read = MAX(loop_bytes_read - (guint) MAX(devc->num_block_bytes - devc->num_samples, 0), 0); + } + + + if (loop_bytes_read > 0) { + sr_dbg("Sending %d bytes of waveform data", loop_bytes_read); + siglent_sds_eseries_send_waveform(sdi, devc, loop_bytes_read); + } else { + sr_dbg("Nothing to send"); + } + + if (devc->num_block_bytes >= devc->num_samples) { + sr_dbg("Waveform data read completed"); + devc->acq_state = ACQ_FINALIZE_WAVEFORM; + } + + return TRUE; +} + +SR_PRIV int siglent_sds_eseries_send_waveform(const struct sr_dev_inst *sdi, struct dev_context *devc, guint bytes_read) { + struct sr_channel *ch; + ch = devc->channel_entry->data; + guint i; + + float vdiv = devc->vdiv[ch->index]; + float offset = devc->vert_offset[ch->index]; + GArray *float_data; + static GArray *data; + float voltage, vdivlog; + 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; + + data = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), bytes_read); + g_array_append_vals(data, devc->buffer, bytes_read); + float_data = g_array_new(FALSE, FALSE, sizeof(float)); + for (i = 0; i < bytes_read; 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 < (float) 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 = (void *) 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); + return TRUE; +} + +SR_PRIV int siglent_sds_eseries_acq_finalize_waveform(const struct sr_dev_inst *sdi, struct dev_context *devc) { + char *cmd; + + if (devc->channel_entry->next) { + devc->channel_entry = devc->channel_entry->next; + } else { + std_session_send_df_frame_end(sdi); + if (++devc->num_frames == devc->limit_frames) { + sdi->driver->dev_acquisition_stop(sdi); + devc->acq_state = ACQ_DONE; + if (devc->data_source == DATA_SOURCE_HISTORY && devc->close_history) { + if (siglent_sds_config_set(sdi, ":HSMD OFF") != SR_OK) { + sr_err("Failed to close history"); + return siglent_sds_eseries_acq_error(devc); + } + } + return TRUE; + } else { + devc->channel_entry = devc->enabled_channels; + cmd = g_strdup_printf(":FRAM %" PRIu64, devc->num_frames + 1); + if (siglent_sds_config_set(sdi, cmd) != SR_OK) { + sr_err("Changing to frame %" PRIu64 " failed", devc->num_frames + 1); + siglent_sds_eseries_stop_acquisition(sdi); + return siglent_sds_eseries_acq_error(devc); + } + g_free(cmd); + std_session_send_df_frame_begin(sdi); + } + } + devc->acq_state = ACQ_REQUEST_WAVEFORM; + return TRUE; +} + +SR_PRIV int siglent_sds_eseries_stop_acquisition(const struct sr_dev_inst *sdi) { + std_session_send_df_frame_end(sdi); + sdi->driver->dev_acquisition_stop(sdi); + return TRUE; +} + +SR_PRIV int siglent_sds_eseries_acq_error(struct dev_context *devc) { + devc->acq_error = TRUE; + sr_dbg("Set acq_error flag"); + return SR_ERR; +} + +SR_PRIV int siglent_sds_flush_buffers(const struct sr_dev_inst *sdi) { + /* Unexpected errors can leave stale data into the receive buffers. + * This function attempts to flush the buffers by sending a command + * with a known response, and it then attempts to read byte by byte + * until it sees the expected response (OFF\n) + */ + char flush_buf[5]; + int flush_count = -3; + int len; + int retry_count = 0; + + if (sr_scpi_send(sdi->conn, ":CHDR?") != SR_OK) + return SR_ERR; + if (sr_scpi_read_begin(sdi->conn) != SR_OK) + return SR_ERR; + + memset(&flush_buf[0], ' ', sizeof(flush_buf)); + flush_buf[4] = '\0'; + do { + flush_buf[0] = flush_buf[1]; + flush_buf[1] = flush_buf[2]; + flush_buf[2] = flush_buf[3]; + sr_dbg("Try to read 1 byte.."); + // USBTMC safety loop.. + do { + len = sr_scpi_read_data(sdi->conn, &flush_buf[3], 1); + if (len == -1) { + if (retry_count > 10) { + sr_err("Flush retry attempts exceeded"); + return SR_ERR; + } + retry_count++; + sr_err("Read error during flush."); + g_usleep(10000); + } + } while (len == -1); + + if (len == 0) { + sr_err("Read empty during flush."); + return SR_ERR; + } + sr_dbg("Flush buf: %s", flush_buf); + if (g_strcmp0(flush_buf, "OFF\n") == 0) { + break; + } + flush_count++; + } while (1); + sr_dbg("Flushed %d bytes.", flush_count); + return SR_OK; +} diff --git a/src/hardware/siglent-sds/protocol.h b/src/hardware/siglent-sds/protocol.h index 89abaf640..d7fa76b85 100644 --- a/src/hardware/siglent-sds/protocol.h +++ b/src/hardware/siglent-sds/protocol.h @@ -46,14 +46,16 @@ #define DEVICE_STATE_DATA_TRIG_RDY 8193 /* Trigger is ready bit */ enum protocol_version { - SPO_MODEL, + SPO_MODEL = 10000, NON_SPO_MODEL, ESERIES, }; enum data_source { - DATA_SOURCE_SCREEN, + DATA_SOURCE_SCREEN = 10000, DATA_SOURCE_HISTORY, + DATA_SOURCE_SINGLE, + DATA_SOURCE_READ_ONLY, }; struct siglent_sds_vendor { @@ -82,10 +84,21 @@ struct siglent_sds_model { }; 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) */ + WAIT_NONE = 10000, /* 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) */ + WAIT_INIT /* Wait for waveform request init */ +}; + +enum acq_states { + ACQ_SETUP = 10000, /* Perform necessary setup SCPI commands before waveform read */ + ACQ_WAIT_STOP, /* Wait for device to stop */ + ACQ_REQUEST_WAVEFORM, /* Request the waveform, prepare SCPI read */ + ACQ_READ_WAVEFORM_HEADER, /* Read and parse the header */ + ACQ_READ_WAVEFORM_DATA, /* Read the waveform data */ + ACQ_FINALIZE_WAVEFORM, + ACQ_DONE, /* All done */ }; struct dev_context { @@ -153,6 +166,14 @@ struct dev_context { unsigned char *buffer; float *data; GArray *dig_buffer; + + /* Eseries specific */ + enum acq_states acq_state; + /* Close history after acquistion is done if history was not open when acqusition started */ + gboolean close_history; + /* General retry counter to allow bailing out of infinite loops */ + int retry_count; + gboolean acq_error; }; SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi, @@ -164,4 +185,16 @@ 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); +SR_PRIV int siglent_sds_eseries_receive(int fd, int revents, void *cb_data); +SR_PRIV int siglent_sds_eseries_acq_setup(const struct sr_dev_inst *sdi, struct dev_context *devc); +SR_PRIV int siglent_sds_eseries_acq_wait_stop(const struct sr_dev_inst *sdi, struct dev_context *devc); +SR_PRIV int siglent_sds_eseries_acq_request_waveform(const struct sr_dev_inst *sdi, struct dev_context *devc); +SR_PRIV int siglent_sds_eseries_acq_read_waveform_header(const struct sr_dev_inst *sdi, struct dev_context *devc); +SR_PRIV int siglent_sds_eseries_acq_read_waveform_data(const struct sr_dev_inst *sdi, struct dev_context *devc); +SR_PRIV int siglent_sds_eseries_send_waveform(const struct sr_dev_inst *sdi, struct dev_context *devc, guint bytes_read); +SR_PRIV int siglent_sds_eseries_acq_finalize_waveform(const struct sr_dev_inst *sdi, struct dev_context *devc); +SR_PRIV int siglent_sds_eseries_stop_acquisition(const struct sr_dev_inst *sdi); +SR_PRIV int siglent_sds_eseries_acq_error(struct dev_context *devc); +SR_PRIV int siglent_sds_flush_buffers(const struct sr_dev_inst *sdi); + #endif From 64584dc968101d4da73716137bed2dba0d4a8c4f Mon Sep 17 00:00:00 2001 From: Alex Carter Date: Sat, 2 Oct 2021 13:22:21 +1000 Subject: [PATCH 3/4] Added product code for SDS1104X-U --- contrib/60-libsigrok.rules | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/60-libsigrok.rules b/contrib/60-libsigrok.rules index 716178039..04f486981 100644 --- a/contrib/60-libsigrok.rules +++ b/contrib/60-libsigrok.rules @@ -290,9 +290,11 @@ ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1006", ENV{ID_SIGROK}="1" # f4ec:ee3a: E.g. SDS1052DL+ scope # f4ec:ee38: E.g. SDS1104X-E scope or SDM3055 Multimeter # f4ed:ee3a: E.g. SDS1202X-E scope or SDG1010 waveform generator +# f4ec:1012: E.g. SDS1104X-U scope ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="ee38", ENV{ID_SIGROK}="1" ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1" ATTRS{idVendor}=="f4ed", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1" +ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="1012", ENV{ID_SIGROK}="1" # sigrok FX2 LA (8ch) # fx2grok-flat (before and after renumeration) From 656d7f62dd1961f4600815faba763af4daa74488 Mon Sep 17 00:00:00 2001 From: Michael Billington Date: Fri, 30 Aug 2024 09:41:37 +1000 Subject: [PATCH 4/4] sort siglent udev entries --- contrib/60-libsigrok.rules | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/60-libsigrok.rules b/contrib/60-libsigrok.rules index 04f486981..c6798229f 100644 --- a/contrib/60-libsigrok.rules +++ b/contrib/60-libsigrok.rules @@ -287,14 +287,14 @@ ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1005", ENV{ID_SIGROK}="1" ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1006", ENV{ID_SIGROK}="1" # Siglent USBTMC devices. -# f4ec:ee3a: E.g. SDS1052DL+ scope +# f4ec:1012: E.g. SDS1104X-U scope # f4ec:ee38: E.g. SDS1104X-E scope or SDM3055 Multimeter +# f4ec:ee3a: E.g. SDS1052DL+ scope # f4ed:ee3a: E.g. SDS1202X-E scope or SDG1010 waveform generator -# f4ec:1012: E.g. SDS1104X-U scope +ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="1012", ENV{ID_SIGROK}="1" ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="ee38", ENV{ID_SIGROK}="1" ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1" ATTRS{idVendor}=="f4ed", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1" -ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="1012", ENV{ID_SIGROK}="1" # sigrok FX2 LA (8ch) # fx2grok-flat (before and after renumeration)