diff --git a/Makefile.am b/Makefile.am index 280cf64d2..6fac3f968 100644 --- a/Makefile.am +++ b/Makefile.am @@ -226,6 +226,12 @@ src_libdrivers_tail_la_SOURCES = src/driver_list_stop.c src_libdrivers_la_SOURCES = src/drivers.c +if HW_AGILENT_54621D +src_libdrivers_la_SOURCES += \ + src/hardware/agilent-54621d/protocol.h \ + src/hardware/agilent-54621d/protocol.c \ + src/hardware/agilent-54621d/api.c +endif if HW_AGILENT_DMM src_libdrivers_la_SOURCES += \ src/hardware/agilent-dmm/protocol.h \ diff --git a/configure.ac b/configure.ac index 3ba6c8c5d..09aee65f0 100644 --- a/configure.ac +++ b/configure.ac @@ -290,6 +290,7 @@ m4_define([_SR_DRIVER], [ m4_define([SR_DRIVER], [_SR_DRIVER([$1], [$2], m4_expand([AS_TR_CPP([HW_$2])]), [$3])]) +SR_DRIVER([Agilent 54621D], [agilent-54621d]) SR_DRIVER([Agilent DMM], [agilent-dmm], [serial_comm]) SR_DRIVER([Appa 55II], [appa-55ii], [serial_comm]) SR_DRIVER([Arachnid Labs Re:load Pro], [arachnid-labs-re-load-pro], [serial_comm]) diff --git a/src/hardware/agilent-54621d/api.c b/src/hardware/agilent-54621d/api.c new file mode 100644 index 000000000..edc53512e --- /dev/null +++ b/src/hardware/agilent-54621d/api.c @@ -0,0 +1,927 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2022 Daniel Echt + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "protocol.h" +#include "scpi.h" +#include + +#define WAIT_FOR_CAPTURE_COMPLETE_RETRIES 100 +#define WAIT_FOR_CAPTURE_COMPLETE_DELAY (100*1000) + +static struct sr_dev_driver agilent_54621d_driver_info; + +static const char *manufacturers[] = { + "AGILENT TECHNOLOGIES", +}; + +static const uint32_t scanopts[] = { + SR_CONF_CONN, + SR_CONF_SERIALCOMM, +}; + +static const uint32_t drvopts[] = { + SR_CONF_OSCILLOSCOPE, + SR_CONF_LOGIC_ANALYZER, +}; + +enum { + CG_INVALID = -1, + CG_NONE, + CG_ANALOG, + CG_DIGITAL, +}; + +//Do not change order +static const char *data_sources[] = { + "Single", + "Memory", +}; + +static const uint64_t samplerates[] = { + SR_HZ(1), + SR_MHZ(200), + SR_HZ(1), +}; + +static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_scpi_hw_info *hw_info; + + sdi = NULL; + devc = NULL; + hw_info = NULL; + + if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) { + sr_info("Couldn't get IDN response."); + goto fail; + } + + if (std_str_idx_s(hw_info->manufacturer, ARRAY_AND_SIZE(manufacturers)) < 0) + goto fail; + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->vendor = g_strdup(hw_info->manufacturer); + sdi->model = g_strdup(hw_info->model); + sdi->version = g_strdup(hw_info->firmware_version); + sdi->serial_num = g_strdup(hw_info->serial_number); + sdi->driver = &agilent_54621d_driver_info; + sdi->inst_type = SR_INST_SCPI; + sdi->conn = scpi; + + sr_scpi_hw_info_free(hw_info); + hw_info = NULL; + + devc = g_malloc0(sizeof(struct dev_context)); + + sdi->priv = devc; + + if (agilent_54621d_init_device(sdi) != SR_OK) + goto fail; + + return sdi; + +fail: + sr_scpi_hw_info_free(hw_info); + sr_dev_inst_free(sdi); + g_free(devc); + + return NULL; +} + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + sr_info("Scanning for agilent 54621d"); + return sr_scpi_scan(di->context, options, probe_device); +} + +static void clear_helper(struct dev_context *devc) +{ + agilent_54621d_scope_state_free(devc->model_state); + g_free(devc->analog_groups); + g_free(devc->digital_groups); + g_free(devc->data); +} + +static int dev_clear(const struct sr_dev_driver *di) +{ + return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper); +} + +static int dev_open(struct sr_dev_inst *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; + } + + if ((ret = agilent_54621d_scope_state_get(sdi)) < 0) { + sr_err("Failed to get device config: %s.", sr_strerror(ret)); + return SR_ERR; + } + + return SR_OK; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + return sr_scpi_close(sdi->conn); +} + +static int check_channel_group(struct dev_context *devc, + const struct sr_channel_group *cg) +{ + const struct scope_config *model; + + model = devc->model_config; + + if (!cg) + return CG_NONE; + + if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) >= 0) + return CG_ANALOG; + + if (std_cg_idx(cg, devc->digital_groups, model->digital_pods) >= 0) + return CG_DIGITAL; + + sr_err("Invalid channel group specified."); + + return CG_INVALID; +} + +static int config_get(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + int cg_type, idx; + struct dev_context *devc; + const struct scope_config *model; + struct scope_state *state; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID) + return SR_ERR; + + model = devc->model_config; + state = devc->model_state; + + switch (key) { + case SR_CONF_NUM_HDIV: + *data = g_variant_new_int32(model->num_xdivs); + break; + case SR_CONF_TIMEBASE: + *data = g_variant_new("(tt)", (*model->timebases)[state->timebase][0], + (*model->timebases)[state->timebase][1]); + break; + case SR_CONF_NUM_VDIV: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type != CG_ANALOG) + return SR_ERR_NA; + if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) < 0) + return SR_ERR_ARG; + *data = g_variant_new_int32(model->num_ydivs); + break; + case SR_CONF_VDIV: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type != CG_ANALOG) + return SR_ERR_NA; + if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0) + return SR_ERR_ARG; + *data = g_variant_new("(tt)", + (*model->vdivs)[state->analog_channels[idx].vdiv][0], + (*model->vdivs)[state->analog_channels[idx].vdiv][1]); + break; + case SR_CONF_TRIGGER_SOURCE: + *data = g_variant_new_string((*model->trigger_sources)[state->trigger_source]); + break; + case SR_CONF_TRIGGER_SLOPE: + *data = g_variant_new_string((*model->trigger_slopes)[state->trigger_slope]); + break; + case SR_CONF_PEAK_DETECTION: + *data = g_variant_new_boolean(state->peak_detection); + break; + case SR_CONF_HORIZ_TRIGGERPOS: + *data = g_variant_new_double(state->horiz_triggerpos); + break; + case SR_CONF_ENABLED: + sr_info("get channel enabled"); + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type == CG_DIGITAL){ + sr_info("get digital channel enabled"); + if ((idx = std_cg_idx(cg, devc->digital_groups, model->digital_pods)) < 0) + return SR_ERR_ARG; + *data = g_variant_new_boolean(state->digital_pods[idx].state); + } + else if (cg_type == CG_ANALOG){ + sr_info("get analog channel enabled"); + if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0) + return SR_ERR_ARG; + *data = g_variant_new_boolean(state->analog_channels[idx].state); + } else { + return SR_ERR; + } + break; + case SR_CONF_COUPLING: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type != CG_ANALOG) + return SR_ERR_NA; + if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0) + return SR_ERR_ARG; + *data = g_variant_new_string((*model->coupling_options)[state->analog_channels[idx].coupling]); + break; + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(devc->sample_rate_limit); //state->sample_rate + break; + case SR_CONF_LOGIC_THRESHOLD: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type != CG_DIGITAL) + return SR_ERR_NA; + if (!model) + return SR_ERR_ARG; + if ((idx = std_cg_idx(cg, devc->digital_groups, model->digital_pods)) < 0) + return SR_ERR_ARG; + *data = g_variant_new_string((*model->logic_threshold)[state->digital_pods[idx].threshold]); + break; + case SR_CONF_LOGIC_THRESHOLD_CUSTOM: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type != CG_DIGITAL) + return SR_ERR_NA; + if (!model) + return SR_ERR_ARG; + if ((idx = std_cg_idx(cg, devc->digital_groups, model->digital_pods)) < 0) + return SR_ERR_ARG; + *data = g_variant_new_double(state->digital_pods[idx].user_threshold); + break; + case SR_CONF_LIMIT_SAMPLES: + *data = g_variant_new_uint64(devc->samples_limit); + break; + case SR_CONF_DATA_SOURCE: + if (devc->data_source == DATA_SOURCE_LIVE) + *data = g_variant_new_string("Single"); + else if (devc->data_source == DATA_SOURCE_MEMORY) + *data = g_variant_new_string("Memory"); + break; + //ToDo: Check if all options are implemented + default: + sr_err("could not find requested parameter: %d", key); + return SR_ERR_NA; + } + + return SR_OK; +} + + +static int config_set(uint32_t key, GVariant *data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + int ret, cg_type, idx, i, j; + char command[MAX_COMMAND_SIZE]; + char float_str[30]; + struct dev_context *devc; + const struct scope_config *model; + struct scope_state *state; + double tmp_d, tmp_d2; + gboolean update_sample_rate, tmp_bool; + int tmp_int; + const char *tmp_str; + + ret = SR_ERR; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID) + return SR_ERR; + + model = devc->model_config; + state = devc->model_state; + update_sample_rate = FALSE; + + switch (key) { + case SR_CONF_SAMPLERATE: + if(state->sample_rate < g_variant_get_uint64(data)){ + devc->sample_rate_limit = state->sample_rate; + ret = SR_OK; + } else { + devc->sample_rate_limit = g_variant_get_uint64(data); + ret = SR_OK; + } + break; + case SR_CONF_LIMIT_SAMPLES: + tmp_int = g_variant_get_uint64(data); + tmp_d = (double)tmp_int/devc->sample_rate_limit; //total time to be transmitted + tmp_d2 = 10*((float) (*model->timebases)[state->timebase][0] / (*model->timebases)[state->timebase][1]); //total time shown in display + if(tmp_d > tmp_d2){ //dont allow to transmit more data than shown in the main view. One could zoom out to increase the amount of data shown, but implementing that would probably be a bitch so this driver wont allow to download more data than shown in main view + devc->samples_limit = tmp_d2*devc->sample_rate_limit; + } else { + devc->samples_limit = g_variant_get_uint64(data); + } + ret = SR_OK; + break; + case SR_CONF_VDIV: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if ((idx = std_u64_tuple_idx(data, *model->vdivs, model->num_vdivs)) < 0) + return SR_ERR_ARG; + if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0) + return SR_ERR_ARG; + g_ascii_formatd(float_str, sizeof(float_str), "%E", + (float) (*model->vdivs)[idx][0] / (*model->vdivs)[idx][1]); + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_VERTICAL_SCALE], + j + 1, float_str); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->analog_channels[j].vdiv = idx; + ret = SR_OK; + break; + case SR_CONF_TIMEBASE: + if ((idx = std_u64_tuple_idx(data, *model->timebases, model->num_timebases)) < 0) + return SR_ERR_ARG; + g_ascii_formatd(float_str, sizeof(float_str), "%E", + (float) (*model->timebases)[idx][0] / (*model->timebases)[idx][1]); + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_TIMEBASE], + float_str); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->timebase = idx; + ret = SR_OK; + update_sample_rate = TRUE; + break; + case SR_CONF_HORIZ_TRIGGERPOS: + tmp_d = g_variant_get_double(data); + if (tmp_d < 0.0 || tmp_d > 1.0) + return SR_ERR; + tmp_d2 = -(tmp_d - 0.5) * + ((double) (*model->timebases)[state->timebase][0] / + (*model->timebases)[state->timebase][1]) + * model->num_xdivs; + g_ascii_formatd(float_str, sizeof(float_str), "%E", tmp_d2); + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_HORIZ_TRIGGERPOS], + float_str); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->horiz_triggerpos = tmp_d; + ret = SR_OK; + break; + case SR_CONF_TRIGGER_SOURCE: + if ((idx = std_str_idx(data, *model->trigger_sources, model->num_trigger_sources)) < 0) + return SR_ERR_ARG; + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SOURCE], + (*model->trigger_sources)[idx]); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->trigger_source = idx; + ret = SR_OK; + break; + case SR_CONF_TRIGGER_SLOPE: + if ((idx = std_str_idx(data, *model->trigger_slopes, model->num_trigger_slopes)) < 0) + return SR_ERR_ARG; + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SLOPE], + (*model->trigger_slopes)[idx]); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->trigger_slope = idx; + ret = SR_OK; + break; + case SR_CONF_PEAK_DETECTION: + tmp_bool = g_variant_get_boolean(data); + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_PEAK_DETECTION], + tmp_bool ? "AUTO" : "OFF"); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + /* Peak Detection automatically switches off High Resolution mode. */ + if (tmp_bool) { + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_HIGH_RESOLUTION], + "OFF"); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->high_resolution = FALSE; + } + state->peak_detection = tmp_bool; + ret = SR_OK; + break; + case SR_CONF_ENABLED: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type == CG_DIGITAL){ + if ((j = std_cg_idx(cg, devc->digital_groups, model->digital_pods)) < 0) + return SR_ERR_ARG; + //enable digital channel + } + else if (cg_type == CG_ANALOG){ + if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0) + return SR_ERR_ARG; + g_snprintf(command, sizeof(command), (*model->scpi_dialect)[SCPI_CMD_SET_ANALOG_CHAN_STATE], j+1, g_variant_get_boolean(data)); + if(sr_scpi_send(sdi->conn, command) != SR_OK || sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->analog_channels[j].state = g_variant_get_boolean(data); + update_sample_rate = TRUE; + ret = SR_OK; + //enable analog channel + } else { + return SR_ERR; + } + break; + case SR_CONF_COUPLING: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if ((idx = std_str_idx(data, *model->coupling_options, model->num_coupling_options)) < 0) + return SR_ERR_ARG; + if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0) + return SR_ERR_ARG; + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_COUPLING], + j + 1, (*model->coupling_options)[idx]); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->analog_channels[j].coupling = idx; + ret = SR_OK; + break; + case SR_CONF_LOGIC_THRESHOLD: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type != CG_DIGITAL) + return SR_ERR_NA; + if (!model) + return SR_ERR_ARG; + if ((idx = std_str_idx(data, *model->logic_threshold, model->num_logic_threshold)) < 0) + return SR_ERR_ARG; + if ((j = std_cg_idx(cg, devc->digital_groups, model->digital_pods)) < 0) + return SR_ERR_ARG; + /* Check if the threshold command is based on the POD or digital channel index. */ + if (model->logic_threshold_for_pod) + i = j + 1; + else + i = j * DIGITAL_CHANNELS_PER_POD; + g_snprintf(command, sizeof(command), + (*model->scpi_dialect)[SCPI_CMD_SET_DIG_POD_THRESHOLD], + i, (*model->logic_threshold)[idx]); + if (sr_scpi_send(sdi->conn, command) != SR_OK || + sr_scpi_get_opc(sdi->conn) != SR_OK) + return SR_ERR; + state->digital_pods[j].threshold = idx; + ret = SR_OK; + break; + case SR_CONF_LOGIC_THRESHOLD_CUSTOM: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (cg_type != CG_DIGITAL) + return SR_ERR_NA; + if (!model) + return SR_ERR_ARG; + if ((j = std_cg_idx(cg, devc->digital_groups, model->digital_pods)) < 0) + return SR_ERR_ARG; + //ToDo: implement logic + ret = SR_OK; + break; + case SR_CONF_DATA_SOURCE: + tmp_str = g_variant_get_string(data, NULL); + sr_dbg("Setting data source to: '%s'", tmp_str); + if(!strcmp(tmp_str, "Single")) + devc->data_source = DATA_SOURCE_LIVE; + else if(!strcmp(tmp_str, "Memory")) + devc->data_source = DATA_SOURCE_MEMORY; + else { + sr_err("Unknown data source: '%s'", tmp_str); + return SR_ERR; + } + ret = SR_OK; + break; + default: + ret = SR_ERR_NA; + break; + } + + if (ret == SR_OK && update_sample_rate) + ret = agilent_54621d_update_sample_rate(sdi); + + return ret; +} + +static int config_list(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + int ret; + + int cg_type = CG_NONE; + struct dev_context *devc = NULL; + const struct scope_config *model = NULL; + + if (sdi) { + devc = sdi->priv; + if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID) + return SR_ERR; + + model = devc->model_config; + } + + ret = SR_OK; + switch (key) { + case SR_CONF_SAMPLERATE: + *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates)); + break; + case SR_CONF_SCAN_OPTIONS: + *data = std_gvar_array_u32(ARRAY_AND_SIZE(scanopts)); + break; + case SR_CONF_DEVICE_OPTIONS: + if (!cg) { + if (model) + *data = std_gvar_array_u32(*model->devopts, model->num_devopts); + else + *data = std_gvar_array_u32(ARRAY_AND_SIZE(drvopts)); + } else if (cg_type == CG_ANALOG) { + *data = std_gvar_array_u32(*model->devopts_cg_analog, model->num_devopts_cg_analog); + } else if (cg_type == CG_DIGITAL) { + *data = std_gvar_array_u32(*model->devopts_cg_digital, model->num_devopts_cg_digital); + } else { + *data = std_gvar_array_u32(NULL, 0); + } + break; + case SR_CONF_COUPLING: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (!model) + return SR_ERR_ARG; + *data = g_variant_new_strv(*model->coupling_options, model->num_coupling_options); + break; + case SR_CONF_TRIGGER_SOURCE: + if (!model) + return SR_ERR_ARG; + *data = g_variant_new_strv(*model->trigger_sources, model->num_trigger_sources); + break; + case SR_CONF_TRIGGER_SLOPE: + if (!model) + return SR_ERR_ARG; + *data = g_variant_new_strv(*model->trigger_slopes, model->num_trigger_slopes); + break; + case SR_CONF_TIMEBASE: + if (!model) + return SR_ERR_ARG; + *data = std_gvar_tuple_array(*model->timebases, model->num_timebases); + break; + case SR_CONF_VDIV: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (!model) + return SR_ERR_ARG; + *data = std_gvar_tuple_array(*model->vdivs, model->num_vdivs); + break; + case SR_CONF_LOGIC_THRESHOLD: + if (!cg) + return SR_ERR_CHANNEL_GROUP; + if (!model) + return SR_ERR_ARG; + *data = g_variant_new_strv(*model->logic_threshold, model->num_logic_threshold); + break; + case SR_CONF_DATA_SOURCE: + *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources)); + break; + /* TODO Check if all relevant option are present here */ + default: + sr_info("Trying to query for config list for:"); + return SR_ERR_NA; + } + + return ret; +} + + +static int dev_acquisition_start(const struct sr_dev_inst *sdi) +{ + GSList *l; + struct sr_channel *ch; + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + struct scope_state *state; + const struct scope_config *model; + int tmp_int; + gboolean tmp_bool; + char command[MAX_COMMAND_SIZE]; + float xinc; + int points; + char *tmp_string; + float tmp_float; + struct analog_channel_transfer_info *encoding; + gboolean digital_added[MAX_DIGITAL_GROUP_COUNT]; + size_t group, pod_count; + + + scpi = sdi->conn; + devc = sdi->priv; + state = devc->model_state; + model = devc->model_config; + + devc->num_samples = 0; + devc->num_frames = 0; + + //reset to empty + for (group = 0; group < ARRAY_SIZE(digital_added); group++) + digital_added[group] = FALSE; + g_slist_free(devc->enabled_channels); + devc->enabled_channels = NULL; + + pod_count = 0; + for (l = sdi->channels; l; l = l->next) { + ch = l->data; + sr_dbg("initializing channel %s", ch->name); + if(ch->type == SR_CHANNEL_ANALOG) { + if(ch->enabled) + devc->enabled_channels = g_slist_append(devc->enabled_channels, ch); + //Enable/disable channel if neccessary + if(ch->enabled != state->analog_channels[ch->index].state) { + if( sr_scpi_send(scpi, (*model->scpi_dialect)[SCPI_CMD_SET_ANALOG_CHAN_STATE], ch->index + 1, ch->enabled ? "ON" : "OFF") != SR_OK || + sr_scpi_get_opc(scpi) != SR_OK) + return SR_ERR; + state->analog_channels[ch->index].state = ch->enabled; + } + } else if (ch->type == SR_CHANNEL_LOGIC) { + group = ch->index / DIGITAL_CHANNELS_PER_POD; + if(ch->enabled && !digital_added[group]){ //Only add a single channel per pod + devc->enabled_channels = g_slist_append(devc->enabled_channels, ch); + digital_added[group] = TRUE; + pod_count++; + } + //Enable/disable channel if neccessary + if(ch->enabled != state->digital_channels[ch->index]) { + if( sr_scpi_send(scpi, (*model->scpi_dialect)[SCPI_CMD_SET_DIG_CHAN_STATE], ch->index, ch->enabled ? "ON" : "OFF") != SR_OK || + sr_scpi_get_opc(scpi) != SR_OK) + return SR_ERR; + state->digital_channels[ch->index] = ch->enabled; + } + } + } + devc->current_channel = devc->enabled_channels; + devc->pod_count = pod_count; + + + if(devc->data_source == DATA_SOURCE_LIVE){ + sr_scpi_get_bool(scpi, ":TER?", &tmp_bool); + sr_scpi_send(scpi, ":SING"); + sr_scpi_get_bool(scpi, ":TER?", &tmp_bool); + while(!tmp_bool){ + g_usleep(WAIT_FOR_CAPTURE_COMPLETE_DELAY); + sr_scpi_get_bool(scpi, ":TER?", &tmp_bool); + } + } else if (devc->data_source == DATA_SOURCE_MEMORY) { + ch = (struct sr_channel *)devc->current_channel->data; + g_snprintf(command, sizeof(command), ":WAV:SOUR %s", ch->name); + sr_scpi_send(scpi, command); + sr_scpi_get_int(scpi, ":WAV:POIN? MAX", &tmp_int); + if(tmp_int <= 0){ + sr_err("No waveform in Memory"); + return SR_OK; + } + } else { + sr_err("Unknown datasource"); + return SR_ERR; + } + + sr_dbg("determine steps to download data"); + + /* + * Downloading data is done in a quite manual way to speed up downloading. The device only allows downloading upto 2k points from the view-buffer, or the complete captured waveform (~1m points) + * The logic is, that if we want less than the complete waveform we can switch to window view, calculate the settings to have the window show exactly 2k points of the wv, and then transfer these 2k points. + * Then we can move the window delay and download the next 2k points. We can repeat this until we have the desired amount of points. + * This is also how the scope transfers the full waveform, however transfering the full wavefrom cannot be interrupted, so the manual approach is better + */ + + //Reset parameters + devc->num_samples = 0; + devc->num_blocks_downloaded = 0; + //devc->buffer = g_malloc0(sizeof(char) * devc->samples_limit); + devc->headerSent = FALSE; + + //set waveform source channel to the first channel to be downloaded + ch = (struct sr_channel *)devc->enabled_channels->data; + if(ch->type == SR_CHANNEL_LOGIC){ + group = ch->index/DIGITAL_CHANNELS_PER_POD+1; + g_snprintf(command, sizeof(command), ":WAV:SOUR POD%ld", group); + } else { + g_snprintf(command, sizeof(command), ":WAV:SOUR %s", ch->name); + } + sr_scpi_send(scpi, command); + + //get required data for calculations + sr_scpi_send(scpi, ":TIM:MODE MAIN;:WAV:POIN MAX"); + sr_scpi_send(scpi, ":WAV:UNS 0"); + g_usleep(300000); //Apparently the device needs a little time to do the wav:sour and tim:mode setup before requesting wav:poin? max. waiting 30ms should be enough + if(sr_scpi_get_int(scpi, ":WAV:POIN? MAX", &points) != SR_OK){ + sr_err("Couldn't get max Points"); + return SR_ERR; + } + if(sr_scpi_get_float(scpi, ":WAV:XINC?", &xinc) != SR_OK){ //ToDo: is currently unneccessarysince the value needs to be updated after zooming + sr_err("Couldn't get x inc"); + return SR_ERR; + } + if(sr_scpi_get_string(scpi, ":TIM:REF?", &tmp_string) != SR_OK){ + sr_err("Couldn't get time ref"); + return SR_ERR; + } + if(sr_scpi_get_float(scpi, ":TIM:SCAL?", &tmp_float) != SR_OK){ + sr_err("Couldn't get time scale"); + return SR_ERR; + } + + + //set main timebase to show entire waveform. This is neccessary since the the windowed view can not be delayed outside the main view + //g_snprintf(command, sizeof(command), ":TIM:RANG %f", (1.0/((struct scope_state*)devc->model_state)->sample_rate)*points); + //sr_scpi_send(scpi, command); + sr_dbg("time ref is %s", tmp_string); + if(!strcmp(tmp_string, "LEFT")){ + sr_dbg("left"); + devc->refPos = -1; + } else if(!strcmp(tmp_string, "CENT")){ + sr_dbg("cent"); + devc->refPos = -5; + } else { + sr_dbg("right"); + devc->refPos = -9; + } + devc->timebaseLbound = tmp_float*devc->refPos; + devc->block_deltaT = 2000.0/((int)devc->sample_rate_limit); + + devc->num_block_to_download = ceil(devc->samples_limit/2000); //ToDo: This needs some sanitization to make sure only available points are being downloaded + devc->trigger_at_sample = (uint64_t)((-devc->timebaseLbound)*(devc->sample_rate_limit)); + sr_dbg("Sample rate is: %ld", state->sample_rate); + //Setup window view for first block download + g_snprintf(command, sizeof(command), ":TIM:MODE MAIN;:TIM:RANG %f;:TIM:DEL %f", devc->block_deltaT, devc->timebaseLbound-devc->refPos*devc->block_deltaT*0.1); + sr_scpi_send(scpi, command); + + sr_dbg("Download %d packets with a width of %f. Maxpoints are %d. Lbound is %f. Trigger at sample %ld", devc->num_block_to_download, devc->block_deltaT, points, devc->timebaseLbound, devc->trigger_at_sample); + + sr_dbg("beginning data download"); + //make final setup before download + sr_scpi_send(scpi, ":WAV:FORM BYTE;BYT MSBF;UNS 0;POIN 2000"); //ToDo: when downloading high resolution data format needs to be word. + + //get header data and store it in channel->priv so the receive_data function can submit it. This only needs to be done for analog channels + while(devc->current_channel){ + ch = (struct sr_channel *)devc->current_channel->data; + if(ch->type == SR_CHANNEL_ANALOG){ + ch->priv = (void *)g_malloc0(sizeof(struct analog_channel_transfer_info)); + encoding = (struct analog_channel_transfer_info *)ch->priv; + sr_scpi_send(scpi, ":WAV:SOUR %s", ch->name); + + //get signed + sr_scpi_get_bool(scpi, ":WAV:UNS?", &tmp_bool); + encoding->is_unsigned = tmp_bool; + + //get transmission format. Byte is 1 byte per point, Word is 2 Byte per point + sr_scpi_get_string(scpi, ":WAV:FORM?", &tmp_string); + if(!strcmp("BYTE", tmp_string)) + encoding->is_eightbit = TRUE; + else if(!strcmp("WORD", tmp_string)) + encoding->is_eightbit = FALSE; + else{ + sr_err("unknown transmission format"); + } + + sr_scpi_get_float(scpi, ":WAV:YINC?", &tmp_float); + encoding->yIncrement = tmp_float; + + sr_scpi_get_float(scpi, ":WAV:YOR?", &tmp_float); + encoding->yOrigin = tmp_float; + + sr_scpi_get_int(scpi, ":WAV:YREF?", &tmp_int); + sr_dbg("yRef is: %d", tmp_int); + encoding->yReference = tmp_int; + + //ToDo: read data and write to encoding. dont forget to set and reset wav:source + sr_dbg("Reading data complete"); + } + devc->current_channel = devc->current_channel->next; + }; + devc->current_channel = devc->enabled_channels; + ch = (struct sr_channel *)devc->current_channel->data; + devc->failcount = 0; + devc->trigger_sent = FALSE; + + + //set waveform source channel to the first channel to be downloaded + ch = (struct sr_channel *)devc->enabled_channels->data; + if(ch->type == SR_CHANNEL_LOGIC){ + group = ch->index/DIGITAL_CHANNELS_PER_POD+1; + g_snprintf(command, sizeof(command), ":WAV:SOUR POD%ld", group); + } else { + g_snprintf(command, sizeof(command), ":WAV:SOUR %s", ch->name); + } + sr_scpi_send(scpi, command); + + //register callback + sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 100, agilent_54621d_receive_data, (void *)sdi); + + + std_session_send_df_header(sdi); + + //std_session_send_df_header(sdi); + std_session_send_df_frame_begin(sdi); + + //request data from instrument + sr_scpi_send(scpi, ":SYST:DSP \"Reading Block 1/%d\"", devc->num_block_to_download); + sr_scpi_send(scpi, ":WAV:DATA?"); + + + + return SR_OK; + +} + +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + const struct scope_config *model; + struct scope_state *state; + float timebase; + struct sr_channel *ch; + + devc = sdi->priv; + model = devc->model_config; + state = devc->model_state; + + std_session_send_df_end(sdi); + + timebase = (float) (*model->timebases)[state->timebase][0] / (*model->timebases)[state->timebase][1]; + + while(devc->current_channel){ + ch = (struct sr_channel *)devc->current_channel->data; + if(ch->type == SR_CHANNEL_ANALOG) + g_free((struct analog_channel_transfer_info *)ch->priv); + devc->current_channel=devc->current_channel->next; + } + + g_slist_free(devc->enabled_channels); + devc->enabled_channels = NULL; + scpi = sdi->conn; + sr_scpi_source_remove(sdi->session, scpi); + sr_scpi_send(scpi, ":SYST:DSP \"\""); + sr_scpi_send(scpi, ":TIM:SCAL %f; :TIM:DEL 0", timebase); + sr_scpi_send(scpi, ":TIM:MODE MAIN"); + return SR_OK; +} + +static struct sr_dev_driver agilent_54621d_driver_info = { + .name = "agilent-54621d", + .longname = "Agilent 54621D", + .api_version = 1, + .init = std_init, + .cleanup = std_cleanup, + .scan = scan, + .dev_list = std_dev_list, + .dev_clear = dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .dev_open = dev_open, + .dev_close = dev_close, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .context = NULL, +}; +SR_REGISTER_DEV_DRIVER(agilent_54621d_driver_info); diff --git a/src/hardware/agilent-54621d/protocol.c b/src/hardware/agilent-54621d/protocol.c new file mode 100644 index 000000000..4c7f6edaa --- /dev/null +++ b/src/hardware/agilent-54621d/protocol.c @@ -0,0 +1,1032 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2022 Daniel Echt + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "protocol.h" +#include "scpi.h" + +SR_PRIV void agilent_54621d_queue_logic_data(struct dev_context *devc, + size_t group, GByteArray *pod_data); +SR_PRIV void agilent_54621d_send_logic_packet(struct sr_dev_inst *sdi, + struct dev_context *devc); +SR_PRIV void agilent_54621d_cleanup_logic_data(struct dev_context *devc); + +static struct scope_state *scope_state_new(const struct scope_config *config); + + +static int analog_channel_state_get(struct sr_dev_inst *sdi, const struct scope_config *config, struct scope_state *state); +static int digital_channel_state_get(struct sr_dev_inst *sdi, const struct scope_config *config, struct scope_state *state); +static int array_float_get(gchar *value, const uint64_t array[][2], int array_len, unsigned int *result); +static void scope_state_dump(const struct scope_config *config, struct scope_state *state); +static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi, const char *command, const char *(*array)[], unsigned int n, int *result); + +#define MAX_COMMAND_SIZE 128 +#define LOGIC_GET_THRESHOLD_SETTING "USER" //Threshold Setting that should be reported by the driver. Has to be included in logic_threshold. Has to be done in that hacky way since the device does only report threshold voltage level, yet not threshold setting + +#define WAIT_FOR_CAPTURE_COMPLETE_RETRIES 100 +#define WAIT_FOR_CAPTURE_COMPLETE_DELAY (100*1000) + +static const char *agilent_scpi_dialect[] = { + [SCPI_CMD_GET_DIG_DATA] = ":FORM UINT,8;:POD%d:DATA?", + [SCPI_CMD_GET_TIMEBASE] = ":TIM:SCAL?", //OK + [SCPI_CMD_SET_TIMEBASE] = ":TIM:SCAL %s", //OK + [SCPI_CMD_GET_COUPLING] = ":CHAN%d:COUP?", //OK + [SCPI_CMD_SET_COUPLING] = ":CHAN%d:COUP %s", //OK + [SCPI_CMD_GET_SAMPLE_RATE] = ":ACQ:SRAT?", //OK + [SCPI_CMD_GET_ANALOG_DATA] = ":FORM:BORD %s;" \ + ":FORM REAL,32;:CHAN%d:DATA?", + [SCPI_CMD_GET_VERTICAL_SCALE] = ":CHAN%d:SCAL?", //OK + [SCPI_CMD_SET_VERTICAL_SCALE] = ":CHAN%d:SCAL %s", //OK + [SCPI_CMD_GET_DIG_POD_STATE] = ":POD%d:DISP?", //Fixed + [SCPI_CMD_SET_DIG_POD_STATE] = ":POD%d:DISP %d", //Fixed + [SCPI_CMD_GET_TRIGGER_SOURCE] = ":TRIG:SOUR?", //Fixed + [SCPI_CMD_SET_TRIGGER_SOURCE] = ":TRIG:SOUR %s", //Fixed + [SCPI_CMD_GET_TRIGGER_SLOPE] = ":TRIG:SLOP?", //Fixed + [SCPI_CMD_SET_TRIGGER_SLOPE] = ":TRIG:MODE EDGE;:TRIG:SLOP %s", //Fixed + [SCPI_CMD_GET_TRIGGER_PATTERN] = ":TRIG:A:PATT:SOUR?", + [SCPI_CMD_SET_TRIGGER_PATTERN] = ":TRIG:A:TYPE LOGIC;" \ + ":TRIG:A:PATT:FUNC AND;" \ + ":TRIG:A:PATT:COND \"TRUE\";" \ + ":TRIG:A:PATT:MODE OFF;" \ + ":TRIG:A:PATT:SOUR \"%s\"", + [SCPI_CMD_GET_HIGH_RESOLUTION] = ":ACQ:HRES?", + [SCPI_CMD_SET_HIGH_RESOLUTION] = ":ACQ:HRES %s", + [SCPI_CMD_GET_PEAK_DETECTION] = ":ACQ:TYPE?", //Fixed + [SCPI_CMD_SET_PEAK_DETECTION] = ":ACQ:TYPE PEAK", //Fixed + [SCPI_CMD_GET_DIG_CHAN_STATE] = ":DIG%d:DISP?", //Fixed + [SCPI_CMD_SET_DIG_CHAN_STATE] = ":DIG%d:DISP %s", //Fixed + [SCPI_CMD_GET_VERTICAL_OFFSET] = ":CHAN%d:OFFS?", //Fixed + [SCPI_CMD_GET_HORIZ_TRIGGERPOS] = ":TIM:POS?", //OK + [SCPI_CMD_SET_HORIZ_TRIGGERPOS] = ":TIM:POS %s", //OK + [SCPI_CMD_GET_ANALOG_CHAN_STATE] = ":CHAN%d:DISP?", //Fixed + [SCPI_CMD_SET_ANALOG_CHAN_STATE] = ":CHAN%d:DISP %s", //Fixed + [SCPI_CMD_GET_PROBE_UNIT] = ":CHAN%d:UNIT?", //Fixed + [SCPI_CMD_GET_DIG_POD_THRESHOLD] = ":POD%d:THR?", //OK + [SCPI_CMD_SET_DIG_POD_THRESHOLD] = ":POD%d:THR %s", //OK +}; + +static const uint32_t devopts[] = { + SR_CONF_OSCILLOSCOPE, + SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_NUM_HDIV | SR_CONF_GET, + SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET, + 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_TRIGGER_PATTERN | SR_CONF_GET | SR_CONF_SET, //ToDo: Implement pattern trigger + SR_CONF_PEAK_DETECTION | SR_CONF_GET | SR_CONF_SET, +// SR_CONF_AVERAGING | SR_CONF_GET | SR_CONF_SET, + SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, //The device doesn't actually support limiting samples, but will allways capture the maximum available amout of samples. However the driver can selectively transfere a subset of samples inorder to reduce transfer times. +}; + +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, + SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, + //SR_CONF_PROBE_FACTOR | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +}; + +static const uint32_t devopts_cg_digital[] = { + SR_CONF_LOGIC_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_LOGIC_THRESHOLD_CUSTOM | SR_CONF_GET | SR_CONF_SET, + SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, +}; + +static const char *coupling_options[] = { + "AC", + "DC", + "GND", + //Agilent 5464x Scopes also allow 50Ohm Termination, however this is configured using the termination command. If someone wants to implement support for that you need to figure out the best way to do that +}; + +static const char *scope_trigger_slopes[] = { + "POS", + "NEG", +}; + +static const char *logic_threshold[] = { + "USER", + "TTL", + "ECL", + "CMOS", +}; + +static const char *trigger_sources[] = { + "CHAN1", "CHAN2", + "LINE", "EXT", "NONE", + "DIG0", "DIG1", "DIG2", "DIG3", "DIG4", "DIG5", "DIG6", "DIG7", "DIG8", "DIG9", "DIG10", "DIG11", "DIG12", "DIG13", "DIG14", "DIG15", +}; + +/* This is not currently used. Does sigrok allow setting trigger mode?*/ +static const char *trigger_mode[] = { + "EDGE", + "GLIT", + "PATT", + "CAN", + "DUR", + "IIC", + "LIN", + "SEQ", + "SPI", + "TV", + "USB", +}; + +static const uint64_t scope_timebases[][2] = { + /* nanoseconds */ + { 5, 1000000000 }, + { 10, 1000000000 }, + { 20, 1000000000 }, + { 50, 1000000000 }, + { 100, 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] = { + /* 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 }, +}; + +static const char *scope_analog_channel_names[] = { + "CHAN1", "CHAN2", +}; + +static const char *scope_digital_channel_names[] = { + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", +}; + +static struct scope_config scope_models[] = { + { + /* Agilent 54621D/54622D Models only differ in Bandwidth; everything else should be the same*/ + .name = {"54621D", "54622D", NULL}, + .analog_channels = 2, + .digital_channels = 16, + + .analog_names = &scope_analog_channel_names, + .digital_names = &scope_digital_channel_names, + + .devopts = &devopts, + .num_devopts = ARRAY_SIZE(devopts), + + .devopts_cg_analog = &devopts_cg_analog, + .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog), + + .devopts_cg_digital = &devopts_cg_digital, + .num_devopts_cg_digital = ARRAY_SIZE(devopts_cg_digital), + + .digital_pods = 2, + + .coupling_options = &coupling_options, + .num_coupling_options = ARRAY_SIZE(coupling_options), + + .logic_threshold = &logic_threshold, + .num_logic_threshold = ARRAY_SIZE(logic_threshold), + .logic_threshold_for_pod = TRUE, + + .trigger_sources = &trigger_sources, + .num_trigger_sources = ARRAY_SIZE(trigger_sources), + + .trigger_slopes = &scope_trigger_slopes, + .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes), + + .timebases = &scope_timebases, + .num_timebases = ARRAY_SIZE(scope_timebases), + + .vdivs = &vdivs, + .num_vdivs = ARRAY_SIZE(vdivs), + + .num_xdivs = 10, + .num_ydivs = 8, + + .scpi_dialect = &agilent_scpi_dialect, + }, + //ToDo: Implement other scope models +}; + +SR_PRIV int agilent_54621d_update_sample_rate(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct scope_state *state; + const struct scope_config *config; + int tmp_int; + + devc = sdi->priv; + config = devc->model_config; + state = devc->model_state; + + if (sr_scpi_get_int(sdi->conn, + (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE], + &tmp_int) != SR_OK) + return SR_ERR; + + state->sample_rate = tmp_int; + + if(devc->sample_rate_limit > (uint64_t)tmp_int) + devc->sample_rate_limit = tmp_int; + + return SR_OK; +} + +SR_PRIV int agilent_54621d_init_device(struct sr_dev_inst *sdi) +{ + int model_index; + unsigned int i, j, group; + struct sr_channel *ch; + struct dev_context *devc; + const char *cg_name; + int ret; + + devc = sdi->priv; + model_index = -1; + + /* find model */ + for(i = 0; i < ARRAY_SIZE(scope_models); i++) { + for(j = 0; scope_models[i].name[j]; j++) { + if(!strcmp(sdi->model, scope_models[i].name[j])) { + model_index = i; + } + } + if(model_index != -1) + break; + } + + if(model_index == -1){ + sr_dbg("Unsupported device."); + return SR_ERR_NA; + } + + devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) *scope_models[model_index].analog_channels); + devc->digital_groups = g_malloc0(sizeof(struct sr_channel_group*) *scope_models[model_index].digital_pods); + if(!devc->analog_groups || !devc->digital_groups){ + g_free(devc->analog_groups); + g_free(devc->digital_groups); + return SR_ERR_MALLOC; + } + + /* Add analog channels. */ + for (i = 0; i < scope_models[model_index].analog_channels; i++) { + ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, + (*scope_models[model_index].analog_names)[i]); + + cg_name = (*scope_models[model_index].analog_names)[i]; + devc->analog_groups[i] = sr_channel_group_new(sdi, cg_name, NULL); + devc->analog_groups[i]->channels = g_slist_append(NULL, ch); + } + + /* Add digital channel groups. */ + ret = SR_OK; + for (i = 0; i < scope_models[model_index].digital_pods; i++) { + devc->digital_groups[i] = sr_channel_group_new(sdi, NULL, NULL); + if (!devc->digital_groups[i]) { + ret = SR_ERR_MALLOC; + break; + } + devc->digital_groups[i]->name = g_strdup_printf("POD%d", i + 1); + } + if (ret != SR_OK) + return ret; + + /* Add digital channels. */ + for (i = 0; i < scope_models[model_index].digital_channels; i++) { + ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, + (*scope_models[model_index].digital_names)[i]); + + group = i / DIGITAL_CHANNELS_PER_POD; + devc->digital_groups[group]->channels = g_slist_append( + devc->digital_groups[group]->channels, ch); + } + + devc->model_config = &scope_models[model_index]; + devc->samples_limit = 2000; + devc->frame_limit = 0; + devc->data_source = DATA_SOURCE_LIVE; + devc->data = g_try_malloc0(2000*sizeof(float)); + + if(!devc->data) + return SR_ERR_MALLOC; + + devc->sample_rate_limit = SR_MHZ(200); + + if (!(devc->model_state = scope_state_new(devc->model_config))) + return SR_ERR_MALLOC; + + + return SR_OK; +} + +static struct scope_state *scope_state_new(const struct scope_config *config) { + struct scope_state *state; + + state = g_malloc0(sizeof(struct scope_state)); + state->analog_channels = g_malloc0_n(config->analog_channels, sizeof(struct analog_channel_state)); + state->digital_channels = g_malloc0_n(config->digital_channels, sizeof(gboolean) * MAX_DIGITAL_CHANNEL_COUNT); + state->digital_pods = g_malloc0_n(config->digital_pods, sizeof(struct digital_pod_state)); + + return state; +} + +SR_PRIV void agilent_54621d_scope_state_free(struct scope_state *state) +{ + g_free(state->analog_channels); + g_free(state->digital_channels); + g_free(state->digital_pods); + g_free(state); +} + +SR_PRIV int agilent_54621d_scope_state_get(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct scope_state *state; + const struct scope_config *config; + float tmp_float; + unsigned int i; + char *tmp_str; + + devc = sdi->priv; + config = devc->model_config; + state = devc->model_state; + + sr_info("Fetching scope state"); + + //get analog channel state + if (analog_channel_state_get(sdi, config, state) != SR_OK) + return SR_ERR; + + //get digital channel state + if (digital_channel_state_get(sdi, config, state) != SR_OK) + return SR_ERR; + + //get timebase + if (sr_scpi_get_string(sdi->conn, + (*config->scpi_dialect)[SCPI_CMD_GET_TIMEBASE], + &tmp_str) != SR_OK) + return SR_ERR; + + if (array_float_get(tmp_str, ARRAY_AND_SIZE(scope_timebases), &i) != SR_OK) { + g_free(tmp_str); + sr_err("Could not determine array index for time base."); + return SR_ERR; + } + g_free(tmp_str); + + state->timebase = i; + + //get trigger horizontal position + if(sr_scpi_get_float(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_HORIZ_TRIGGERPOS], &tmp_float) != SR_OK) + return SR_ERR; + + state->horiz_triggerpos = tmp_float / + (((double) (*config->timebases)[state->timebase][0] / + (*config->timebases)[state->timebase][1]) * config->num_xdivs); + state->horiz_triggerpos -= 0.5; + state->horiz_triggerpos *= -1; + //ToDo: This might be dependent on time ref setting + + if (scope_state_get_array_option(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SOURCE], config->trigger_sources, config->num_trigger_sources, &state->trigger_source) != SR_OK) + return SR_ERR; + + if (scope_state_get_array_option(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE], config->trigger_slopes, config->num_trigger_slopes, &state->trigger_slope) != SR_OK) + return SR_ERR; + + //ToDo: get trigger pattern + //documentation for reading the trigger pattern is a little wonky so I need to test how this is done + strncpy(state->trigger_pattern, "00000000000000000000", MAX_ANALOG_CHANNEL_COUNT + MAX_DIGITAL_CHANNEL_COUNT+1); + + + //ToDo: get current resolution + //Default resolution is 8bit. aquiring at 8 bit also can increase transfer speed, since only a byte of data per point has to be transmitted + //Resolution is a function of sweep speed and number of averages + //Resolution > 8bit if acq mode == avg && (timebase >= 5us/div || num_avg>1) + state->high_resolution = FALSE; + + + //get peak detection + if (sr_scpi_get_string(sdi->conn, + (*config->scpi_dialect)[SCPI_CMD_GET_PEAK_DETECTION], + &tmp_str) != SR_OK) + return SR_ERR; + if (!strcmp("PEAK", tmp_str)) + state->peak_detection = TRUE; + else + state->peak_detection = FALSE; + g_free(tmp_str); + + if(agilent_54621d_update_sample_rate(sdi) != SR_OK) + return SR_ERR; + + sr_info("Fetching finished."); + + scope_state_dump(config, state); + + return SR_OK; +} + +static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi, const char *command, const char *(*array)[], unsigned int n, int *result) +{ + char *tmp; + int idx; + + if (sr_scpi_get_string(scpi, command, &tmp) != SR_OK) + return SR_ERR; + + if ((idx = std_str_idx_s(tmp, *array, n)) < 0) { + g_free(tmp); + return SR_ERR_ARG; + } + + *result = idx; + + g_free(tmp); + + return SR_OK; +} + + +/* +*This function is a helper function to output the scope configuration to the info log +* +* +* +*/ +static void scope_state_dump(const struct scope_config *config, struct scope_state *state) +{ + unsigned int i; + char *tmp; + + for (i = 0; i < config->analog_channels; i++) { + tmp = sr_voltage_string((*config->vdivs)[state->analog_channels[i].vdiv][0], + (*config->vdivs)[state->analog_channels[i].vdiv][1]); + sr_info("State of analog channel %d -> %s : %s (coupling) %s (vdiv) %2.2e (offset)", + i + 1, state->analog_channels[i].state ? "On" : "Off", + (*config->coupling_options)[state->analog_channels[i].coupling], + tmp, state->analog_channels[i].vertical_offset); + } + + for (i = 0; i < config->digital_channels; i++) { + sr_info("State of digital channel %d -> %s", i, + state->digital_channels[i] ? "On" : "Off"); + } + + for (i = 0; i < config->digital_pods; i++) { + if (!strncmp("USER", (*config->logic_threshold)[state->digital_pods[i].threshold], 4) || + !strcmp("MAN", (*config->logic_threshold)[state->digital_pods[i].threshold])) + sr_info("State of digital POD %d -> %s : %E (threshold)", i + 1, + state->digital_pods[i].state ? "On" : "Off", + state->digital_pods[i].user_threshold); + else + sr_info("State of digital POD %d -> %s : %s (threshold)", i + 1, + state->digital_pods[i].state ? "On" : "Off", + (*config->logic_threshold)[state->digital_pods[i].threshold]); + } + + tmp = sr_period_string((*config->timebases)[state->timebase][0], + (*config->timebases)[state->timebase][1]); + sr_info("Current timebase: %s", tmp); + g_free(tmp); + + tmp = sr_samplerate_string(state->sample_rate); + sr_info("Current samplerate: %s", tmp); + g_free(tmp); + + if (!strcmp("PATT", (*config->trigger_sources)[state->trigger_source])) + sr_info("Current trigger: %s (pattern), %.2f (offset)", + state->trigger_pattern, + state->horiz_triggerpos); + else // Edge (slope) trigger + sr_info("Current trigger: %s (source), %s (slope) %.2f (offset)", + (*config->trigger_sources)[state->trigger_source], + (*config->trigger_slopes)[state->trigger_slope], + state->horiz_triggerpos); +} + +/** + * This function takes a value of the form "2.000E-03" and returns the index + * of an array where a matching pair was found. + * + * @param value The string to be parsed. + * @param array The array of s/f pairs. + * @param array_len The number of pairs in the array. + * @param result The index at which a matching pair was found. + * + * @return SR_ERR on any parsing error, SR_OK otherwise. + */ +static int array_float_get(gchar *value, const uint64_t array[][2], int array_len, unsigned int *result) +{ + struct sr_rational rval; + struct sr_rational aval; + + if (sr_parse_rational(value, &rval) != SR_OK) + return SR_ERR; + + for (int i = 0; i < array_len; i++) { + sr_rational_set(&aval, array[i][0], array[i][1]); + if (sr_rational_eq(&rval, &aval)) { + *result = i; + return SR_OK; + } + } + + return SR_ERR; +} + +static struct sr_channel *get_channel_by_index_and_type(GSList *channel_lhead, + int index, int type) +{ + while (channel_lhead) { + struct sr_channel *ch = channel_lhead->data; + if (ch->index == index && ch->type == type) + return ch; + + channel_lhead = channel_lhead->next; + } + + return 0; +} + +static int analog_channel_state_get(struct sr_dev_inst *sdi, const struct scope_config *config, struct scope_state *state) +{ + unsigned int i, j; + char command[MAX_COMMAND_SIZE]; + char *tmp_str; + struct sr_channel *ch; + struct sr_scpi_dev_inst *scpi = sdi->conn; + + for(i = 0; i < config->analog_channels; i++){ + //get channel enabled (visible) + g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_ANALOG_CHAN_STATE], i+1); + + if(sr_scpi_get_bool(scpi, command, &state->analog_channels[i].state) != SR_OK) + return SR_ERR; + + ch = get_channel_by_index_and_type(sdi->channels, i, SR_CHANNEL_ANALOG); + if(ch) + ch->enabled = state->analog_channels[i].state; + + //get vertical scale (v/div) + g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_SCALE], i+1); + + if(sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK) + return SR_ERR; + + if(array_float_get(tmp_str, *(config->vdivs), config->num_vdivs, &j) != SR_OK){ + g_free(tmp_str); + sr_err("could not determine array index for vertical div scale."); + return SR_ERR; + } + + g_free(tmp_str); + state->analog_channels[i].vdiv = j; + + //Get vertical offset + g_snprintf(command, sizeof(command), + (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_OFFSET], + i + 1); + + if (sr_scpi_get_float(scpi, command, + &state->analog_channels[i].vertical_offset) != SR_OK) + return SR_ERR; + + //get coupling + g_snprintf(command, sizeof(command), + (*config->scpi_dialect)[SCPI_CMD_GET_COUPLING], + i + 1); + + if (scope_state_get_array_option(scpi, command, config->coupling_options, + config->num_coupling_options, + &state->analog_channels[i].coupling) != SR_OK) + return SR_ERR; + + //get Unit (Amp/Volt) + g_snprintf(command, sizeof(command), + (*config->scpi_dialect)[SCPI_CMD_GET_PROBE_UNIT], + i + 1); + + if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK) + return SR_ERR; + + if (tmp_str[0] == 'A') + state->analog_channels[i].probe_unit = 'A'; + else + state->analog_channels[i].probe_unit = 'V'; + g_free(tmp_str); + } + + return SR_OK; +} + +static int digital_channel_state_get(struct sr_dev_inst *sdi, const struct scope_config *config, struct scope_state *state) +{ + unsigned int i, user_index; + char command[MAX_COMMAND_SIZE]; + struct sr_channel *ch; + struct sr_scpi_dev_inst *scpi = sdi->conn; + + //get enabled channels + for (i = 0; i < config->digital_channels; i++) { + g_snprintf(command, sizeof(command), + (*config->scpi_dialect)[SCPI_CMD_GET_DIG_CHAN_STATE], + i); + + if (sr_scpi_get_bool(scpi, command, + &state->digital_channels[i]) != SR_OK) + return SR_ERR; + + ch = get_channel_by_index_and_type(sdi->channels, i, SR_CHANNEL_LOGIC); + if (ch) + ch->enabled = state->digital_channels[i]; + } + + for (user_index = 0; user_index < ARRAY_SIZE(logic_threshold); user_index++){ + if(!strcmp(logic_threshold[i], LOGIC_GET_THRESHOLD_SETTING)) + break; + } + + for (i = 0; i < config->digital_pods; i++){ + //get enabled pods + g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_DIG_POD_STATE], i+1); + if (sr_scpi_get_bool(scpi, command, &state->digital_pods[i].state) != SR_OK) + return SR_ERR; + + //get logic threshold + //the device driver will allways report logic threshold to be "USER" with the currently set actual level saved as user level, since the device only reports current voltage but not the current setting + state->digital_pods[i].threshold = user_index; + g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_DIG_POD_THRESHOLD], i+1); + + if(sr_scpi_get_float(scpi, command, &state->digital_pods[i].user_threshold) != SR_OK) + return SR_ERR; + + } + return SR_OK; +} + +/* Queue data of one channel group, for later submission. */ +SR_PRIV void agilent_54621d_queue_logic_data(struct dev_context *devc, + size_t group, GByteArray *pod_data) +{ + size_t size; + GByteArray *store; + uint8_t *logic_data; + size_t idx, logic_step; + + /* + * Upon first invocation, allocate the array which can hold the + * combined logic data for all channels. Assume that each channel + * will yield an identical number of samples per receive call. + * + * As a poor man's safety measure: (Silently) skip processing + * for unexpected sample counts, and ignore samples for + * unexpected channel groups. Don't bother with complicated + * resize logic, considering that many models only support one + * pod, and the most capable supported models have two pods of + * identical size. We haven't yet seen any "odd" configuration. + */ + if (!devc->logic_data) { + size = pod_data->len * devc->pod_count; //ToDo: check if podcount is correctly set + store = g_byte_array_sized_new(size); + memset(store->data, 0, size); + store = g_byte_array_set_size(store, size); + devc->logic_data = store; + } else { + store = devc->logic_data; + size = store->len / devc->pod_count; + if (group >= devc->pod_count) + return; + } + + /* + * Fold the data of the most recently received channel group into + * the storage, where data resides for all channels combined. + */ + logic_data = store->data; + logic_data += group; + logic_step = devc->pod_count; + for (idx = 0; idx < pod_data->len; idx++) { + *logic_data = pod_data->data[idx]; + logic_data += logic_step; + } + + /* Truncate acquisition if a smaller number of samples has been requested. */ + if (devc->samples_limit > 0 && devc->logic_data->len > devc->samples_limit * devc->pod_count) + devc->logic_data->len = devc->samples_limit * devc->pod_count; +} + +/* Submit data for all channels, after the individual groups got collected. */ +SR_PRIV void agilent_54621d_send_logic_packet(struct sr_dev_inst *sdi, + struct dev_context *devc) +{ + struct sr_datafeed_packet packet; + struct sr_datafeed_logic logic; + + if (!devc->logic_data) + return; + + if((uint64_t)devc->num_blocks_downloaded == devc->trigger_at_sample/2000 && !devc->trigger_sent){ + logic.data = devc->logic_data->data; + logic.length = devc->trigger_at_sample; + logic.unitsize = devc->pod_count; + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + sr_session_send(sdi, &packet); + + packet.type = SR_DF_TRIGGER; + packet.payload = NULL; + sr_session_send(sdi, &packet); + devc->trigger_sent = TRUE; + + logic.data = devc->logic_data[devc->trigger_at_sample].data; + logic.length = devc->logic_data->len-devc->trigger_at_sample; + logic.unitsize = devc->pod_count; + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + sr_session_send(sdi, &packet); + } + else + { + logic.data = devc->logic_data->data; + logic.length = devc->logic_data->len; + logic.unitsize = devc->pod_count; + + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + + sr_session_send(sdi, &packet); + } +} + +/* Undo previous resource allocation. */ +SR_PRIV void agilent_54621d_cleanup_logic_data(struct dev_context *devc) +{ + + if (devc->logic_data) { + g_byte_array_free(devc->logic_data, TRUE); + devc->logic_data = NULL; + } + /* + * Keep 'pod_count'! It's required when more frames will be + * received, and does not harm when kept after acquisition. + */ +} + + +SR_PRIV int agilent_54621d_receive_data(int fd, int revents, void *cb_data) +{ + struct sr_channel *ch; + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_datafeed_packet packet; + GByteArray *data; + struct sr_datafeed_analog analog; + struct sr_analog_encoding encoding; + struct sr_analog_meaning meaning; + struct sr_analog_spec spec; + size_t group; + int i; + struct analog_channel_transfer_info *info; + signed int tmp_int; + char command[MAX_COMMAND_SIZE]; + float timebase_offset; + char *tmp_string; + int points_before_trigger; + + tmp_string = NULL; + + (void)fd; + (void)revents; + + if (!(sdi = cb_data)) + return TRUE; + + if (!(devc = sdi->priv)) + return TRUE; + + ch = devc->current_channel->data; + + switch (ch->type){ + case SR_CHANNEL_ANALOG: + info = (struct analog_channel_transfer_info *)ch->priv; + data = NULL; + if(sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK){ + if(data) + g_byte_array_free(data, TRUE); + sr_err("Failed to retreive block"); + devc->failcount++; + if(devc->failcount>=3){ + sr_scpi_send(sdi->conn, ":WAV:DATA?"); + devc->failcount=0; + } + + return TRUE; + } + devc->failcount=0; + + //if Trigger point is in this block + if((uint64_t)devc->num_blocks_downloaded == devc->trigger_at_sample/2000 && !devc->trigger_sent){ + points_before_trigger = (int)(devc->trigger_at_sample - (devc->num_blocks_downloaded*2000)); + + sr_dbg("Handling trigger in analog package. Trigger at point: %d", points_before_trigger); + + for(i = 0; i<(int)points_before_trigger; i++){ + tmp_int=(int8_t)data->data[i]; + devc->data[i] = (((float)(tmp_int) - info->yReference) * info->yIncrement) + info->yOrigin; + } + if(points_before_trigger>0){ + sr_analog_init(&analog, &encoding, &meaning, &spec, 2); //ToDo: 2 digits is just placeholder. needs to be calculated correctly + analog.meaning->channels = g_slist_append(NULL, ch); + analog.num_samples = points_before_trigger; + analog.data = devc->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); + } + + packet.type = SR_DF_TRIGGER; + packet.payload = NULL; + sr_session_send(sdi, &packet); + devc->trigger_sent = TRUE; + + for(i = points_before_trigger; i<(int)data->len; i++){ + tmp_int=(int8_t)data->data[i]; + devc->data[i] = (((float)(tmp_int) - info->yReference) * info->yIncrement) + info->yOrigin; + } + sr_analog_init(&analog, &encoding, &meaning, &spec, 2); //ToDo: 2 digits is just placeholder. needs to be calculated correctly + analog.meaning->channels = g_slist_append(NULL, ch); + analog.num_samples = data->len-points_before_trigger; + analog.data = devc->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); + } + else + { + for(i = 0; i<(int)data->len; i++){ + tmp_int=(int8_t)data->data[i]; + devc->data[i] = (((float)(tmp_int) - info->yReference) * info->yIncrement) + info->yOrigin; + } + sr_analog_init(&analog, &encoding, &meaning, &spec, 2); //ToDo: 2 digits is just placeholder. needs to be calculated correctly + analog.meaning->channels = g_slist_append(NULL, ch); + analog.num_samples = data->len; + analog.data = devc->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_byte_array_free(data, TRUE); + data = NULL; + break; + case SR_CHANNEL_LOGIC: + data = NULL; + if(sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK){ + if(data) + g_byte_array_free(data, TRUE); + sr_err("Failed to retreive block"); + devc->failcount++; + if(devc->failcount>=3){ + sr_scpi_send(sdi->conn, ":WAV:DATA?"); + devc->failcount=0; + } + + return TRUE; + } + devc->failcount=0; + + group = ch->index / DIGITAL_CHANNELS_PER_POD; + agilent_54621d_queue_logic_data(devc, group, data); + + + break; + default: + sr_err("Invalid channel type"); + break; + } + + //Sometimes the trailing \nl on a datablock is received delayed an not read by the sr_get_data_block. therefore we try to read a response and just dismiss it if there is any + if(sr_scpi_read_data(sdi->conn, tmp_string, 1) == SR_OK) + sr_info("Received delayed NL on block download"); + + //if more channels need to be downloaded for the current frame + if(devc->current_channel->next){ + devc->current_channel = devc->current_channel->next; + //sr_scpi_send(sdi->conn, ":WAV:SOUR %s;DATA?", ((struct sr_channel *)devc->current_channel->data)->name); + ch = (struct sr_channel *)devc->current_channel->data; + if(ch->type == SR_CHANNEL_LOGIC){ + group = ch->index/DIGITAL_CHANNELS_PER_POD+1; + g_snprintf(command, sizeof(command), ":WAV:SOUR POD%ld;DATA?", group); + } else { + g_snprintf(command, sizeof(command), ":WAV:SOUR %s;UNS 0;DATA?", ch->name); + } + sr_scpi_send(sdi->conn, command); + return TRUE; + } + agilent_54621d_send_logic_packet(sdi, devc); + + //if more blocks need to be downloaded + sr_dbg("Downloaded %d/%d datablocks", devc->num_blocks_downloaded, devc->num_block_to_download); + if(devc->num_blocks_downloaded < devc->num_block_to_download){ + devc->num_blocks_downloaded++; + devc->current_channel = devc->enabled_channels; + timebase_offset = devc->timebaseLbound+(devc->num_blocks_downloaded-devc->refPos*0.1)*devc->block_deltaT; + sr_scpi_send(sdi->conn, ":TIM:DEL %f", timebase_offset); + sr_scpi_send(sdi->conn, ":SYST:DSP \"Reading Block %d/%d\"", devc->num_blocks_downloaded+1, devc->num_block_to_download); + //sr_scpi_send(sdi->conn, ":WAV:SOUR %s;DATA?", ((struct sr_channel *)devc->current_channel->data)->name); + ch = (struct sr_channel *)devc->current_channel->data; + if(ch->type == SR_CHANNEL_LOGIC){ + group = ch->index/DIGITAL_CHANNELS_PER_POD+1; + g_snprintf(command, sizeof(command), ":WAV:SOUR POD%ld;DATA?", group); + } else { + g_snprintf(command, sizeof(command), ":WAV:SOUR %s;UNS 0;DATA?", ch->name); + } + sr_scpi_send(sdi->conn, command); + + return TRUE; + } else { + agilent_54621d_cleanup_logic_data(devc); + std_session_send_df_frame_end(sdi); + sr_dev_acquisition_stop(sdi); + return TRUE; + } + return FALSE; +} + diff --git a/src/hardware/agilent-54621d/protocol.h b/src/hardware/agilent-54621d/protocol.h new file mode 100644 index 000000000..94d2f4399 --- /dev/null +++ b/src/hardware/agilent-54621d/protocol.h @@ -0,0 +1,180 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2022 Daniel Echt + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBSIGROK_HARDWARE_AGILENT_54621D_PROTOCOL_H +#define LIBSIGROK_HARDWARE_AGILENT_54621D_PROTOCOL_H + +#include +#include +#include +#include +#include "libsigrok-internal.h" +#include "scpi.h" + +#define LOG_PREFIX "agilent-54621d" +#define MAX_COMMAND_SIZE 128 + +#define DIGITAL_CHANNELS_PER_POD 8 +#define MAX_INSTRUMENT_VERSIONS 10 +#define MAX_ANALOG_CHANNEL_COUNT 4 +#define MAX_DIGITAL_CHANNEL_COUNT 16 +#define MAX_DIGITAL_POD_COUNT 2 +#define MAX_DIGITAL_GROUP_COUNT 2 + + +enum data_source { + DATA_SOURCE_LIVE, + DATA_SOURCE_MEMORY, +}; + +struct scope_config { + const char *name[MAX_INSTRUMENT_VERSIONS]; + const uint8_t analog_channels; + const uint8_t digital_channels; + uint8_t digital_pods; + + const char *(*analog_names)[]; + const char *(*digital_names)[]; + + const uint32_t (*devopts)[]; + const uint8_t num_devopts; + + const uint32_t (*devopts_cg_analog)[]; + const uint8_t num_devopts_cg_analog; + + const uint32_t (*devopts_cg_digital)[]; + const uint8_t num_devopts_cg_digital; + + const char *(*coupling_options)[]; + const uint8_t num_coupling_options; + + const char *(*logic_threshold)[]; + const uint8_t num_logic_threshold; + const gboolean logic_threshold_for_pod; + + const char *(*trigger_sources)[]; + const uint8_t num_trigger_sources; + + const char *(*trigger_slopes)[]; + const uint8_t num_trigger_slopes; + + const uint64_t (*timebases)[][2]; + const uint8_t num_timebases; + + const uint64_t (*vdivs)[][2]; + const uint8_t num_vdivs; + + unsigned int num_xdivs; + const unsigned int num_ydivs; + + const char *(*scpi_dialect)[]; +}; + +struct analog_channel_state { + int coupling; + + int vdiv; + float vertical_offset; + + gboolean state; + char probe_unit; +}; + +struct analog_channel_transfer_info { + int yReference; + float yOrigin; + float yIncrement; + gboolean is_unsigned; + gboolean is_eightbit; +}; + +struct digital_pod_state { + gboolean state; + + int threshold; + float user_threshold; +}; + +struct scope_state { + struct analog_channel_state *analog_channels; + gboolean *digital_channels; + struct digital_pod_state *digital_pods; + + int timebase; + float horiz_triggerpos; + + int trigger_source; + int trigger_slope; + char trigger_pattern[MAX_ANALOG_CHANNEL_COUNT + MAX_DIGITAL_CHANNEL_COUNT + 1]; + + gboolean high_resolution; + gboolean peak_detection; + + uint64_t sample_rate; +}; + +struct dev_context { + const void *model_config; + void *model_state; + + /* Device properties */ + const float minTimebase; + const float maxTimebase; + + struct sr_channel_group **analog_groups; + struct sr_channel_group **digital_groups; + + enum data_source data_source; + + GSList *enabled_channels; + GSList *current_channel; + uint64_t num_samples; + uint64_t num_frames; + + uint64_t samples_limit; + uint64_t frame_limit; + + size_t pod_count; + + //helper variables for data download + uint64_t sample_rate_limit; + GByteArray *buffer; + GSList *channels_to_download; + int num_block_to_download; + int num_blocks_downloaded; + float block_deltaT; + float timebaseLbound; + gboolean headerSent; + float *data; + int failcount; + GByteArray *logic_data; + uint64_t trigger_at_sample; + gboolean trigger_sent; + int refPos; +}; + +SR_PRIV int agilent_54621d_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int agilent_54621d_request_data(const struct sr_dev_inst *sdi); +SR_PRIV int agilent_54621d_init_device(struct sr_dev_inst *sdi); +SR_PRIV int agilent_54621d_scope_state_get(struct sr_dev_inst *sdi); +SR_PRIV int agilent_54621d_update_sample_rate(const struct sr_dev_inst *sdi); +SR_PRIV void agilent_54621d_scope_state_free(struct scope_state *state); + + +#endif diff --git a/src/scpi/scpi.c b/src/scpi/scpi.c index be829af4b..fc710ee33 100644 --- a/src/scpi/scpi.c +++ b/src/scpi/scpi.c @@ -57,6 +57,7 @@ static int parse_strict_bool(const char *str, gboolean *ret) return SR_ERR_ARG; if (!g_strcmp0(str, "1") || + !g_strcmp0(str, "+1") || !g_ascii_strncasecmp(str, "y", 1) || !g_ascii_strncasecmp(str, "t", 1) || !g_ascii_strncasecmp(str, "yes", 3) || @@ -65,6 +66,7 @@ static int parse_strict_bool(const char *str, gboolean *ret) *ret = TRUE; return SR_OK; } else if (!g_strcmp0(str, "0") || + !g_strcmp0(str, "+0") || !g_ascii_strncasecmp(str, "n", 1) || !g_ascii_strncasecmp(str, "f", 1) || !g_ascii_strncasecmp(str, "no", 2) ||