]> sigrok.org Git - libsigrok.git/blob - src/output/wavedrom.c
output/wavedrom: introduce a wavedrom output module
[libsigrok.git] / src / output / wavedrom.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2019 Marc Jacobi <obiwanjacobi@hotmail.com>
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 3 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 #include <config.h>
21 #include <string.h>
22 #include <libsigrok/libsigrok.h>
23 #include "libsigrok-internal.h"
24
25 #define LOG_PREFIX "output/wavedrom"
26
27 struct context
28 {
29         uint32_t channel_count;
30         // TODO: remove this, channels are available with each call
31         struct sr_channel **channels;
32
33         // output strings
34         GString **channel_outputs;
35 };
36
37 // takes all data collected in context and
38 // renders it all to a JSON string.
39 static GString *wavedrom_render(const struct context *ctx)
40 {
41         // open
42         GString *output = g_string_new("{ \"signal\": [");
43         uint32_t ch, i;
44         char lastChar, currentChar;
45
46         for (ch = 0; ch < ctx->channel_count; ch++) {
47                 if (!ctx->channel_outputs[ch])
48                         continue;
49
50                 // channel strip
51                 g_string_append_printf(output,
52                         "{ \"name\": \"%s\", \"wave\": \"", ctx->channels[ch]->name);
53
54                 lastChar = 0;
55                 for (i = 0; i < ctx->channel_outputs[ch]->len; i++) {
56                         currentChar = ctx->channel_outputs[ch]->str[i];
57                         // data point
58                         if (currentChar == lastChar) {
59                                 g_string_append_c(output, '.');
60                         } else {
61                                 g_string_append_c(output, currentChar);
62                                 lastChar = currentChar;
63                         }
64                 }
65                 if (ch < ctx->channel_count - 1) {
66                         g_string_append(output, "\" },");
67                 } else {
68                         // last channel - no comma
69                         g_string_append(output, "\" }");
70                 }
71         }
72
73         // close
74         g_string_append(output, "], \"config\": { \"skin\": \"narrow\" }}");
75         return output;
76 }
77
78 static int init(struct sr_output *o, GHashTable *options)
79 {
80         struct context *ctx;
81         struct sr_channel *channel;
82         GSList *l;
83         uint32_t i;
84
85         (void)options;
86
87         if (!o || !o->sdi)
88                 return SR_ERR_ARG;
89
90         o->priv = ctx = g_malloc(sizeof(struct context));
91
92         ctx->channel_count = g_slist_length(o->sdi->channels);
93         ctx->channels = g_malloc(
94                 sizeof(struct sr_channel) * ctx->channel_count);
95         ctx->channel_outputs = g_malloc(
96                 sizeof(GString *) * ctx->channel_count);
97
98         for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
99                 channel = l->data;
100                 if (channel->enabled &&
101                         channel->type == SR_CHANNEL_LOGIC) {
102                         ctx->channels[i] = channel;
103                         ctx->channel_outputs[i] = g_string_new(NULL);
104                 } else {
105                         ctx->channels[i] = NULL;
106                         ctx->channel_outputs[i] = NULL;
107                 }
108         }
109
110         return SR_OK;
111 }
112
113 static int cleanup(struct sr_output *o)
114 {
115         struct context *ctx;
116         uint32_t i;
117
118         if (!o)
119                 return SR_ERR_ARG;
120
121         ctx = o->priv;
122         o->priv = NULL;
123
124         if (ctx) {
125                 for (i = 0; i < ctx->channel_count; i++) {
126                         if (ctx->channel_outputs[i])
127                                 g_string_free(ctx->channel_outputs[i], TRUE);
128                 }
129                 g_free(ctx->channel_outputs);
130                 g_free(ctx->channels);
131                 g_free(ctx);
132         }
133
134         return SR_OK;
135 }
136
137 static void process_logic(const struct context *ctx,
138                           const struct sr_datafeed_logic *logic)
139 {
140         unsigned int sample_count, ch, i;
141         uint8_t *sample;
142
143         sample_count = logic->length / logic->unitsize;
144
145         // extract the logic bits for each channel and
146         // store them as wavedrom letters (1/0) in each channel's string
147         for (ch = 0; ch < ctx->channel_count; ch++) {
148                 if (ctx->channels[ch]) {
149                         for (i = 0; i < sample_count; i++) {
150                                 sample = logic->data + i * logic->unitsize;
151
152                                 if (ctx->channel_outputs[ch]) {
153                                         g_string_append_c(ctx->channel_outputs[ch],
154                                                                           sample[ch / 8] & (1 << (ch % 8)) ? '1' : '0');
155                                 }
156                         }
157                 }
158         }
159 }
160
161 static int receive(const struct sr_output *o,
162                    const struct sr_datafeed_packet *packet, GString **out)
163 {
164         struct context *ctx;
165
166         *out = NULL;
167
168         if (!o || !o->sdi || !o->priv)
169                 return SR_ERR_ARG;
170
171         ctx = o->priv;
172
173         switch (packet->type) {
174         case SR_DF_LOGIC:
175                 process_logic(ctx, packet->payload);
176                 break;
177         case SR_DF_END:
178                 *out = wavedrom_render(ctx);
179                 break;
180         }
181
182         return SR_OK;
183 }
184
185 SR_PRIV struct sr_output_module output_wavedrom = {
186         .id = "wavedrom",
187         .name = "WAVEDROM",
188         .desc = "WaveDrom.com file format",
189         .exts = (const char *[]){"wavedrom", "json", NULL},
190         .flags = 0,
191         .options = NULL,
192         .init = init,
193         .receive = receive,
194         .cleanup = cleanup,
195 };