]> sigrok.org Git - libsigrok.git/blob - src/output/csv.c
c4801ef7f984bca96b432fda2eb37c55fd9da730
[libsigrok.git] / src / output / csv.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /*
21  * Options and their values:
22  *
23  * gnuplot: Write out a gnuplot interpreter script (.gpi file) to plot
24  *          the datafile using the parameters given. It should be called
25  *          from a gnuplot session with the data file name as a parameter
26  *          after adjusting line styles, terminal, etc.
27  *
28  * scale:   The gnuplot graphs are scaled so they all have the same
29  *          peak-to-peak distance. Defaults to TRUE.
30  *
31  * value:   The string used to separate values in a record. Defaults to ','.
32  *
33  * record:  The string to use to separate records. Default is newline. gnuplot
34  *          files must use newline.
35  *
36  * frame:   The string to use when a frame ends. The default is a blank line.
37  *          This may confuse some CSV parsers, but it makes gnuplot happy.
38  *
39  * comment: The string that starts a comment line. Defaults to ';'.
40  *
41  * header:  Print header comment with capture metadata. Defaults to TRUE.
42  *
43  * label:   What to use for channel labels as the first line of output.
44  *          Values are "channel", "units", "off". Defaults to "units".
45  *
46  * time:    Whether or not the first column should include the time the sample
47  *          was taken. Defaults to FALSE.
48  *
49  * trigger: Whether or not to add a "trigger" column as the last column.
50  *          Defaults to FALSE.
51  *
52  * dedup:   Don't output duplicate rows. Defaults to FALSE. If time is off, then
53  *          this is forced to be off.
54  */
55
56 #include <config.h>
57 #include <math.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <glib.h>
61 #include <libsigrok/libsigrok.h>
62 #include "libsigrok-internal.h"
63
64 #define LOG_PREFIX "output/csv"
65
66 struct ctx_channel {
67         struct sr_channel *ch;
68         char *label;
69         float min, max;
70 };
71
72 struct context {
73         /* Options */
74         const char *gnuplot;
75         gboolean scale;
76         const char *value;
77         const char *record;
78         const char *frame;
79         const char *comment;
80         gboolean header, did_header;
81         gboolean label_do, label_did, label_names;
82         gboolean time;
83         gboolean do_trigger;
84         gboolean dedup;
85
86         /* Plot data */
87         unsigned int num_analog_channels;
88         unsigned int num_logic_channels;
89         struct ctx_channel *channels;
90
91         /* Metadata */
92         gboolean trigger;
93         uint32_t num_samples;
94         uint32_t channel_count, logic_channel_count;
95         uint32_t channels_seen;
96         uint64_t sample_rate;
97         uint64_t sample_scale;
98         uint64_t out_sample_count;
99         uint8_t *previous_sample;
100         float *analog_samples;
101         uint8_t *logic_samples;
102         const char *xlabel;     /* Don't free: will point to a static string. */
103         const char *title;      /* Don't free: will point into the driver struct. */
104
105         /* Input data constraints check. */
106         gboolean have_checked;
107         gboolean have_frames;
108         uint64_t pkt_snums;
109 };
110
111 /*
112  * TODO:
113  *  - Option to print comma-separated bits, or whole bytes/words (for 8/16
114  *    channel LAs) as ASCII/hex etc. etc.
115  */
116
117 static int init(struct sr_output *o, GHashTable *options)
118 {
119         unsigned int i, analog_channels, logic_channels;
120         struct context *ctx;
121         struct sr_channel *ch;
122         const char *label_string;
123         GSList *l;
124
125         if (!o || !o->sdi)
126                 return SR_ERR_ARG;
127
128         ctx = g_malloc0(sizeof(struct context));
129         o->priv = ctx;
130
131         /* Options */
132         ctx->gnuplot = g_strdup(g_variant_get_string(
133                 g_hash_table_lookup(options, "gnuplot"), NULL));
134         ctx->scale = g_variant_get_boolean(g_hash_table_lookup(options, "scale"));
135         ctx->value = g_strdup(g_variant_get_string(
136                 g_hash_table_lookup(options, "value"), NULL));
137         ctx->record = g_strdup(g_variant_get_string(
138                 g_hash_table_lookup(options, "record"), NULL));
139         ctx->frame = g_strdup(g_variant_get_string(
140                 g_hash_table_lookup(options, "frame"), NULL));
141         ctx->comment = g_strdup(g_variant_get_string(
142                 g_hash_table_lookup(options, "comment"), NULL));
143         ctx->header = g_variant_get_boolean(g_hash_table_lookup(options, "header"));
144         ctx->time = g_variant_get_boolean(g_hash_table_lookup(options, "time"));
145         ctx->do_trigger = g_variant_get_boolean(g_hash_table_lookup(options, "trigger"));
146         label_string = g_variant_get_string(
147                 g_hash_table_lookup(options, "label"), NULL);
148         ctx->dedup = g_variant_get_boolean(g_hash_table_lookup(options, "dedup"));
149         ctx->dedup &= ctx->time;
150
151         if (*ctx->gnuplot && g_strcmp0(ctx->record, "\n"))
152                 sr_warn("gnuplot record separator must be newline.");
153
154         if (*ctx->gnuplot && strlen(ctx->value) > 1)
155                 sr_warn("gnuplot doesn't support multichar value separators.");
156
157         if ((ctx->label_did = ctx->label_do = g_strcmp0(label_string, "off") != 0))
158                 ctx->label_names = g_strcmp0(label_string, "units") != 0;
159
160         sr_dbg("gnuplot = '%s', scale = %d", ctx->gnuplot, ctx->scale);
161         sr_dbg("value = '%s', record = '%s', frame = '%s', comment = '%s'",
162                ctx->value, ctx->record, ctx->frame, ctx->comment);
163         sr_dbg("header = %d, time = %d, do_trigger = %d, dedup = %d",
164                ctx->header, ctx->time, ctx->do_trigger, ctx->dedup);
165         sr_dbg("label_do = %d, label_names = %d", ctx->label_do, ctx->label_names);
166
167         analog_channels = logic_channels = 0;
168         /* Get the number of channels, and the unitsize. */
169         for (l = o->sdi->channels; l; l = l->next) {
170                 ch = l->data;
171                 if (ch->type == SR_CHANNEL_LOGIC) {
172                         ctx->logic_channel_count++;
173                         if (ch->enabled)
174                                 logic_channels++;
175                 }
176                 if (ch->type == SR_CHANNEL_ANALOG && ch->enabled)
177                         analog_channels++;
178         }
179         if (analog_channels) {
180                 sr_info("Outputting %d analog values", analog_channels);
181                 ctx->num_analog_channels = analog_channels;
182         }
183         if (logic_channels) {
184                 sr_info("Outputting %d logic values", logic_channels);
185                 ctx->num_logic_channels = logic_channels;
186         }
187         ctx->channels = g_malloc(sizeof(struct ctx_channel)
188                 * (ctx->num_analog_channels + ctx->num_logic_channels));
189
190         /* Once more to map the enabled channels. */
191         ctx->channel_count = g_slist_length(o->sdi->channels);
192         for (i = 0, l = o->sdi->channels; l; l = l->next) {
193                 ch = l->data;
194                 if (ch->enabled) {
195                         if (ch->type == SR_CHANNEL_ANALOG) {
196                                 ctx->channels[i].min = FLT_MAX;
197                                 ctx->channels[i].max = FLT_MIN;
198                         } else if (ch->type == SR_CHANNEL_LOGIC) {
199                                 ctx->channels[i].min = 0;
200                                 ctx->channels[i].max = 1;
201                         } else {
202                                 sr_warn("Unknown channel type %d.", ch->type);
203                         }
204                         if (ctx->label_do && ctx->label_names)
205                                 ctx->channels[i].label = ch->name;
206                         ctx->channels[i++].ch = ch;
207                 }
208         }
209
210         return SR_OK;
211 }
212
213 static const char *xlabels[] = {
214         "samples", "milliseconds", "microseconds", "nanoseconds", "picoseconds",
215         "femtoseconds", "attoseconds",
216 };
217
218 static GString *gen_header(const struct sr_output *o,
219                            const struct sr_datafeed_header *hdr)
220 {
221         struct context *ctx;
222         struct sr_channel *ch;
223         GVariant *gvar;
224         GString *header;
225         GSList *channels, *l;
226         unsigned int num_channels, i;
227         char *samplerate_s;
228
229         ctx = o->priv;
230         header = g_string_sized_new(512);
231
232         if (ctx->sample_rate == 0) {
233                 if (sr_config_get(o->sdi->driver, o->sdi, NULL,
234                                   SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
235                         ctx->sample_rate = g_variant_get_uint64(gvar);
236                         g_variant_unref(gvar);
237                 }
238
239                 i = 0;
240                 ctx->sample_scale = 1;
241                 while (ctx->sample_scale < ctx->sample_rate) {
242                         i++;
243                         ctx->sample_scale *= 1000;
244                 }
245                 if (i < ARRAY_SIZE(xlabels))
246                         ctx->xlabel = xlabels[i];
247                 sr_info("Set sample rate, scale to %" PRIu64 ", %" PRIu64 " %s",
248                         ctx->sample_rate, ctx->sample_scale, ctx->xlabel);
249         }
250         ctx->title = (o->sdi && o->sdi->driver) ? o->sdi->driver->longname : "unknown";
251
252         /* Some metadata */
253         if (ctx->header && !ctx->did_header) {
254                 /* save_gnuplot knows how many lines we print. */
255                 time_t secs;
256                 secs = hdr->starttime.tv_sec;
257                 g_string_append_printf(header,
258                         "%s CSV generated by %s %s\n%s from %s on %s",
259                         ctx->comment, PACKAGE_NAME,
260                         sr_package_version_string_get(), ctx->comment,
261                         ctx->title, ctime(&secs));
262
263                 /* Columns / channels */
264                 channels = o->sdi ? o->sdi->channels : NULL;
265                 num_channels = g_slist_length(channels);
266                 g_string_append_printf(header, "%s Channels (%d/%d):",
267                         ctx->comment, ctx->num_analog_channels +
268                         ctx->num_logic_channels, num_channels);
269                 for (l = channels; l; l = l->next) {
270                         ch = l->data;
271                         if (ch->enabled)
272                                 g_string_append_printf(header, " %s,", ch->name);
273                 }
274                 if (channels) {
275                         /* Drop last separator. */
276                         g_string_truncate(header, header->len - 1);
277                 }
278                 g_string_append_printf(header, "\n");
279                 if (ctx->sample_rate != 0) {
280                         samplerate_s = sr_samplerate_string(ctx->sample_rate);
281                         g_string_append_printf(header, "%s Samplerate: %s\n",
282                                                ctx->comment, samplerate_s);
283                         g_free(samplerate_s);
284                 }
285                 ctx->did_header = TRUE;
286         }
287
288         /* Time column requested but samplerate unknown. Emit a warning. */
289         if (ctx->time && !ctx->sample_rate)
290                 sr_warn("Samplerate unknown, cannot provide timestamps.");
291
292         return header;
293 }
294
295 /*
296  * Analog devices can have samples of different types. Since each
297  * packet has only one meaning, it is restricted to having at most one
298  * type of data. So they can send multiple packets for a single sample.
299  * To further complicate things, they can send multiple samples in a
300  * single packet.
301  *
302  * So we need to pull any channels of interest out of a packet and save
303  * them until we have complete samples to output. Some devices make this
304  * simple by sending DF_FRAME_BEGIN/DF_FRAME_END packets, the latter of which
305  * signals the end of a set of samples, so we can dump things there.
306  *
307  * At least one driver (the demo driver) sends packets that contain parts of
308  * multiple samples without wrapping them in DF_FRAME. Possibly this driver
309  * is buggy, but it's also the standard for testing, so it has to be supported
310  * as is.
311  *
312  * Many assumptions about the "shape" of the data here:
313  *
314  * All of the data for a channel is assumed to be in one frame;
315  * otherwise the data in the second packet will overwrite the data in
316  * the first packet.
317  */
318 static void process_analog(struct context *ctx,
319                            const struct sr_datafeed_analog *analog)
320 {
321         int ret;
322         size_t num_rcvd_ch, num_have_ch;
323         size_t idx_have, idx_smpl, idx_rcvd;
324         size_t idx_send;
325         struct sr_analog_meaning *meaning;
326         GSList *l;
327         float *fdata = NULL;
328         struct sr_channel *ch;
329
330         if (!ctx->analog_samples) {
331                 ctx->analog_samples = g_malloc(analog->num_samples
332                         * sizeof(float) * ctx->num_analog_channels);
333                 if (!ctx->num_samples)
334                         ctx->num_samples = analog->num_samples;
335         }
336         if (ctx->num_samples != analog->num_samples)
337                 sr_warn("Expecting %u analog samples, got %u.",
338                         ctx->num_samples, analog->num_samples);
339
340         meaning = analog->meaning;
341         num_rcvd_ch = g_slist_length(meaning->channels);
342         ctx->channels_seen += num_rcvd_ch;
343         sr_dbg("Processing packet of %zu analog channels", num_rcvd_ch);
344         fdata = g_malloc(analog->num_samples * num_rcvd_ch * sizeof(float));
345         if ((ret = sr_analog_to_float(analog, fdata)) != SR_OK)
346                 sr_warn("Problems converting data to floating point values.");
347
348         num_have_ch = ctx->num_analog_channels + ctx->num_logic_channels;
349         idx_send = 0;
350         for (idx_have = 0; idx_have < num_have_ch; idx_have++) {
351                 if (ctx->channels[idx_have].ch->type != SR_CHANNEL_ANALOG)
352                         continue;
353                 sr_dbg("Looking for channel %s",
354                        ctx->channels[idx_have].ch->name);
355                 for (l = meaning->channels, idx_rcvd = 0; l; l = l->next, idx_rcvd++) {
356                         ch = l->data;
357                         sr_dbg("Checking %s", ch->name);
358                         if (ctx->channels[idx_have].ch != ch)
359                                 continue;
360                         if (ctx->label_do && !ctx->label_names) {
361                                 sr_analog_unit_to_string(analog,
362                                         &ctx->channels[idx_have].label);
363                         }
364                         for (idx_smpl = 0; idx_smpl < analog->num_samples; idx_smpl++)
365                                 ctx->analog_samples[idx_smpl * ctx->num_analog_channels + idx_send] = fdata[idx_smpl * num_rcvd_ch + idx_rcvd];
366                         break;
367                 }
368                 idx_send++;
369         }
370         g_free(fdata);
371 }
372
373 /*
374  * We treat logic packets the same as analog packets, though it's not
375  * strictly required. This allows us to process mixed signals properly.
376  */
377 static void process_logic(struct context *ctx,
378                           const struct sr_datafeed_logic *logic)
379 {
380         unsigned int i, j, ch, num_samples;
381         int idx;
382         uint8_t *sample;
383
384         num_samples = logic->length / logic->unitsize;
385         ctx->channels_seen += ctx->logic_channel_count;
386         sr_dbg("Logic packet had %d channels", logic->unitsize * 8);
387         if (!ctx->logic_samples) {
388                 ctx->logic_samples = g_malloc(num_samples * ctx->num_logic_channels);
389                 if (!ctx->num_samples)
390                         ctx->num_samples = num_samples;
391         }
392         if (ctx->num_samples != num_samples)
393                 sr_warn("Expecting %u samples, got %u",
394                         ctx->num_samples, num_samples);
395
396         for (j = ch = 0; ch < ctx->num_logic_channels; j++) {
397                 if (ctx->channels[j].ch->type == SR_CHANNEL_LOGIC) {
398                         for (i = 0; i < num_samples; i++) {
399                                 sample = logic->data + i * logic->unitsize;
400                                 idx = ctx->channels[j].ch->index;
401                                 if (ctx->label_do && !ctx->label_names)
402                                         ctx->channels[j].label = "logic";
403                                 ctx->logic_samples[i * ctx->num_logic_channels + ch] = sample[idx / 8] & (1 << (idx % 8));
404                         }
405                         ch++;
406                 }
407         }
408 }
409
410 static void dump_saved_values(struct context *ctx, GString **out)
411 {
412         unsigned int i, j, analog_size, num_channels;
413         double sample_time_dbl;
414         uint64_t sample_time_u64;
415         float *analog_sample, value;
416         uint8_t *logic_sample;
417
418         /* If we haven't seen samples we're expecting, skip them. */
419         if ((ctx->num_analog_channels && !ctx->analog_samples) ||
420             (ctx->num_logic_channels && !ctx->logic_samples)) {
421                 sr_warn("Discarding partial packet");
422         } else {
423                 sr_info("Dumping %u samples", ctx->num_samples);
424
425                 if (!*out)
426                         *out = g_string_sized_new(512);
427                 num_channels =
428                     ctx->num_logic_channels + ctx->num_analog_channels;
429
430                 if (ctx->label_do) {
431                         if (ctx->time)
432                                 g_string_append_printf(*out, "%s%s",
433                                         ctx->label_names ? "Time" : ctx->xlabel,
434                                         ctx->value);
435                         for (i = 0; i < num_channels; i++) {
436                                 g_string_append_printf(*out, "%s%s",
437                                         ctx->channels[i].label, ctx->value);
438                                 if (ctx->channels[i].ch->type == SR_CHANNEL_ANALOG
439                                                 && ctx->label_names)
440                                         g_free(ctx->channels[i].label);
441                         }
442                         if (ctx->do_trigger)
443                                 g_string_append_printf(*out, "Trigger%s",
444                                                        ctx->value);
445                         /* Drop last separator. */
446                         g_string_truncate(*out, (*out)->len - 1);
447                         g_string_append(*out, ctx->record);
448
449                         ctx->label_do = FALSE;
450                 }
451
452                 analog_size = ctx->num_analog_channels * sizeof(float);
453                 if (ctx->dedup && !ctx->previous_sample)
454                         ctx->previous_sample = g_malloc0(analog_size + ctx->num_logic_channels);
455
456                 for (i = 0; i < ctx->num_samples; i++) {
457                         analog_sample =
458                             &ctx->analog_samples[i * ctx->num_analog_channels];
459                         logic_sample =
460                             &ctx->logic_samples[i * ctx->num_logic_channels];
461
462                         if (ctx->dedup) {
463                                 if (i > 0 && i < ctx->num_samples - 1 &&
464                                     !memcmp(logic_sample, ctx->previous_sample,
465                                             ctx->num_logic_channels) &&
466                                     !memcmp(analog_sample,
467                                             ctx->previous_sample +
468                                             ctx->num_logic_channels,
469                                             analog_size))
470                                         continue;
471                                 memcpy(ctx->previous_sample, logic_sample,
472                                        ctx->num_logic_channels);
473                                 memcpy(ctx->previous_sample
474                                        + ctx->num_logic_channels,
475                                        analog_sample, analog_size);
476                         }
477
478                         if (ctx->time && !ctx->sample_rate) {
479                                 g_string_append_printf(*out, "0%s", ctx->value);
480                         } else if (ctx->time) {
481                                 sample_time_dbl = ctx->out_sample_count++;
482                                 sample_time_dbl /= ctx->sample_rate;
483                                 sample_time_dbl *= ctx->sample_scale;
484                                 sample_time_u64 = sample_time_dbl;
485                                 g_string_append_printf(*out, "%" PRIu64 "%s",
486                                         sample_time_u64, ctx->value);
487                         }
488
489                         for (j = 0; j < num_channels; j++) {
490                                 if (ctx->channels[j].ch->type == SR_CHANNEL_ANALOG) {
491                                         value = ctx->analog_samples[i * ctx->num_analog_channels + j];
492                                         ctx->channels[j].max =
493                                             fmax(value, ctx->channels[j].max);
494                                         ctx->channels[j].min =
495                                             fmin(value, ctx->channels[j].min);
496                                         g_string_append_printf(*out, "%g%s",
497                                                 value, ctx->value);
498                                 } else if (ctx->channels[j].ch->type == SR_CHANNEL_LOGIC) {
499                                         g_string_append_printf(*out, "%c%s",
500                                                                ctx->logic_samples[i * ctx->num_logic_channels + j] ? '1' : '0', ctx->value);
501                                 } else {
502                                         sr_warn("Unexpected channel type: %d",
503                                                 ctx->channels[i].ch->type);
504                                 }
505                         }
506
507                         if (ctx->do_trigger) {
508                                 g_string_append_printf(*out, "%d%s",
509                                         ctx->trigger, ctx->value);
510                                 ctx->trigger = FALSE;
511                         }
512                         g_string_truncate(*out, (*out)->len - 1);
513                         g_string_append(*out, ctx->record);
514                 }
515         }
516
517         /* Discard all of the working space. */
518         g_free(ctx->previous_sample);
519         g_free(ctx->analog_samples);
520         g_free(ctx->logic_samples);
521         ctx->channels_seen = 0;
522         ctx->num_samples = 0;
523         ctx->previous_sample = NULL;
524         ctx->analog_samples = NULL;
525         ctx->logic_samples = NULL;
526 }
527
528 static void save_gnuplot(struct context *ctx)
529 {
530         float offset, max, sum;
531         unsigned int i, num_channels;
532         GString *script;
533
534         script = g_string_sized_new(512);
535         g_string_append_printf(script, "set datafile separator '%s'\n",
536                                ctx->value);
537         if (ctx->label_did)
538                 g_string_append(script, "set key autotitle columnhead\n");
539         if (ctx->xlabel && ctx->time)
540                 g_string_append_printf(script, "set xlabel '%s'\n",
541                                        ctx->xlabel);
542
543         g_string_append(script, "plot ");
544
545         num_channels = ctx->num_analog_channels + ctx->num_logic_channels;
546
547         /* Graph position and scaling. */
548         max = FLT_MIN;
549         sum = 0;
550         for (i = 0; i < num_channels; i++) {
551                 ctx->channels[i].max =
552                     ctx->channels[i].max - ctx->channels[i].min;
553                 max = fmax(max, ctx->channels[i].max);
554                 sum += ctx->channels[i].max;
555         }
556         sum = (ctx->scale ? max : sum / num_channels) / 4;
557         offset = sum;
558         for (i = num_channels; i > 0;) {
559                 i--;
560                 ctx->channels[i].min = offset - ctx->channels[i].min;
561                 offset += sum + (ctx->scale ? max : ctx->channels[i].max);
562         }
563
564         for (i = 0; i < num_channels; i++) {
565                 sr_spew("Channel %d, min %g, max %g", i, ctx->channels[i].min,
566                         ctx->channels[i].max);
567                 g_string_append(script, "ARG1 ");
568                 if (ctx->did_header)
569                         g_string_append(script, "skip 4 ");
570                 g_string_append_printf(script, "using %u:($%u * %g + %g), ",
571                         ctx->time, i + 1 + ctx->time, ctx->scale ?
572                         max / ctx->channels[i].max : 1, ctx->channels[i].min);
573                 offset += 1.1 * (ctx->channels[i].max - ctx->channels[i].min);
574         }
575         g_string_truncate(script, script->len - 2);
576         g_file_set_contents(ctx->gnuplot, script->str, script->len, NULL);
577         g_string_free(script, TRUE);
578 }
579
580 static void check_input_constraints(struct context *ctx)
581 {
582         size_t snum_count, snum_warn_limit;
583         size_t logic, analog;
584         gboolean has_frames, is_short, is_mixed, is_multi_analog;
585         gboolean do_warn;
586
587         /*
588          * Check and conditionally warn exactly once during execution
589          * of the output module on a set of input data.
590          */
591         if (ctx->have_checked)
592                 return;
593         ctx->have_checked = TRUE;
594
595         /*
596          * This implementation of the CSV output module assumes some
597          * constraints which need not be met in reality. Emit warnings
598          * until a better version becomes available. Letting users know
599          * that their request may not get processed correctly is the
600          * only thing we can do for now except for complete refusal to
601          * process the input data.
602          *
603          * What the implementation appears to assume (unverified, this
604          * interpretation may be incorrect and/or incomplete):
605          * - Multi-channel analog data, or mixed signal input, always
606          *   is enclosed in frame markers.
607          * - Data which gets received across several packets spans a
608          *   consistent sample number range. All samples of one frame
609          *   and channel number or data type fit into a single packet.
610          *   Arbitrary chunking seems to not be supported.
611          * - A specific order of analog data packets is assumed.
612          *
613          * With these assumptions encoded in the implementation, and
614          * not being met at runtime, incorrect and unexpected results
615          * were seen for these configurations:
616          * - More than one analog channel.
617          * - The combination of logic and analog channel types.
618          *
619          * The condition of frames with large sample counts is a wild
620          * guess, the limit is a totally arbitrary choice. It assumes
621          * typical scope frames with at most a few thousand samples per
622          * frame, and assumes that a channel's data gets sent in large
623          * enough packets. The absence of a warning message does not
624          * necessarily translate to correct output, it's more of a rate
625          * limiting approach to not scare users too much.
626          */
627         snum_count = ctx->pkt_snums;
628         snum_warn_limit = 1 * 1000 * 1000;
629         logic = ctx->num_logic_channels;
630         analog = ctx->num_analog_channels;
631         has_frames = ctx->have_frames;
632         is_short = snum_count < snum_warn_limit;
633         is_mixed = logic && analog;
634         is_multi_analog = analog > 1;
635
636         if (has_frames && is_short) {
637                 sr_info("Assuming consistent framed input data.");
638                 return;
639         }
640
641         do_warn = FALSE;
642         if (has_frames) {
643                 sr_warn("Untested configuration: large frame content.");
644                 do_warn = TRUE;
645         }
646         if (is_mixed) {
647                 sr_warn("Untested configuration: mixed signal input data.");
648                 do_warn = TRUE;
649         }
650         if (is_multi_analog) {
651                 sr_warn("Untested configuration: multi-channel analog data.");
652                 do_warn = TRUE;
653         }
654         if (!do_warn)
655                 return;
656         sr_warn("Resulting CSV output data may be incomplete or incorrect.");
657 }
658
659 static int receive(const struct sr_output *o,
660                    const struct sr_datafeed_packet *packet, GString **out)
661 {
662         struct context *ctx;
663         const struct sr_datafeed_logic *logic;
664         const struct sr_datafeed_analog *analog;
665
666         *out = NULL;
667         if (!o || !o->sdi)
668                 return SR_ERR_ARG;
669         if (!(ctx = o->priv))
670                 return SR_ERR_ARG;
671
672         sr_dbg("Got packet of type %d", packet->type);
673         switch (packet->type) {
674         case SR_DF_HEADER:
675                 ctx->have_checked = FALSE;
676                 ctx->have_frames = FALSE;
677                 ctx->pkt_snums = FALSE;
678                 *out = gen_header(o, packet->payload);
679                 break;
680         case SR_DF_TRIGGER:
681                 ctx->trigger = TRUE;
682                 break;
683         case SR_DF_LOGIC:
684                 *out = g_string_sized_new(512);
685                 logic = packet->payload;
686                 ctx->pkt_snums = logic->length;
687                 ctx->pkt_snums /= logic->length;
688                 check_input_constraints(ctx);
689                 process_logic(ctx, logic);
690                 break;
691         case SR_DF_ANALOG:
692                 *out = g_string_sized_new(512);
693                 analog = packet->payload;
694                 ctx->pkt_snums = analog->num_samples;
695                 ctx->pkt_snums /= g_slist_length(analog->meaning->channels);
696                 check_input_constraints(ctx);
697                 process_analog(ctx, analog);
698                 break;
699         case SR_DF_FRAME_BEGIN:
700                 ctx->have_frames = TRUE;
701                 *out = g_string_new(ctx->frame);
702                 /* Fallthrough */
703         case SR_DF_END:
704                 /* Got to end of frame/session with part of the data. */
705                 if (ctx->channels_seen)
706                         ctx->channels_seen = ctx->channel_count;
707                 if (*ctx->gnuplot)
708                         save_gnuplot(ctx);
709                 break;
710         }
711
712         /* If we've got them all, dump the values. */
713         if (ctx->channels_seen >= ctx->channel_count)
714                 dump_saved_values(ctx, out);
715
716         return SR_OK;
717 }
718
719 static int cleanup(struct sr_output *o)
720 {
721         struct context *ctx;
722
723         if (!o || !o->sdi)
724                 return SR_ERR_ARG;
725
726         if (o->priv) {
727                 ctx = o->priv;
728                 g_free((gpointer)ctx->record);
729                 g_free((gpointer)ctx->frame);
730                 g_free((gpointer)ctx->comment);
731                 g_free((gpointer)ctx->gnuplot);
732                 g_free((gpointer)ctx->value);
733                 g_free(ctx->previous_sample);
734                 g_free(ctx->channels);
735                 g_free(o->priv);
736                 o->priv = NULL;
737         }
738
739         return SR_OK;
740 }
741
742 static struct sr_option options[] = {
743         {"gnuplot", "gnuplot", "gnuplot script file name", NULL, NULL},
744         {"scale", "scale", "Scale gnuplot graphs", NULL, NULL},
745         {"value", "Value separator", "Character to print between values", NULL, NULL},
746         {"record", "Record separator", "String to print between records", NULL, NULL},
747         {"frame", "Frame separator", "String to print between frames", NULL, NULL},
748         {"comment", "Comment start string", "String used at start of comment lines", NULL, NULL},
749         {"header", "Output header", "Output header comment with capture metdata", NULL, NULL},
750         {"label", "Label values", "Type of column labels", NULL, NULL},
751         {"time", "Time column", "Output sample time as column 1", NULL, NULL},
752         {"trigger", "Trigger column", "Output trigger indicator as last column ", NULL, NULL},
753         {"dedup", "Dedup rows", "Set to false to output duplicate rows", NULL, NULL},
754         ALL_ZERO
755 };
756
757 static const struct sr_option *get_options(void)
758 {
759         GSList *l = NULL;
760
761         if (!options[0].def) {
762                 options[0].def = g_variant_ref_sink(g_variant_new_string(""));
763                 options[1].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
764                 options[2].def = g_variant_ref_sink(g_variant_new_string(","));
765                 options[3].def = g_variant_ref_sink(g_variant_new_string("\n"));
766                 options[4].def = g_variant_ref_sink(g_variant_new_string("\n"));
767                 options[5].def = g_variant_ref_sink(g_variant_new_string(";"));
768                 options[6].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
769                 options[7].def = g_variant_ref_sink(g_variant_new_string("units"));
770                 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("units")));
771                 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("channel")));
772                 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("off")));
773                 options[7].values = l;
774                 options[8].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
775                 options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
776                 options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
777         }
778
779         return options;
780 }
781
782 SR_PRIV struct sr_output_module output_csv = {
783         .id = "csv",
784         .name = "CSV",
785         .desc = "Comma-separated values",
786         .exts = (const char *[]){"csv", NULL},
787         .flags = 0,
788         .options = get_options,
789         .init = init,
790         .receive = receive,
791         .cleanup = cleanup,
792 };