]> sigrok.org Git - libsigrok.git/blob - src/output/csv.c
output/analog: Fix key order, add missing items.
[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, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <glib.h>
24 #include <libsigrok/libsigrok.h>
25 #include "libsigrok-internal.h"
26
27 #define LOG_PREFIX "output/csv"
28
29 struct context {
30         unsigned int num_enabled_channels;
31         uint64_t samplerate;
32         char separator;
33         gboolean header_done;
34         struct sr_channel **channels;
35
36         /* For analog measurements split into frames, not packets. */
37         struct sr_channel **analog_channels;
38         float *analog_vals; /* Analog values stored until the end of the frame. */
39         unsigned int num_analog_channels;
40         gboolean inframe;
41 };
42
43 /*
44  * TODO:
45  *  - Option to specify delimiter character and/or string.
46  *  - Option to (not) print metadata as comments.
47  *  - Option to specify the comment character(s), e.g. # or ; or C/C++-style.
48  *  - Option to (not) print samplenumber / time as extra column.
49  *  - Option to "compress" output (only print changed samples, VCD-like).
50  *  - Option to print comma-separated bits, or whole bytes/words (for 8/16
51  *    channel LAs) as ASCII/hex etc. etc.
52  *  - Trigger support.
53  */
54
55 static int init(struct sr_output *o, GHashTable *options)
56 {
57         struct context *ctx;
58         struct sr_channel *ch;
59         GSList *l;
60         int i, j;
61
62         (void)options;
63
64         if (!o || !o->sdi)
65                 return SR_ERR_ARG;
66
67         ctx = g_malloc0(sizeof(struct context));
68         o->priv = ctx;
69         ctx->separator = ',';
70
71         /* Get the number of channels, and the unitsize. */
72         for (l = o->sdi->channels; l; l = l->next) {
73                 ch = l->data;
74                 if (ch->enabled) {
75                         if (ch->type == SR_CHANNEL_LOGIC ||
76                             ch->type == SR_CHANNEL_ANALOG)
77                                 ctx->num_enabled_channels++;
78                         if (ch->type == SR_CHANNEL_ANALOG)
79                                 ctx->num_analog_channels++;
80                 }
81         }
82         ctx->channels = g_malloc(sizeof(struct sr_channel *)
83                                         * ctx->num_enabled_channels);
84         ctx->analog_channels = g_malloc(sizeof(struct sr_channel *)
85                                         * ctx->num_analog_channels);
86         ctx->analog_vals = g_malloc(sizeof(float) * ctx->num_analog_channels);
87
88         /* Once more to map the enabled channels. */
89         for (i = 0, l = o->sdi->channels, j = 0; l; l = l->next) {
90                 ch = l->data;
91                 if (ch->enabled) {
92                         if (ch->type == SR_CHANNEL_LOGIC ||
93                             ch->type == SR_CHANNEL_ANALOG)
94                                 ctx->channels[i++] = ch;
95                         if (ch->type == SR_CHANNEL_ANALOG)
96                                 ctx->analog_channels[j++] = ch;
97                 }
98
99         }
100
101         return SR_OK;
102 }
103
104 static GString *gen_header(const struct sr_output *o)
105 {
106         struct context *ctx;
107         struct sr_channel *ch;
108         GVariant *gvar;
109         GString *header;
110         GSList *l;
111         time_t t;
112         int num_channels, i;
113         char *samplerate_s;
114
115         ctx = o->priv;
116         header = g_string_sized_new(512);
117
118         /* Some metadata */
119         t = time(NULL);
120         g_string_append_printf(header, "; CSV, generated by %s %s on %s",
121                         PACKAGE_NAME, SR_PACKAGE_VERSION_STRING, ctime(&t));
122
123         /* Columns / channels */
124         num_channels = g_slist_length(o->sdi->channels);
125         g_string_append_printf(header, "; Channels (%d/%d):",
126                         ctx->num_enabled_channels, num_channels);
127         for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
128                 ch = l->data;
129                 if (ch->enabled &&
130                     (ch->type == SR_CHANNEL_LOGIC ||
131                      ch->type == SR_CHANNEL_ANALOG))
132                         g_string_append_printf(header, " %s,", ch->name);
133         }
134         if (o->sdi->channels)
135                 /* Drop last separator. */
136                 g_string_truncate(header, header->len - 1);
137         g_string_append_printf(header, "\n");
138
139         if (ctx->samplerate == 0) {
140                 if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
141                                 &gvar) == SR_OK) {
142                         ctx->samplerate = g_variant_get_uint64(gvar);
143                         g_variant_unref(gvar);
144                 }
145         }
146         if (ctx->samplerate != 0) {
147                 samplerate_s = sr_samplerate_string(ctx->samplerate);
148                 g_string_append_printf(header, "; Samplerate: %s\n", samplerate_s);
149                 g_free(samplerate_s);
150         }
151
152         return header;
153 }
154
155 static void init_output(GString **out, struct context *ctx,
156                         const struct sr_output *o)
157 {
158         if (!ctx->header_done) {
159                 *out = gen_header(o);
160                 ctx->header_done = TRUE;
161         } else {
162                 *out = g_string_sized_new(512);
163         }
164 }
165
166 static void handle_analog_frame(struct context *ctx,
167                                 const struct sr_datafeed_analog *analog)
168 {
169         unsigned int numch, nums, i, j, s;
170         GSList *l;
171
172         numch = g_slist_length(analog->channels);
173         if ((unsigned int)analog->num_samples > numch)
174                 nums = analog->num_samples / numch;
175         else
176                 nums = 1;
177
178         s = 0;
179         l = analog->channels;
180         for (i = 0; i < nums; i++) {
181                 for (j = 0; j < ctx->num_analog_channels; j++) {
182                         if (ctx->analog_channels[j] == l->data)
183                                 ctx->analog_vals[j] = analog->data[s++];
184                 }
185                 l = l->next;
186         }
187 }
188
189 static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
190                 GString **out)
191 {
192         const struct sr_datafeed_meta *meta;
193         const struct sr_datafeed_logic *logic;
194         const struct sr_datafeed_analog *analog;
195         const struct sr_config *src;
196         GSList *l;
197         struct context *ctx;
198         int idx;
199         uint64_t i, j, k, nums, numch;
200         gchar *p, c;
201         int ret = SR_OK;
202
203         *out = NULL;
204         if (!o || !o->sdi)
205                 return SR_ERR_ARG;
206         if (!(ctx = o->priv))
207                 return SR_ERR_ARG;
208
209         switch (packet->type) {
210         case SR_DF_META:
211                 meta = packet->payload;
212                 for (l = meta->config; l; l = l->next) {
213                         src = l->data;
214                         if (src->key != SR_CONF_SAMPLERATE)
215                                 continue;
216                         ctx->samplerate = g_variant_get_uint64(src->data);
217                 }
218                 break;
219         case SR_DF_FRAME_BEGIN:
220                 /*
221                  * Special case - we start gathering data from analog channels
222                  * and wait for SR_DF_FRAME_END to dump it.
223                  */
224                 memset(ctx->analog_vals, 0, sizeof(float) * ctx->num_analog_channels);
225                 ctx->inframe = TRUE;
226                 ret = SR_OK_CONTINUE;
227                 break;
228         case SR_DF_FRAME_END:
229                 /*
230                  * Dump gathered data.
231                  */
232                 init_output(out, ctx, o);
233
234                 for (i = 0, j = 0; i < ctx->num_enabled_channels; i++) {
235                         if (ctx->channels[i]->type == SR_CHANNEL_ANALOG) {
236                                 g_string_append_printf(*out, "%f",
237                                                         ctx->analog_vals[j++]);
238                         }
239                         g_string_append_c(*out, ctx->separator);
240                 }
241                 g_string_truncate(*out, (*out)->len - 1);
242                 g_string_append_printf(*out, "\n");
243
244                 ctx->inframe = FALSE;
245                 break;
246         case SR_DF_LOGIC:
247                 logic = packet->payload;
248                 init_output(out, ctx, o);
249
250                 for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
251                         for (j = 0; j < ctx->num_enabled_channels; j++) {
252                                 if (ctx->channels[j]->type == SR_CHANNEL_LOGIC) {
253                                         idx = ctx->channels[j]->index;
254                                         p = logic->data + i + idx / 8;
255                                         c = *p & (1 << (idx % 8));
256                                         g_string_append_c(*out, c ? '1' : '0');
257                                 }
258                                 g_string_append_c(*out, ctx->separator);
259                         }
260                         if (j) {
261                                 /* Drop last separator. */
262                                 g_string_truncate(*out, (*out)->len - 1);
263                         }
264                         g_string_append_printf(*out, "\n");
265                 }
266                 break;
267         case SR_DF_ANALOG:
268                 analog = packet->payload;
269
270                 if (ctx->inframe) {
271                         handle_analog_frame(ctx, analog);
272                         ret = SR_OK_CONTINUE;
273                         break;
274                 }
275
276                 init_output(out, ctx, o);
277                 k = 0;
278                 l = NULL;
279
280                 numch = g_slist_length(analog->channels);
281                 if ((unsigned int)analog->num_samples > numch)
282                         nums = analog->num_samples / numch;
283                 else
284                         nums = 1;
285
286                 for (i = 0; i < nums; i++) {
287                         for (j = 0; j < ctx->num_enabled_channels; j++) {
288                                 if (ctx->channels[j]->type == SR_CHANNEL_ANALOG) {
289                                         if (!l)
290                                                 l = analog->channels;
291
292                                         if (ctx->channels[j] == l->data) {
293                                                 g_string_append_printf(*out,
294                                                         "%f", analog->data[k++]);
295                                         }
296
297                                         l = l->next;
298                                 }
299                                 g_string_append_c(*out, ctx->separator);
300                         }
301                         g_string_truncate(*out, (*out)->len - 1);
302                         g_string_append_printf(*out, "\n");
303                 }
304                 break;
305         /* TODO case SR_DF_ANALOG2: */
306         }
307
308         return ret;
309 }
310
311 static int cleanup(struct sr_output *o)
312 {
313         struct context *ctx;
314
315         if (!o || !o->sdi)
316                 return SR_ERR_ARG;
317
318         if (o->priv) {
319                 ctx = o->priv;
320                 g_free(ctx->channels);
321                 g_free(ctx->analog_channels);
322                 g_free(ctx->analog_vals);
323                 g_free(o->priv);
324                 o->priv = NULL;
325         }
326
327         return SR_OK;
328 }
329
330 SR_PRIV struct sr_output_module output_csv = {
331         .id = "csv",
332         .name = "CSV",
333         .desc = "Comma-separated values",
334         .exts = (const char*[]){"csv", NULL},
335         .flags = 0,
336         .options = NULL,
337         .init = init,
338         .receive = receive,
339         .cleanup = cleanup,
340 };