From 46ece526383c244f86238c375f638ccb1796035a Mon Sep 17 00:00:00 2001 From: Eric Kuzmenko Date: Thu, 17 Feb 2022 17:54:45 -0500 Subject: [PATCH] siglent-sds: Add SDS2000X Plus series support Don't set num_block_bytes before any waveform data is read. Make SIGLENT_HEADER_SIZE 361 based on experimentation. Add a pause after setting sending a command to enter ARM mode. This is necessary to prevent crashes when the horizontal scale is long. Move the start point after every full 10MB block is read. Don't wait for the instrument to prepare its output buffers if its a SDS2000X Plus. Fix multi-channel support and ensure that the correct sample rate is obtained when the horizontal window is changed or when channels are enabled/disabled in "display" mode. If a channel is turned on/off on the oscilloscope then use Sigrok's settings instead. --- src/hardware/siglent-sds/api.c | 54 +++++++++++++++++--- src/hardware/siglent-sds/protocol.c | 77 +++++++++++++++++++++++------ src/hardware/siglent-sds/protocol.h | 6 ++- 3 files changed, 113 insertions(+), 24 deletions(-) diff --git a/src/hardware/siglent-sds/api.c b/src/hardware/siglent-sds/api.c index 76955e6da0..af0b21c41a 100644 --- a/src/hardware/siglent-sds/api.c +++ b/src/hardware/siglent-sds/api.c @@ -171,6 +171,7 @@ enum series { SDS1000XP, SDS1000XE, SDS2000X, + SDS2000XP, }; /* short name, full name */ @@ -183,19 +184,21 @@ static const struct siglent_sds_vendor supported_vendors[] = { * 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}, + { 50, 1 }, { 2, 1000 }, 18, 8, 25, 1400363}, [SDS1000CNL] = {VENDOR(SIGLENT), "SDS1000CNL", NON_SPO_MODEL, - { 50, 1 }, { 2, 1000 }, 18, 8, 1400363}, + { 50, 1 }, { 2, 1000 }, 18, 8, 25, 1400363}, [SDS1000DL] = {VENDOR(SIGLENT), "SDS1000DL", NON_SPO_MODEL, - { 50, 1 }, { 2, 1000 }, 18, 8, 1400363}, + { 50, 1 }, { 2, 1000 }, 18, 8, 25, 1400363}, [SDS1000X] = {VENDOR(SIGLENT), "SDS1000X", SPO_MODEL, - { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + { 50, 1 }, { 500, 100000 }, 14, 8, 25, 14000363}, [SDS1000XP] = {VENDOR(SIGLENT), "SDS1000X+", SPO_MODEL, - { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + { 50, 1 }, { 500, 100000 }, 14, 8, 25, 14000363}, [SDS1000XE] = {VENDOR(SIGLENT), "SDS1000XE", ESERIES, - { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + { 50, 1 }, { 500, 100000 }, 14, 8, 25, 14000363}, [SDS2000X] = {VENDOR(SIGLENT), "SDS2000X", SPO_MODEL, - { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + { 50, 1 }, { 500, 100000 }, 14, 8, 25, 14000363}, + [SDS2000XP] = {VENDOR(SIGLENT), "SDS2000X+", SPO_MODEL, + { 100, 1 }, { 500, 100000 }, 10, 8, 30, 14000363}, }; #define SERIES(x) &supported_series[x] @@ -227,6 +230,11 @@ 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(SDS2000XP), "SDS2102X Plus", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS2000XP), "SDS2104X Plus", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS2000XP), "SDS2204X Plus", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS2000XP), "SDS2354X Plus", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS2000XP), "SDS2504X Plus", { 1, 1000000000 }, 4, TRUE, 16 }, }; static struct sr_dev_driver siglent_sds_driver_info; @@ -429,7 +437,38 @@ static int config_get(uint32_t key, GVariant **data, *data = g_variant_new_string("History"); break; case SR_CONF_SAMPLERATE: + /* SDS2000X+: The channels need to be enabled/disabled prior to + * obtaining the sample rate when the data source is set to display. + * If a channel is turned on/off on the oscilloscope then use Sigrok's settings instead. */ + if (strcmp(devc->model->series->name, "SDS2000X+\n") && devc->data_source == DATA_SOURCE_SCREEN) { + GSList *l; + for (l = sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type == SR_CHANNEL_ANALOG) { + char *channel_state; + char *cmd = g_strdup_printf("C%d:TRA?", ch->index + 1); + sr_scpi_get_string(sdi->conn, cmd, &channel_state); + if (!strcmp(channel_state, "ON") && !ch->enabled) + if (siglent_sds_config_set(sdi, "C%d:TRA %s", ch->index + 1, + ch->enabled ? "ON" : "OFF") != SR_OK) + return SR_ERR; + if (!strcmp(channel_state, "OFF") && ch->enabled) + if (siglent_sds_config_set(sdi, "C%d:TRA %s", ch->index + 1, + ch->enabled ? "ON" : "OFF") != SR_OK) + return SR_ERR; + 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; + } + } + } + } + devc->samplerate_obtained = FALSE; siglent_sds_get_dev_cfg_horizontal(sdi); + devc->samplerate_obtained = TRUE; *data = g_variant_new_uint64(devc->samplerate); break; case SR_CONF_TRIGGER_SOURCE: @@ -858,6 +897,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) // devc->digital_frame_size = devc->model->series->buffer_samples; siglent_sds_get_dev_cfg_horizontal(sdi); + siglent_sds_get_dev_cfg_vertical(sdi); switch (devc->model->series->protocol) { case SPO_MODEL: if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK) diff --git a/src/hardware/siglent-sds/protocol.c b/src/hardware/siglent-sds/protocol.c index 919df9b27c..e197711d0c 100644 --- a/src/hardware/siglent-sds/protocol.c +++ b/src/hardware/siglent-sds/protocol.c @@ -167,6 +167,13 @@ SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi) devc->num_frames + 1, devc->limit_frames); if (siglent_sds_config_set(sdi, "ARM") != SR_OK) return SR_ERR; + /* This pause is necessary for large memory depths. */ + struct sr_channel *ch = devc->channel_entry->data; + if (ch->type == SR_CHANNEL_ANALOG) { + float wait = devc->memory_depth_analog / 100; + sr_spew("Waiting %.f ms for the instrument to enter ARM mode.", wait / 1000); + g_usleep(wait); + } if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK) return SR_ERR; sr_atoi(buf, &out); @@ -322,6 +329,7 @@ static int siglent_sds_read_header(struct sr_dev_inst *sdi) ret = sr_scpi_read_data(scpi, buf, SIGLENT_HEADER_SIZE); if (ret < SIGLENT_HEADER_SIZE) { sr_err("Read error while reading data header."); + sr_dbg("Device returned %i bytes.", ret); return SR_ERR; } sr_dbg("Device returned %i bytes.", ret); @@ -519,10 +527,12 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) switch (devc->model->series->protocol) { case NON_SPO_MODEL: case SPO_MODEL: - /* The older models need more time to prepare the the output buffers due to CPU speed. */ - wait = (devc->memory_depth_analog * 2.5); - sr_dbg("Waiting %.f0 ms for device to prepare the output buffers", wait / 1000); - g_usleep(wait); + if (strcmp(devc->model->series->name, "SDS2000X+")) { + /* The older models need more time to prepare the the output buffers due to CPU speed. */ + wait = (devc->memory_depth_analog * 2.5); + sr_dbg("Waiting %.f ms for device to prepare the output buffers", wait / 1000); + g_usleep(wait); + } if (sr_scpi_read_begin(scpi) != SR_OK) return TRUE; break; @@ -547,7 +557,6 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) sdi->driver->dev_acquisition_stop(sdi); return TRUE; } - devc->num_block_bytes = len; devc->num_block_read = 0; if (len == -1) { @@ -566,13 +575,27 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) len = devc->num_samples; } else { sr_dbg("Requesting: %" PRIu64 " bytes.", devc->num_samples - devc->num_block_bytes); + /* SDS2000X+ sends 10MB blocks, as found by "WAV:MAXPoint?". */ + /* It needs to have the next starting point specified to continue. */ + if (!strcmp(devc->model->series->name, "SDS2000X+")) { + if (sr_scpi_send(sdi->conn, "WAV:SOUR C%d", ch->index + 1) != SR_OK) + return SR_ERR; + if (sr_scpi_send(sdi->conn, "WAV:STARt %d", devc->num_block_bytes) != SR_OK) + return SR_ERR; + if (sr_scpi_send(sdi->conn, "WAV:DATA?") != SR_OK) + return SR_ERR; + sr_scpi_read_data(scpi, (char *)devc->buffer, 16); + } len = sr_scpi_read_data(scpi, (char *)devc->buffer, devc->num_samples-devc->num_block_bytes); - if (len == -1) { + if (len == -1 || len == 0) { sr_err("Read error, aborting capture."); std_session_send_df_frame_end(sdi); sdi->driver->dev_acquisition_stop(sdi); return TRUE; } + /* SDS2000X+: Throw away the last two 0A 0A bytes for full 10MB blocks. */ + if (!strcmp(devc->model->series->name, "SDS2000X+") && len == 10000002) + len = 10000000; devc->num_block_read++; devc->num_block_bytes += len; } @@ -589,7 +612,7 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) 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 = (float)g_array_index(data, int8_t, i) / devc->model->series->code_per_div; voltage = ((vdiv * voltage) - offset); g_array_append_val(float_data, voltage); } @@ -609,16 +632,22 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) g_array_free(data, TRUE); } len = 0; - if (devc->num_samples == (devc->num_block_bytes - SIGLENT_HEADER_SIZE)) { + if (devc->num_samples == devc->num_block_bytes) { sr_dbg("Transfer has been completed."); devc->num_header_bytes = 0; devc->num_block_bytes = 0; read_complete = TRUE; + /* SDS2000X+: Move the start point back to 0. */ + if (!strcmp(devc->model->series->name, "SDS2000X+")) + if (sr_scpi_send(sdi->conn, "WAV:STARt 0") != SR_OK) + return SR_ERR; if (!sr_scpi_read_complete(scpi)) { - sr_err("Read should have been completed."); - std_session_send_df_frame_end(sdi); - sdi->driver->dev_acquisition_stop(sdi); - return TRUE; + sr_err("Reading CH%d should have been completed.", ch->index + 1); + if (!devc->channel_entry->next) { + std_session_send_df_frame_end(sdi); + sdi->driver->dev_acquisition_stop(sdi); + return TRUE; + } } devc->num_block_read = 0; } else { @@ -741,7 +770,7 @@ SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi) } /* Timebase. */ - if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK) + if (sr_scpi_get_double(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK) return SR_ERR; sr_dbg("Current timebase: %g.", devc->timebase); @@ -890,7 +919,19 @@ SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi) case SPO_MODEL: case NON_SPO_MODEL: cmd = g_strdup_printf("SANU? C1"); - res = sr_scpi_get_string(sdi->conn, cmd, &sample_points_string); + /* SDS2000X+: If capturing from the display + * then quickly trigger and stop to get the correct memory depth. + * This is done due to the number of sample points not being updated after + * the horizontal window changes or after a channel is enabled/disabled. */ + if (!strcmp(devc->model->series->name, "SDS2000X+") && devc->data_source == DATA_SOURCE_SCREEN && !devc->samplerate_obtained) { + if (sr_scpi_send(sdi->conn, "TRIG_MODE SINGLE") != SR_OK) + return SR_ERR; + res = sr_scpi_get_string(sdi->conn, cmd, &sample_points_string); + if (sr_scpi_send(sdi->conn, ":TRIG:STOP") != SR_OK) + return SR_ERR; + } + else + res = sr_scpi_get_string(sdi->conn, cmd, &sample_points_string); g_free(cmd); samplerate_scope = 0; fvalue = 0; @@ -915,6 +956,12 @@ SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi) } samplerate_scope = fvalue * 10000; } else { + sample_points_string[strlen(sample_points_string)] = '\0'; + if (sr_atof_ascii(sample_points_string, &fvalue) != SR_OK) { + sr_dbg("Invalid float converted from scope response."); + g_free(sample_points_string); + return SR_ERR; + } samplerate_scope = fvalue; } g_free(sample_points_string); @@ -936,7 +983,7 @@ SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi) }; /* Get the timebase. */ - if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK) + if (sr_scpi_get_double(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK) return SR_ERR; sr_dbg("Current timebase: %g.", devc->timebase); diff --git a/src/hardware/siglent-sds/protocol.h b/src/hardware/siglent-sds/protocol.h index 89abaf640b..294d67b710 100644 --- a/src/hardware/siglent-sds/protocol.h +++ b/src/hardware/siglent-sds/protocol.h @@ -31,7 +31,7 @@ //#define ACQ_BUFFER_SIZE (6000000) #define ACQ_BUFFER_SIZE (18000000) -#define SIGLENT_HEADER_SIZE 363 +#define SIGLENT_HEADER_SIZE 361 #define SIGLENT_DIG_HEADER_SIZE 346 /* Maximum number of samples to retrieve at once. */ @@ -69,6 +69,7 @@ struct siglent_sds_series { uint64_t min_vdiv[2]; int num_horizontal_divs; int num_vertical_divs; + int code_per_div; int buffer_samples; }; @@ -115,12 +116,13 @@ struct dev_context { uint64_t memory_depth_digital; long block_header_size; float samplerate; + gboolean samplerate_obtained; /* Device settings */ gboolean analog_channels[MAX_ANALOG_CHANNELS]; gboolean digital_channels[MAX_DIGITAL_CHANNELS]; gboolean la_enabled; - float timebase; + double timebase; float attenuation[MAX_ANALOG_CHANNELS]; float vdiv[MAX_ANALOG_CHANNELS]; int vert_reference[MAX_ANALOG_CHANNELS];