]> sigrok.org Git - libsigrok.git/blame - src/output/wavedrom.c
output/wavedrom: introduce a wavedrom output module
[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
27struct 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.
39static 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
78static 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
113static 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
137static 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
161static 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
185SR_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};