]> sigrok.org Git - libsigrok.git/blame - src/output/wavedrom.c
uni-t-ut181a: silence compiler warning, use of uninitialized variable
[libsigrok.git] / src / output / wavedrom.c
CommitLineData
40f812f5
MJ
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
dd5735c9 27struct context {
40f812f5 28 uint32_t channel_count;
40f812f5 29 struct sr_channel **channels;
dd5735c9 30 GString **channel_outputs; /* output strings */
40f812f5
MJ
31};
32
dd5735c9 33/* Converts accumulated output data to a JSON string. */
40f812f5
MJ
34static GString *wavedrom_render(const struct context *ctx)
35{
dd5735c9
GS
36 GString *output;
37 size_t ch, i;
38 char last_char, curr_char;
40f812f5 39
dd5735c9 40 output = g_string_new("{ \"signal\": [");
40f812f5
MJ
41 for (ch = 0; ch < ctx->channel_count; ch++) {
42 if (!ctx->channel_outputs[ch])
43 continue;
44
dd5735c9 45 /* Channel strip. */
40f812f5
MJ
46 g_string_append_printf(output,
47 "{ \"name\": \"%s\", \"wave\": \"", ctx->channels[ch]->name);
48
dd5735c9 49 last_char = 0;
40f812f5 50 for (i = 0; i < ctx->channel_outputs[ch]->len; i++) {
dd5735c9
GS
51 curr_char = ctx->channel_outputs[ch]->str[i];
52 /* Data point. */
53 if (curr_char == last_char) {
40f812f5
MJ
54 g_string_append_c(output, '.');
55 } else {
dd5735c9
GS
56 g_string_append_c(output, curr_char);
57 last_char = curr_char;
40f812f5
MJ
58 }
59 }
60 if (ch < ctx->channel_count - 1) {
61 g_string_append(output, "\" },");
62 } else {
dd5735c9 63 /* Last channel, no comma. */
40f812f5
MJ
64 g_string_append(output, "\" }");
65 }
66 }
40f812f5 67 g_string_append(output, "], \"config\": { \"skin\": \"narrow\" }}");
dd5735c9 68
40f812f5
MJ
69 return output;
70}
71
7a0d1bdc
GS
72static void process_logic(const struct context *ctx,
73 const struct sr_datafeed_logic *logic)
74{
75 size_t sample_count, ch, i;
38950645
GS
76 uint8_t *sample, bit;
77 GString *accu;
7a0d1bdc 78
38950645
GS
79 if (!ctx->channel_count)
80 return;
7a0d1bdc
GS
81
82 /*
83 * Extract the logic bits for each channel and store them
84 * as wavedrom letters (1/0) in each channel's text string.
38950645
GS
85 * This transforms the input which consists of sample sets
86 * that span multiple channels into output stripes per logic
87 * channel which consist of bits for that individual channel.
88 *
89 * TODO Reduce memory consumption during accumulation of
90 * output data.
91 *
92 * Ideally we'd accumulate binary chunks, and defer conversion
93 * to the text format. Analog data already won't get here, only
94 * logic data does. When the per-channel transformation also
95 * gets deferred until later, then the only overhead would be
96 * for disabled logic channels. Which may be acceptable or even
97 * negligable.
98 *
99 * An optional addition to the above mentioned accumulation of
100 * binary data is RLE compression. Mark both the position in the
101 * accumulated data as well as a repetition counter, instead of
102 * repeatedly storing the same sample set. The efficiency of
103 * this approach of course depends on the change rate of input
104 * data. But the approach perfectly matches the WaveDrom syntax
105 * for repeated bit patterns, and thus is easily handled in the
106 * text rendering stage of the output module.
7a0d1bdc 107 */
38950645
GS
108 sample_count = logic->length / logic->unitsize;
109 for (i = 0; i < sample_count; i++) {
110 sample = logic->data + i * logic->unitsize;
111 for (ch = 0; ch < ctx->channel_count; ch++) {
112 accu = ctx->channel_outputs[ch];
113 if (!accu)
114 continue;
115 bit = sample[ch / 8] & (1 << (ch % 8));
116 g_string_append_c(accu, bit ? '1' : '0');
7a0d1bdc
GS
117 }
118 }
119}
120
121static int receive(const struct sr_output *o,
122 const struct sr_datafeed_packet *packet, GString **out)
123{
124 struct context *ctx;
125
126 *out = NULL;
127
128 if (!o || !o->sdi || !o->priv)
129 return SR_ERR_ARG;
130
131 ctx = o->priv;
132
133 switch (packet->type) {
134 case SR_DF_LOGIC:
135 process_logic(ctx, packet->payload);
136 break;
137 case SR_DF_END:
138 *out = wavedrom_render(ctx);
139 break;
140 }
141
142 return SR_OK;
143}
144
40f812f5
MJ
145static int init(struct sr_output *o, GHashTable *options)
146{
147 struct context *ctx;
148 struct sr_channel *channel;
149 GSList *l;
dd5735c9 150 size_t i;
40f812f5
MJ
151
152 (void)options;
153
154 if (!o || !o->sdi)
155 return SR_ERR_ARG;
156
dd5735c9 157 o->priv = ctx = g_malloc0(sizeof(*ctx));
40f812f5
MJ
158
159 ctx->channel_count = g_slist_length(o->sdi->channels);
dd5735c9
GS
160 ctx->channels = g_malloc0(
161 sizeof(ctx->channels[0]) * ctx->channel_count);
162 ctx->channel_outputs = g_malloc0(
163 sizeof(ctx->channel_outputs[0]) * ctx->channel_count);
40f812f5
MJ
164
165 for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
166 channel = l->data;
dd5735c9 167 if (channel->enabled && channel->type == SR_CHANNEL_LOGIC) {
40f812f5
MJ
168 ctx->channels[i] = channel;
169 ctx->channel_outputs[i] = g_string_new(NULL);
40f812f5
MJ
170 }
171 }
172
173 return SR_OK;
174}
175
176static int cleanup(struct sr_output *o)
177{
178 struct context *ctx;
dd5735c9 179 GString *s;
40f812f5
MJ
180
181 if (!o)
182 return SR_ERR_ARG;
183
184 ctx = o->priv;
185 o->priv = NULL;
186
187 if (ctx) {
dd5735c9
GS
188 while (--ctx->channel_count) {
189 s = ctx->channel_outputs[ctx->channel_count];
190 if (s)
191 g_string_free(s, TRUE);
40f812f5
MJ
192 }
193 g_free(ctx->channel_outputs);
194 g_free(ctx->channels);
195 g_free(ctx);
196 }
197
198 return SR_OK;
199}
200
40f812f5
MJ
201SR_PRIV struct sr_output_module output_wavedrom = {
202 .id = "wavedrom",
dd5735c9 203 .name = "WaveDrom",
40f812f5
MJ
204 .desc = "WaveDrom.com file format",
205 .exts = (const char *[]){"wavedrom", "json", NULL},
206 .flags = 0,
207 .options = NULL,
208 .init = init,
209 .receive = receive,
210 .cleanup = cleanup,
211};