/*
* TODO
*
- * - Unbreak analog data when submitted in the 'double' data type. This
- * was observed with sigrok-cli screen output. Is analog.encoding->unitsize
- * not handled appropriately? Is it a sigrok-cli or libsigrok issue?
* - Add a test suite for input modules in general, and CSV in specific?
* Becomes more important with the multitude of options and their
* interaction. Could cover edge cases (BOM presence, line termination
* samplerates, etc).
*/
-typedef float csv_analog_t; /* 'double' currently is flawed. */
+typedef double csv_analog_t;
/* Single column formats. */
enum single_col_format {
/* Current samplerate, optionally determined from input data. */
uint64_t samplerate;
+ uint64_t calc_samplerate;
double prev_timestamp;
gboolean samplerate_sent;
/* List of previously created sigrok channels. */
GSList *prev_sr_channels;
+ GSList **prev_df_channels;
};
/*
static int flush_samplerate(const struct sr_input *in)
{
struct context *inc;
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_config *src;
inc = in->priv;
- if (inc->samplerate && !inc->samplerate_sent) {
- packet.type = SR_DF_META;
- packet.payload = &meta;
- src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
- meta.config = g_slist_append(NULL, src);
- sr_session_send(in->sdi, &packet);
- g_slist_free(meta.config);
- sr_config_free(src);
+ if (!inc->calc_samplerate && inc->samplerate)
+ inc->calc_samplerate = inc->samplerate;
+ if (inc->calc_samplerate && !inc->samplerate_sent) {
+ (void)sr_session_send_meta(in->sdi, SR_CONF_SAMPLERATE,
+ g_variant_new_uint64(inc->calc_samplerate));
inc->samplerate_sent = TRUE;
}
return rc;
inc->datafeed_buf_fill = 0;
+
return SR_OK;
}
if (rc != SR_OK)
return rc;
}
+
return SR_OK;
}
{
if (ch_idx >= inc->analog_channels)
return;
- if (!value)
- return;
inc->analog_sample_buffer[ch_idx * inc->analog_datafeed_buf_size] = value;
}
}
inc->analog_datafeed_buf_fill = 0;
+
return SR_OK;
}
if (rc != SR_OK)
return rc;
}
+
return SR_OK;
}
* line won't get processed another time.
*/
column = column_texts[detail->col_nr - 1];
- if (inc->use_header && column && *column)
+ if (inc->use_header && column && *column) {
+ column = g_strstrip(column);
caption = sr_scpi_unquote_string(column);
- else
+ } else {
caption = NULL;
+ }
if (!caption || !*caption)
caption = NULL;
/*
return NULL;
if (!nr || nr > inc->column_want_count)
return NULL;
+
return &inc->column_details[nr - 1];
}
}
/**
- * @brief Splits a text line into a set of columns.
+ * Splits a text line into a set of columns.
*
* @param[in] buf The input text line to split.
* @param[in] inc The input module's context.
*/
static char **split_line(char *buf, struct context *inc)
{
- return g_strsplit(buf, inc->delimiter->str, 0);
+ char **fields, *f;
+ size_t l;
+
+ fields = g_strsplit(buf, inc->delimiter->str, 0);
+ if (!fields)
+ return NULL;
+
+ l = g_strv_length(fields);
+ while (l--) {
+ f = fields[l];
+ g_strchomp(f);
+ }
+
+ return fields;
}
/**
- * @brief Parse a multi-bit field into several logic channels.
+ * Parse a multi-bit field into several logic channels.
*
* @param[in] column The input text, a run of bin/hex/oct digits.
* @param[in] inc The input module's context.
}
/**
- * @brief Parse a floating point text into an analog value.
+ * Parse a floating point text into an analog value.
*
* @param[in] column The input text, a floating point number.
* @param[in] inc The input module's context.
}
/**
- * @brief Parse a timestamp text, auto-determine samplerate.
+ * Parse a timestamp text, auto-determine samplerate.
*
* @param[in] column The input text, a floating point number.
* @param[in] inc The input module's context.
* reduced rounding errors which result in odd rates.
* - Support other formats ("2 ms" or similar)?
*/
- if (inc->samplerate)
+ if (inc->calc_samplerate)
return SR_OK;
ret = sr_atod_ascii(column, &ts);
if (ret != SR_OK)
ts = 0.0;
if (!ts) {
- sr_warn("Cannot convert timestamp text %s in line %zu (or zero value).",
+ sr_info("Cannot convert timestamp text %s in line %zu (or zero value).",
column, inc->line_number);
inc->prev_timestamp = 0.0;
return SR_OK;
rate += 0.5;
rate = (uint64_t)rate;
sr_dbg("Rate from timestamp %g in line %zu.", rate, inc->line_number);
- inc->samplerate = rate;
+ inc->calc_samplerate = rate;
inc->prev_timestamp = 0.0;
return SR_OK;
}
/**
- * @brief Parse routine which ignores the input text.
+ * Parse routine which ignores the input text.
*
* This routine exists to unify dispatch code paths, mapping input file
* columns' data types to their respective parse routines.
(void)column;
(void)inc;
(void)details;
+
return SR_OK;
}
if (!status)
return SR_ERR;
+
return SR_OK;
}
* Check the channel list for consistency across file re-import. See
* the VCD input module for more details and motivation.
*/
+static void release_df_channels(struct context *inc, GSList **l)
+{
+ size_t idx;
+
+ if (!inc->analog_channels || !l)
+ return;
+ for (idx = 0; idx < inc->analog_channels; idx++)
+ g_slist_free(l[idx]);
+ g_free(l);
+}
static void keep_header_for_reread(const struct sr_input *in)
{
struct context *inc;
inc = in->priv;
+
g_slist_free_full(inc->prev_sr_channels, sr_channel_free_cb);
inc->prev_sr_channels = in->sdi->channels;
in->sdi->channels = NULL;
+
+ release_df_channels(inc, inc->prev_df_channels);
+ inc->prev_df_channels = inc->analog_datafeed_channels;
+ inc->analog_datafeed_channels = NULL;
}
static int check_header_in_reread(const struct sr_input *in)
sr_err("Channel list change not supported for file re-read.");
return FALSE;
}
+
g_slist_free_full(in->sdi->channels, sr_channel_free_cb);
in->sdi->channels = inc->prev_sr_channels;
inc->prev_sr_channels = NULL;
+ release_df_channels(inc, inc->analog_datafeed_channels);
+ inc->analog_datafeed_channels = inc->prev_df_channels;
+ inc->prev_df_channels = NULL;
+
return TRUE;
}
ret = SR_ERR;
goto out;
}
- sr_dbg("DIAG Got %zu columns in text line: %s.", num_columns, line);
+ sr_dbg("Got %zu columns in text line: %s.", num_columns, line);
/*
* Interpret the user provided column format specs. This might
inc->analog_datafeed_buf_size /= sample_size;
inc->analog_datafeed_buf_size /= inc->analog_channels;
sample_count = inc->analog_channels * inc->analog_datafeed_buf_size;
- inc->analog_datafeed_buffer = g_malloc0(sample_count * sample_size);
+ inc->analog_datafeed_buffer = g_malloc(sample_count * sample_size);
if (!inc->analog_datafeed_buffer) {
sr_err("Cannot allocate datafeed send buffer (analog).");
ret = SR_ERR_MALLOC;
static void cleanup(struct sr_input *in)
{
- struct context *inc;
+ struct context *inc, save_ctx;
+ /* Keep channel references between file re-imports. */
keep_header_for_reread(in);
+ /* Release dynamically allocated resources. */
inc = in->priv;
g_free(inc->termination);
inc->datafeed_buffer = NULL;
g_free(inc->analog_datafeed_buffer);
inc->analog_datafeed_buffer = NULL;
+ g_free(inc->analog_datafeed_digits);
+ inc->analog_datafeed_digits = NULL;
+ /* analog_datafeed_channels was released in keep_header_for_reread() */
+ /* TODO Release channel names (before releasing details). */
+ g_free(inc->column_details);
+ inc->column_details = NULL;
+
+ /* Clear internal state, but keep what .init() has provided. */
+ save_ctx = *inc;
+ memset(inc, 0, sizeof(*inc));
+ inc->samplerate = save_ctx.samplerate;
+ inc->delimiter = save_ctx.delimiter;
+ inc->comment = save_ctx.comment;
+ inc->column_formats = save_ctx.column_formats;
+ inc->start_line = save_ctx.start_line;
+ inc->use_header = save_ctx.use_header;
+ inc->prev_sr_channels = save_ctx.prev_sr_channels;
+ inc->prev_df_channels = save_ctx.prev_df_channels;
}
static int reset(struct sr_input *in)
{
- struct context *inc = in->priv;
+ struct context *inc;
+ inc = in->priv;
cleanup(in);
inc->started = FALSE;
g_string_truncate(in->buf, 0);
},
[OPT_HEADER] = {
"header", "Get channel names from first line.",
- "Use the first processed line's column captions (when available) as channel names. Off by default",
+ "Use the first processed line's column captions (when available) as channel names. Enabled by default.",
NULL, NULL,
},
[OPT_SAMPLERATE] = {
l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("oct")));
options[OPT_SINGLE_FMT].values = l;
options[OPT_START_LINE].def = g_variant_ref_sink(g_variant_new_uint32(1));
- options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+ options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
options[OPT_SAMPLERATE].def = g_variant_ref_sink(g_variant_new_uint64(0));
options[OPT_COL_SEP].def = g_variant_ref_sink(g_variant_new_string(","));
options[OPT_COMMENT].def = g_variant_ref_sink(g_variant_new_string(";"));