diff --git a/.gitignore b/.gitignore index 235d51c2c..a3f055be0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ .*.sw* /*.kdev4 /Makefile.am.user +.vscode/ # clangd language server cruft .cache/ diff --git a/src/hardware/siglent-sds/api.c b/src/hardware/siglent-sds/api.c index 76955e6da..c4465a3c3 100644 --- a/src/hardware/siglent-sds/api.c +++ b/src/hardware/siglent-sds/api.c @@ -106,7 +106,7 @@ static const uint64_t timebases[][2] = { static const uint64_t vdivs[][2] = { /* microvolts */ - { 500, 100000 }, + { 500, 1000000 }, /* millivolts */ { 1, 1000 }, { 2, 1000 }, @@ -171,6 +171,16 @@ enum series { SDS1000XP, SDS1000XE, SDS2000X, + SDS2000XP, + SDS800XHD, + SDS1000XHD, + SDS2000XHD, + SDS3000XHD, + SDS5000X, + SDS6000L, + SDS6000A, + SDS6000PRO, + SDS7000A, }; /* short name, full name */ @@ -183,19 +193,39 @@ 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, 1000000 }, 14, 8, 25, 14000363}, [SDS1000XP] = {VENDOR(SIGLENT), "SDS1000X+", SPO_MODEL, - { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + { 50, 1 }, { 500, 1000000 }, 14, 8, 25, 14000363}, [SDS1000XE] = {VENDOR(SIGLENT), "SDS1000XE", ESERIES, - { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + { 50, 1 }, { 500, 1000000 }, 14, 8, 25, 14000363}, [SDS2000X] = {VENDOR(SIGLENT), "SDS2000X", SPO_MODEL, - { 50, 1 }, { 500, 100000 }, 14, 8, 14000363}, + { 50, 1 }, { 500, 1000000 }, 14, 8, 25, 14000363}, + [SDS2000XP] = {VENDOR(SIGLENT), "SDS2000X+", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 14000363}, + [SDS800XHD] = {VENDOR(SIGLENT), "SDS800XHD", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 50000000}, + [SDS1000XHD] = {VENDOR(SIGLENT), "SDS1000XHD", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 100000000}, + [SDS2000XHD] = {VENDOR(SIGLENT), "SDS2000XHD", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 200000000}, + [SDS3000XHD] = {VENDOR(SIGLENT), "SDS3000XHD", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 400000000}, + [SDS5000X] = {VENDOR(SIGLENT), "SDS5000X", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 250000000}, + [SDS6000L] = {VENDOR(SIGLENT), "SDS6000L", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 500000000}, + [SDS6000A] = {VENDOR(SIGLENT), "SDS6000A", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 500000000}, + [SDS6000PRO] = {VENDOR(SIGLENT), "SDS6000PRO", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 500000000}, + [SDS7000A] = {VENDOR(SIGLENT), "SDS7000A", E11, + { 100, 1 }, { 500, 1000000 }, 10, 8, 30, 500000000}, }; #define SERIES(x) &supported_series[x] @@ -227,6 +257,62 @@ 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 }, + // SDS 800X HD Series + { SERIES(SDS800XHD), "SDS802X HD", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS800XHD), "SDS804X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS800XHD), "SDS812X HD", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS800XHD), "SDS814X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS800XHD), "SDS822X HD", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS800XHD), "SDS824X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + // SDS 1000X HD Series + { SERIES(SDS1000XHD), "SDS1102X HD", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS1000XHD), "SDS1104X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS1000XHD), "SDS1202X HD", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS1000XHD), "SDS1204X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + // SDS 2000X HD Series + { SERIES(SDS2000XHD), "SDS2104X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS2000XHD), "SDS2204X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS2000XHD), "SDS2354X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS2000XHD), "SDS2504X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + // SDS 3000X HD Series + { SERIES(SDS3000XHD), "SDS3034X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS3000XHD), "SDS3054X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS3000XHD), "SDS3104X HD", { 1, 1000000000 }, 4, TRUE, 16 }, + // SDS 5000X Series + { SERIES(SDS5000X), "SDS5032X", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS5000X), "SDS5034X", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS5000X), "SDS5052X", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS5000X), "SDS5054X", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS5000X), "SDS5102X", { 1, 1000000000 }, 2, TRUE, 16 }, + { SERIES(SDS5000X), "SDS5104X", { 1, 1000000000 }, 4, TRUE, 16 }, + // SDS 6000L Series + { SERIES(SDS6000L), "SDS6208L", { 1, 1000000000 }, 8, TRUE, 16 }, + { SERIES(SDS6000L), "SDS6204L", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000L), "SDS6108L", { 1, 1000000000 }, 8, TRUE, 16 }, + { SERIES(SDS6000L), "SDS6104L", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000L), "SDS6058L", { 1, 1000000000 }, 8, TRUE, 16 }, + { SERIES(SDS6000L), "SDS6054L", { 1, 1000000000 }, 4, TRUE, 16 }, + // SDS 6000A Series + { SERIES(SDS6000A), "SDS6054A", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000A), "SDS6104A", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000A), "SDS6204A", { 1, 1000000000 }, 4, TRUE, 16 }, + // SDS 6000 Pro Series + { SERIES(SDS6000PRO), "SDS6034 H10 Pro", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000PRO), "SDS6034 H12 Pro", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000PRO), "SDS6054 H10 Pro", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000PRO), "SDS6054 H12 Pro", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000PRO), "SDS6104 H10 Pro", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000PRO), "SDS6104 H12 Pro", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000PRO), "SDS6204 H10 Pro", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS6000PRO), "SDS6204 H12 Pro", { 1, 1000000000 }, 4, TRUE, 16 }, + // SDS 7000A Series + { SERIES(SDS7000A), "SDS7404A", { 1, 1000000000 }, 4, TRUE, 16 }, + { SERIES(SDS7000A), "SDS7304A", { 1, 1000000000 }, 4, TRUE, 16 }, }; static struct sr_dev_driver siglent_sds_driver_info; @@ -628,17 +714,22 @@ static int config_set(uint32_t key, GVariant *data, if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0) return SR_ERR_ARG; devc->vdiv[i] = (float)vdivs[idx][0] / vdivs[idx][1]; - p = vdivs[idx][0]; - switch (vdivs[idx][1]) { - case 1: - cmd = g_strdup_printf("%" PRIu64 "V", p); - break; - case 1000: - cmd = g_strdup_printf("%" PRIu64 "MV", p); - break; - case 100000: - cmd = g_strdup_printf("%" PRIu64 "UV", p); - break; + if(devc->model->series->protocol == E11) { + /* E11 models don't support "MV" format for values bellow 10mV => switch to scientific format */ + cmd = g_strdup_printf("%.0E", devc->vdiv[i]); + } else { + p = vdivs[idx][0]; + switch (vdivs[idx][1]) { + case 1: + cmd = g_strdup_printf("%" PRIu64 "V", p); + break; + case 1000: + cmd = g_strdup_printf("%" PRIu64 "MV", p); + break; + case 1000000: + cmd = g_strdup_printf("%" PRIu64 "UV", p); + break; + } } ret = siglent_sds_config_set(sdi, "C%d:VDIV %s", i + 1, cmd); g_free(cmd); @@ -770,6 +861,7 @@ static int config_list(uint32_t key, GVariant **data, break; case SPO_MODEL: case ESERIES: + case E11: *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources)); break; } @@ -877,11 +969,15 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK) return SR_ERR; break; + case E11: + /** TODO : handle acquisition mode (sampling / average) */ + break; default: break; } - sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 7000, + int tiemout = (devc->model->series->protocol == E11) ? 50 : 7000; + sr_scpi_source_add(sdi->session, scpi, G_IO_IN, tiemout, siglent_sds_receive, (void *) sdi); std_session_send_df_header(sdi); diff --git a/src/hardware/siglent-sds/protocol.c b/src/hardware/siglent-sds/protocol.c index d59e52cbc..16a4bea99 100644 --- a/src/hardware/siglent-sds/protocol.c +++ b/src/hardware/siglent-sds/protocol.c @@ -36,6 +36,137 @@ #include "scpi.h" #include "protocol.h" +/** Wait for 500 ms while reading SCPI data */ +#define WAIT_DATA_TIMEOUT 500000 + +/** + * Wait and read data from SCPI device. + * + * @param scpi Previously initialised SCPI device structure. + * @param buf Buffer to store result. + * @param maxlen Maximum number of bytes to read. + * + * @return Number of bytes read, or SR_ERR upon failure. + */ +static int siglent_scpi_wait_read_data(struct sr_scpi_dev_inst *scpi, + char *buf, int maxlen) +{ + int64_t start = g_get_monotonic_time(); + int64_t elapsed; + int ret; + do { + ret = sr_scpi_read_data(scpi, buf, maxlen); + elapsed = g_get_monotonic_time() - start; + } while ((ret == 0) && (elapsed < WAIT_DATA_TIMEOUT)); + return ret; +} + +/** + * Get the number of points of the current wave and save it to devc->num_samples. + * + * @param sdi the device instance. + * @param devc the device context. + * + * @return SR_ERR upon failure. + */ +static int siglent_get_wave_points(struct sr_dev_inst *sdi,struct dev_context *devc) +{ + /* Get maximum number of point per data block */ + double acq_points; + int result = sr_scpi_get_double(sdi->conn, "ACQ:POIN?", &acq_points); + if(result == SR_OK) + devc->num_samples = acq_points; + return result; +} + +/** + * Get the number of points of the current digital channel and save it to devc->num_samples. + * + * @param sdi the device instance. + * @param devc the device context. + * + * @return SR_ERR upon failure. + */ +static int siglent_get_digital_points(struct sr_dev_inst *sdi,struct dev_context *devc) +{ + /* Get maximum number of point per data block */ + double dig_points; + int result = sr_scpi_get_double(sdi->conn, "DIG:POIN?", &dig_points); + if(result == SR_OK) + devc->num_samples = dig_points/8; /* 8 points per byte for digital channels */ + return result; +} + +/** + * Wait and read data from SCPI device. + * + * @param sdi the device instance. + * @param ch the channel. + * @param digital true for digital channels. + * + * @return Last number of bytes read, or SR_ERR upon failure. + */ +static int siglent_read_wave_e11(struct sr_dev_inst *sdi, struct sr_channel *ch, gboolean digital) +{ + struct sr_scpi_dev_inst *scpi = sdi->conn; + struct dev_context *devc = sdi->priv; + int len = 0; + int maxlen; + /* New block => send block request command */ + if(digital && (devc->num_block_read == 0)) { + /* PRE? command is not called on each Digital channel, so we need to set channel number here for first block*/ + if (sr_scpi_send(sdi->conn, "WAV:SOUR D%d", ch->index) != SR_OK) + return SR_ERR; + } + if (sr_scpi_send(sdi->conn, "WAV:STARt %d",digital ? (devc->num_block_bytes*8) : devc->num_block_bytes) != SR_OK) /* 8 points per byte in digital mode */ + return SR_ERR; + if (sr_scpi_send(sdi->conn, "WAV:DATA?") != SR_OK) + return SR_ERR; + /* Read header size : The header is of form “#9000001000” which nine ASCII integers are used to give the number of the waveform data points (1000 pts). */ + sr_scpi_read_data(scpi, (char *)devc->buffer, 2); + if(devc->buffer[0] != '#') { + /* This is protocol error, consume any pending data before returning */ + sr_scpi_read_data(scpi, (char *)(devc->buffer+devc->num_block_bytes), (devc->model->series->buffer_samples-devc->num_block_bytes)); + if(digital) { + /* In digital mode, SDS 2000X HD (as for firmware 2.5.1.2.2.5) as a bug in block pagination : + * for memory depth > 50Mpts, if start point is >= to a certain value (625000 or 1250000) the DATA? command returns "DAT2,#9000000000" + * as a workaround, let's silently end acquisition at this point by filling the buffer with zeros */ + len = devc->num_samples-devc->num_block_bytes; + memset((char *)(devc->buffer+devc->num_block_bytes), 0, len); + devc->num_block_bytes = devc->num_samples; + return len; + } + return SR_ERR; + } + int headerSize = devc->buffer[1]-'0'; + /* Conume header */ + sr_scpi_read_data(scpi, (char *)devc->buffer, headerSize); + /* Extract block size from header */ + devc->buffer[headerSize] = 0; + devc->max_points = atoi((char*)devc->buffer); + sr_dbg("Parsed data lengh from header '%s' : %d.",(char*)devc->buffer,devc->max_points); + do { + /* Try and read end of block if not in last block or end of wave when in last block */ + maxlen = MIN(devc->num_samples-devc->num_block_bytes,devc->max_points-devc->num_bytes_current_block); + /* For analog channels, blocks are read one at a time, for digital all blocks are read in one buffer */ + len = siglent_scpi_wait_read_data(scpi, (char *)(devc->buffer + (digital ? devc->num_block_bytes : devc->num_bytes_current_block)), maxlen); + 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; + } + devc->num_block_bytes += len; + devc->num_bytes_current_block += len; + sr_dbg("Asked %d, read %" PRIu64 " bytes, %" PRIu64 " remaining.",maxlen,devc->num_block_bytes, devc->num_samples - devc->num_block_bytes); + } while ((devc->num_bytes_current_block < (uint64_t)devc->max_points) && (devc->num_block_bytes < devc->num_samples)); + /* End of block or end of wave => consume 0x0A 0x0A message footer */ + sr_dbg("End of block => consume footer."); + uint16_t footer; + sr_scpi_read_data(scpi, (char *)&footer, 2); + return len; +} + /* 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) { @@ -261,6 +392,24 @@ SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi) case NON_SPO_MODEL: siglent_sds_set_wait_event(devc, WAIT_TRIGGER); break; + case E11: { + char* buf; + /* Get maximum number of point per data block */ + if (sr_scpi_get_int(sdi->conn, "WAV:MAXP?", &devc->max_points) != SR_OK) + return SR_ERR; + sr_dbg("Found maxp value of : %d.", devc->max_points); + + /* Force trigger stop to make sure we have a consistant acquisition across channels */ + if (siglent_sds_config_set(sdi, "TRIG:STOP") != SR_OK) + return SR_ERR; + if (sr_scpi_get_string(sdi->conn, "TRIG:STAT?", &buf) != SR_OK) { + g_free(buf); + return SR_ERR; + } + g_free(buf); + siglent_sds_set_wait_event(devc, WAIT_BLOCK); + } + break; } return SR_OK; @@ -288,6 +437,28 @@ 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 E11: + if (ch->type == SR_CHANNEL_LOGIC) { + if (sr_scpi_send(sdi->conn, "WAV:SOUR D%d", ch->index) != SR_OK) + return SR_ERR; + /* WAV:PRE? is not working consistenly on SDS2000X HD (as for firmware 2.5.1.2.2.5) : + * For memory depth > 5 Mpoints it generates a SCPI response message stating "fp > current memory depth!". + * When only digital channels are activated, the WAV:PRE? response is longer than expected starting by + * "#9000000346WAVEDESC" but continuing after the 346's byte by a second ASCII message : + * "",DESC,#9000000346WAVEDESC". The data length specified in the first part is invalid + * The workaround is to not use the WAV:PRE? command and find the data length at the begenning + * of the WAV:DATA? response */ + devc->block_header_size = 0; + devc->num_samples = 0; + } + else { + 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:PRE?") != SR_OK) + 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", @@ -304,6 +475,7 @@ SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi) devc->num_channel_bytes = 0; devc->num_header_bytes = 0; devc->num_block_bytes = 0; + devc->num_bytes_current_block = 0; return SR_OK; } @@ -315,13 +487,16 @@ static int siglent_sds_read_header(struct sr_dev_inst *sdi) struct dev_context *devc = sdi->priv; char *buf = (char *)devc->buffer; int ret, desc_length; - int block_offset = 15; /* Offset for descriptor block. */ + int block_offset = (devc->model->series->protocol == E11) ? 11 : 15; /* Offset for descriptor block. */ long data_length = 0; /* Read header from device. */ - ret = sr_scpi_read_data(scpi, buf, SIGLENT_HEADER_SIZE); - if (ret < SIGLENT_HEADER_SIZE) { - sr_err("Read error while reading data header."); + int header_size = (devc->model->series->protocol == E11) ? (SIGLENT_DIG_HEADER_SIZE+block_offset+1 /*+1 for 0x0A footer */) : SIGLENT_HEADER_SIZE; ; /* Size of the header, defined in wave_desc_length. */ + ret = sr_scpi_read_data(scpi, buf, header_size); + if (ret < header_size) { + sr_err("Read error while reading data header : received %d, expecred %d.",ret,header_size); + devc->block_header_size = 0; + devc->num_samples = 0; return SR_ERR; } sr_dbg("Device returned %i bytes.", ret); @@ -330,110 +505,251 @@ static int siglent_sds_read_header(struct sr_dev_inst *sdi) /* Parse WaveDescriptor header. */ memcpy(&desc_length, buf + 36, 4); /* Descriptor block length */ - memcpy(&data_length, buf + 60, 4); /* Data block length */ + devc->block_header_size = desc_length + block_offset; + + if(devc->model->series->protocol == E11) { + /* WAV:PRE? is not working consistenly on SDS2000X HD (as for firmware 2.5.1.2.2.5) => get the sample number ACQ:POIN? command */ + if(siglent_get_wave_points(sdi,devc) != SR_OK) { + sr_err("Read error while reading ACQ:POIN?."); + devc->num_samples = 0; + return SR_ERR; + } + if(devc->num_samples > (uint64_t)devc->max_points) { + /* We need to set block size to max_points*/ + if (sr_scpi_send(sdi->conn, "WAV:POINt %d", (int)devc->max_points) != SR_OK) + return SR_ERR; + } + } else { + memcpy(&data_length, buf + 60, 4); /* Data block length */ + devc->num_samples = data_length; + } + sr_dbg("Received data block header: '%s' -> header length = %d, data length = %" PRIu64 ".", buf, ret, devc->num_samples); + return ret; +} + +static int siglent_sds_get_digital_e11(struct sr_dev_inst *sdi, struct sr_channel *ch, uint8_t samplerate_ratio, GArray *data_low_channels, GArray *data_high_channels, gboolean* low_channels, gboolean* high_channels) +{ + uint8_t tmp_value; /* Holding temp value from data */ + GSList *l; + GArray *tmp_samplebuf; /* Temp buffer while iterating over the scope samples */ + GArray *buffdata; + struct sr_scpi_dev_inst *scpi = sdi->conn; + struct dev_context *devc = sdi->priv; + int len = 0; + int channel_index = 0; + uint64_t samples_index; + for (l = sdi->channels; l; l = l->next) { + ch = l->data; + samples_index = 0; + if (ch->type == SR_CHANNEL_LOGIC) { + if (ch->enabled) { + do { + if (sr_scpi_read_begin(scpi) != SR_OK) + return TRUE; + len = siglent_read_wave_e11(sdi,ch,TRUE); + if (len < 0) { + return len; + } - devc->block_header_size = desc_length + 15; - devc->num_samples = data_length; + devc->num_block_read++; + devc->num_bytes_current_block = 0; + } while (devc->num_block_bytes < devc->num_samples); + + + buffdata = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), devc->num_block_bytes); + g_array_append_vals(buffdata, (char *)(devc->buffer), devc->num_block_bytes); + + tmp_samplebuf = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), devc->num_block_bytes*samplerate_ratio); /* New temp buffer. */ + sr_err("Iterate on samples : number = %" PRIu64".",devc->num_block_bytes); + for (uint64_t cur_sample_index = 0; cur_sample_index < (unsigned)devc->num_block_bytes; cur_sample_index++) { + char sample = (char)g_array_index(buffdata, uint8_t, cur_sample_index); + for (int ii = 0; ii < 8; ii++, sample >>= 1) { + if (ch->index < 8) { + channel_index = ch->index; + if (data_low_channels->len <= samples_index) { + tmp_value = 0; /* New sample. */ + (*low_channels) = TRUE; /* We have at least one enabled low channel. */ + } else { + /* Get previous stored sample from low channel buffer. */ + tmp_value = g_array_index(data_low_channels, uint8_t, samples_index*samplerate_ratio); + } + } else { + channel_index = ch->index - 8; + if (data_high_channels->len <= samples_index) { + tmp_value = 0; /* New sample. */ + (*high_channels) = TRUE; /* We have at least one enabled high channel. */ + } else { + /* Get previous stored sample from high channel buffer. */ + tmp_value = g_array_index(data_high_channels, uint8_t, samples_index*samplerate_ratio); + } + } + /* Check if the current scope sample bit is set. */ + if (sample & 0x1) { + tmp_value |= (1UL << channel_index); /* Set current scope sample bit based on channel index. */ + } + + + g_array_append_val(tmp_samplebuf, tmp_value); + + /* SDS2000X+: Since the LA sample rate is a fraction of the sample rate of the analog channels, + * there needs to be repeated "fake" samples inserted after each "real" sample + * in order to make the output match the timebase of an enabled analog channel. + * The scaling by a factor of 2.5 and 5 appears to be necessary due to an artifact + * where the instrument will present half of the entire sample quantity from the screen + * within a single block (625000 bytes, or 5000000 bits / samples). Which means there + * are some legitimate points missing that are filled with "fake" ones at larger timebases. */ + for (int i = 0; i < samplerate_ratio-1; i++, ii++) + g_array_append_val(tmp_samplebuf, tmp_value); + + samples_index++; + } + } - sr_dbg("Received data block header: '%s' -> block length %d.", buf, ret); + /* Clear the buffers to prepare for the new samples */ + if (ch->index < 8) { + g_free(g_array_steal(data_low_channels,NULL)); + } else { + g_free(g_array_steal(data_high_channels,NULL)); + } - return ret; + /* Storing the converted temp values from the the scope into the buffers. */ + for (uint64_t index = 0; index < tmp_samplebuf->len; index++) { + uint8_t value = g_array_index(tmp_samplebuf, uint8_t, index); + if (ch->index < 8) + g_array_append_val(data_low_channels, value); + else + g_array_append_val(data_high_channels, value); + } + devc->num_block_bytes = 0; + devc->num_block_read = 0; + g_free(g_array_steal(tmp_samplebuf, NULL)); + g_free(g_array_steal(buffdata, NULL)); + } + } + } + return len; } -static int siglent_sds_get_digital(const struct sr_dev_inst *sdi, struct sr_channel *ch) +static int siglent_sds_get_digital(struct sr_dev_inst *sdi, struct sr_channel *ch) { struct sr_scpi_dev_inst *scpi = sdi->conn; struct dev_context *devc = sdi->priv; - GArray *tmp_samplebuf; /* Temp buffer while iterating over the scope samples */ char *buf = (char *)devc->buffer; /* Buffer from scope */ - uint8_t tmp_value; /* Holding temp value from data */ - GArray *data_low_channels, *data_high_channels, *buffdata; - GSList *l; + GArray *data_low_channels, *data_high_channels; gboolean low_channels; /* Lower channels enabled */ gboolean high_channels; /* Higher channels enabled */ - int len, channel_index; + int len; uint64_t samples_index; + uint8_t samplerate_ratio = 1; + + if (devc->model->series->protocol == E11) { + /* Get num samples for digital channel */ + if(siglent_get_digital_points(sdi,devc) != SR_OK) { + return SR_ERR; + } + /* Set memory depth accordingly */ + devc->memory_depth_digital = devc->num_samples; + samplerate_ratio = devc->memory_depth_analog / devc->memory_depth_digital; + sr_dbg("Digital configuration : memory depth digital = %" PRIu64 ", memory depth analog = %" PRIu64 ", sample ratio = %d.", + devc->memory_depth_digital,devc->memory_depth_analog,samplerate_ratio); + } + len = 0; - channel_index = 0; low_channels = FALSE; high_channels = FALSE; data_low_channels = g_array_new(FALSE, TRUE, sizeof(uint8_t)); data_high_channels = g_array_new(FALSE, TRUE, sizeof(uint8_t)); - for (l = sdi->channels; l; l = l->next) { - ch = l->data; - samples_index = 0; - if (ch->type != SR_CHANNEL_LOGIC) - continue; - if (!ch->enabled) - continue; - if (sr_scpi_send(sdi->conn, "D%d:WF? DAT2", ch->index) != SR_OK) - return SR_ERR; - if (sr_scpi_read_begin(scpi) != SR_OK) - return TRUE; - len = sr_scpi_read_data(scpi, buf, -1); - if (len < 0) - return TRUE; - len -= 15; - buffdata = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len); - buf += 15; /* Skipping the data header. */ - g_array_append_vals(buffdata, buf, len); - tmp_samplebuf = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len); /* New temp buffer. */ - for (uint64_t cur_sample_index = 0; cur_sample_index < devc->memory_depth_digital; cur_sample_index++) { - char sample = (char)g_array_index(buffdata, uint8_t, cur_sample_index); - for (int ii = 0; ii < 8; ii++, sample >>= 1) { - if (ch->index < 8) { - channel_index = ch->index; - if (data_low_channels->len <= samples_index) { - tmp_value = 0; /* New sample. */ - low_channels = TRUE; /* We have at least one enabled low channel. */ - } else { - /* Get previous stored sample from low channel buffer. */ - tmp_value = g_array_index(data_low_channels, uint8_t, samples_index); - } - } else { - channel_index = ch->index - 8; - if (data_high_channels->len <= samples_index) { - tmp_value = 0; /* New sample. */ - high_channels = TRUE; /* We have at least one enabled high channel. */ + if(devc->model->series->protocol == E11) { + len = siglent_sds_get_digital_e11(sdi,ch,samplerate_ratio,data_low_channels,data_high_channels,&low_channels,&high_channels); + if (len < 0) { + sr_err("Read error, aborting capture."); + std_session_send_df_frame_end(sdi); + sdi->driver->dev_acquisition_stop(sdi); + return FALSE; + } + } + else { + uint8_t tmp_value; /* Holding temp value from data */ + GArray *tmp_samplebuf; /* Temp buffer while iterating over the scope samples */ + GSList *l; + GArray *buffdata; + int channel_index = 0; + for (l = sdi->channels; l; l = l->next) { + ch = l->data; + samples_index = 0; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + if (sr_scpi_send(sdi->conn, "D%d:WF? DAT2", ch->index) != SR_OK) + return SR_ERR; + if (sr_scpi_read_begin(scpi) != SR_OK) + return TRUE; + len = sr_scpi_read_data(scpi, buf, -1); + if (len < 0) + return TRUE; + len -= 15; + buffdata = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len); + buf += 15; /* Skipping the data header. */ + g_array_append_vals(buffdata, buf, len); + tmp_samplebuf = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len); /* New temp buffer. */ + for (uint64_t cur_sample_index = 0; cur_sample_index < devc->memory_depth_digital; cur_sample_index++) { + char sample = (char)g_array_index(buffdata, uint8_t, cur_sample_index); + for (int ii = 0; ii < 8; ii++, sample >>= 1) { + if (ch->index < 8) { + channel_index = ch->index; + if (data_low_channels->len <= samples_index) { + tmp_value = 0; /* New sample. */ + low_channels = TRUE; /* We have at least one enabled low channel. */ + } else { + /* Get previous stored sample from low channel buffer. */ + tmp_value = g_array_index(data_low_channels, uint8_t, samples_index); + } } else { - /* Get previous stored sample from high channel buffer. */ - tmp_value = g_array_index(data_high_channels, uint8_t, samples_index); + channel_index = ch->index - 8; + if (data_high_channels->len <= samples_index) { + tmp_value = 0; /* New sample. */ + high_channels = TRUE; /* We have at least one enabled high channel. */ + } else { + /* Get previous stored sample from high channel buffer. */ + tmp_value = g_array_index(data_high_channels, uint8_t, samples_index); + } } + /* Check if the current scope sample bit is set. */ + if (sample & 0x1) + tmp_value |= 1UL << channel_index; /* Set current scope sample bit based on channel index. */ + g_array_append_val(tmp_samplebuf, tmp_value); + samples_index++; } - /* Check if the current scope sample bit is set. */ - if (sample & 0x1) - tmp_value |= 1UL << channel_index; /* Set current scope sample bit based on channel index. */ - g_array_append_val(tmp_samplebuf, tmp_value); - samples_index++; } - } - /* Clear the buffers to prepare for the new samples */ - if (ch->index < 8) { - g_array_free(data_low_channels, TRUE); - data_low_channels = g_array_new(FALSE, FALSE, sizeof(uint8_t)); - } else { - g_array_free(data_high_channels, TRUE); - data_high_channels = g_array_new(FALSE, FALSE, sizeof(uint8_t)); - } + /* Clear the buffers to prepare for the new samples */ + if (ch->index < 8) { + g_array_free(data_low_channels, TRUE); + data_low_channels = g_array_new(FALSE, FALSE, sizeof(uint8_t)); + } else { + g_array_free(data_high_channels, TRUE); + data_high_channels = g_array_new(FALSE, FALSE, sizeof(uint8_t)); + } - /* Storing the converted temp values from the the scope into the buffers. */ - for (uint64_t index = 0; index < tmp_samplebuf->len; index++) { - uint8_t value = g_array_index(tmp_samplebuf, uint8_t, index); - if (ch->index < 8) - g_array_append_val(data_low_channels, value); - else - g_array_append_val(data_high_channels, value); + /* Storing the converted temp values from the the scope into the buffers. */ + for (uint64_t index = 0; index < tmp_samplebuf->len; index++) { + uint8_t value = g_array_index(tmp_samplebuf, uint8_t, index); + if (ch->index < 8) + g_array_append_val(data_low_channels, value); + else + g_array_append_val(data_high_channels, value); + } + g_array_free(tmp_samplebuf, TRUE); + g_array_free(buffdata, TRUE); } - g_array_free(tmp_samplebuf, TRUE); - g_array_free(buffdata, TRUE); } /* Combining the lower and higher channel buffers into one buffer for sigrok. */ devc->dig_buffer = g_array_new(FALSE, FALSE, sizeof(uint8_t)); - for (uint64_t index = 0; index < devc->memory_depth_digital; index++) { + for (uint64_t index = 0; index < devc->memory_depth_digital * samplerate_ratio; index++) { uint8_t value; if (low_channels) { value = g_array_index(data_low_channels, uint8_t, index); @@ -469,7 +785,8 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) struct sr_analog_spec spec; struct sr_datafeed_logic logic; struct sr_channel *ch; - int len, i; + int len; + uint64_t i; float wait; gboolean read_complete = FALSE; @@ -534,6 +851,10 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) sr_dbg("Waiting %.f0 ms for device to prepare the output buffers", wait / 1000); g_usleep(wait); break; + case E11: + if (sr_scpi_read_begin(scpi) != SR_OK) + return TRUE; + break; } sr_dbg("New block with header expected."); @@ -547,7 +868,9 @@ 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; + if(devc->model->series->protocol != E11) { + devc->num_block_bytes = len; + } devc->num_block_read = 0; if (len == -1) { @@ -563,20 +886,30 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) /* We received all data as one block. */ /* Offset the data block buffer past the IEEE header and description header. */ devc->buffer += devc->block_header_size; - len = devc->num_samples; + devc->num_bytes_current_block = devc->num_samples; } else { - sr_dbg("Requesting: %" PRIu64 " bytes.", devc->num_samples - devc->num_block_bytes); - len = sr_scpi_read_data(scpi, (char *)devc->buffer, devc->num_samples-devc->num_block_bytes); - if (len == -1) { - sr_err("Read error, aborting capture."); - std_session_send_df_frame_end(sdi); - sdi->driver->dev_acquisition_stop(sdi); - return TRUE; + sr_dbg("Requesting: %" PRIu64 " bytes for %" PRIu64 " samples.", devc->num_samples - devc->num_block_bytes,devc->num_samples); + if(devc->model->series->protocol == E11) { + len = siglent_read_wave_e11(sdi,ch,FALSE); + if (len < 0) { + sr_err("Read error, aborting capture."); + std_session_send_df_frame_end(sdi); + sdi->driver->dev_acquisition_stop(sdi); + return TRUE; + } + } + else { + len = sr_scpi_read_data(scpi, (char *)devc->buffer, devc->num_samples-devc->num_block_bytes); + if (len == -1) { + sr_err("Read error, aborting capture."); + std_session_send_df_frame_end(sdi); + sdi->driver->dev_acquisition_stop(sdi); + return TRUE; + } + devc->num_block_bytes += len; } - devc->num_block_read++; - devc->num_block_bytes += len; } - sr_dbg("Received block: %i, %d bytes.", devc->num_block_read, len); + sr_dbg("Received block: %i, %" PRIu64 " bytes.", devc->num_block_read, devc->num_bytes_current_block); if (ch->type == SR_CHANNEL_ANALOG) { float vdiv = devc->vdiv[ch->index]; float offset = devc->vert_offset[ch->index]; @@ -585,11 +918,11 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) float voltage, vdivlog; int digits; - data = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len); - g_array_append_vals(data, devc->buffer, len); + data = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), devc->num_bytes_current_block); + g_array_append_vals(data, devc->buffer, devc->num_bytes_current_block); 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; + for (i = 0; i < devc->num_bytes_current_block; i++) { + 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); } @@ -608,17 +941,23 @@ SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data) g_slist_free(analog.meaning->channels); g_array_free(data, TRUE); } + devc->num_block_read++; + devc->num_bytes_current_block = 0; len = 0; - if (devc->num_samples == (devc->num_block_bytes - SIGLENT_HEADER_SIZE)) { + uint64_t bytes_to_read = devc->num_samples - ((devc->model->series->protocol == E11) ? (devc->num_block_bytes) : (devc->num_block_bytes - SIGLENT_HEADER_SIZE)); + sr_dbg("Total samples: %" PRIu64 "; bytes already read %" PRIu64 "; bytes left to read: %" PRIu64 " bytes.",devc->num_samples, devc->num_block_bytes, bytes_to_read); + if (bytes_to_read <= 0) { sr_dbg("Transfer has been completed."); devc->num_header_bytes = 0; devc->num_block_bytes = 0; read_complete = TRUE; 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 +1080,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); @@ -876,6 +1215,39 @@ SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi) return SR_OK; } +static int siglent_parse_timebase(int read_result, char *sample_points_string, float* fvalue, float* samplerate_scope) +{ + if (read_result != SR_OK) { + return SR_ERR; + } + if (g_strstr_len(sample_points_string, -1, "Mpts") != NULL) { + sample_points_string[strlen(sample_points_string) - 4] = '\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 * 1000000; + } else if (g_strstr_len(sample_points_string, -1, "Kpts") != NULL) { + sample_points_string[strlen(sample_points_string) - 4] = '\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 * 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; + } + return SR_OK; +} + SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi) { struct dev_context *devc; @@ -894,30 +1266,10 @@ SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi) g_free(cmd); samplerate_scope = 0; fvalue = 0; - if (res != SR_OK) { - g_free(sample_points_string); - return SR_ERR; - } - if (g_strstr_len(sample_points_string, -1, "Mpts") != NULL) { - sample_points_string[strlen(sample_points_string) - 4] = '\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 * 1000000; - } else if (g_strstr_len(sample_points_string, -1, "Kpts") != NULL) { - sample_points_string[strlen(sample_points_string) - 4] = '\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 * 10000; - } else { - samplerate_scope = fvalue; - } + res = siglent_parse_timebase(res,sample_points_string,&fvalue,&samplerate_scope); g_free(sample_points_string); + if (res != SR_OK) + return SR_ERR; devc->memory_depth_analog = samplerate_scope; break; case ESERIES: @@ -933,10 +1285,33 @@ SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi) } g_free(cmd); break; + case E11: { + /* Get the timebase. */ + if (sr_scpi_get_double(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK) + return SR_ERR; + cmd = g_strdup_printf("SANU? C1"); + res = sr_scpi_get_string(sdi->conn, cmd, &sample_points_string); + if (devc->la_enabled) { + cmd = g_strdup_printf("SANU? D0"); + if (sr_scpi_get_float(sdi->conn, cmd, &fvalue) != SR_OK) + return SR_ERR; + devc->memory_depth_digital = (long)fvalue; + } + + g_free(cmd); + samplerate_scope = 0; + fvalue = 0; + res = siglent_parse_timebase(res,sample_points_string,&fvalue,&samplerate_scope); + g_free(sample_points_string); + if (res != SR_OK) + return SR_ERR; + devc->memory_depth_analog = samplerate_scope; + break; + } }; /* 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 89abaf640..ff684563e 100644 --- a/src/hardware/siglent-sds/protocol.h +++ b/src/hardware/siglent-sds/protocol.h @@ -49,6 +49,9 @@ enum protocol_version { SPO_MODEL, NON_SPO_MODEL, ESERIES, + // Refers to E11 Siglent Programming Guide : + // https://www.siglenteu.com/wp-content/uploads/dlm_uploads/2024/03/ProgrammingGuide_EN11F.pdf + E11, }; enum data_source { @@ -69,6 +72,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; }; @@ -120,7 +124,9 @@ struct dev_context { gboolean analog_channels[MAX_ANALOG_CHANNELS]; gboolean digital_channels[MAX_DIGITAL_CHANNELS]; gboolean la_enabled; - float timebase; + gboolean channels_switched; + double timebase; + int max_points; float attenuation[MAX_ANALOG_CHANNELS]; float vdiv[MAX_ANALOG_CHANNELS]; int vert_reference[MAX_ANALOG_CHANNELS]; @@ -143,6 +149,8 @@ struct dev_context { uint64_t num_header_bytes; /* Number of data blocks bytes already read. */ uint64_t num_block_bytes; + /* Number of data bytes already read in the current block. */ + uint64_t num_bytes_current_block; /* Number of data blocks read. */ int num_block_read; /* What to wait for in *_receive. */