]> sigrok.org Git - libsigrok.git/blobdiff - src/output/csv.c
Don't reference SR_PACKAGE_VERSION_STRING directly in output modules.
[libsigrok.git] / src / output / csv.c
index 6900200db1b0e63994895828d2e6fb2fb7f9d8ba..1ff4e3a7f7fe2a4d5dbfc433e4329ba6661c35cf 100644 (file)
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
@@ -41,8 +40,8 @@
  *
  * header:  Print header comment with capture metadata. Defaults to TRUE.
  *
- * label:   Add a line of channel labels as the first line of output. Defaults
- *          to TRUE.
+ * label:   What to use for channel labels as the first line of output.
+ *          Values are "channel", "units", "off". Defaults to "units".
  *
  * time:    Whether or not the first column should include the time the sample
  *          was taken. Defaults to TRUE.
  * trigger: Whether or not to add a "trigger" column as the last column.
  *          Defaults to FALSE.
  *
- * dedup:   Don't output duplicate rows. Defaults to TRUE. If time is off, then
+ * dedup:   Don't output duplicate rows. Defaults to FALSE. If time is off, then
  *          this is forced to be off.
  */
 
-#include <math.h>
 #include <config.h>
+#include <math.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
@@ -66,6 +65,7 @@
 
 struct ctx_channel {
        struct sr_channel *ch;
+       char *label;
        float min, max;
 };
 
@@ -78,7 +78,7 @@ struct context {
        const char *frame;
        const char *comment;
        gboolean header, did_header;
-       gboolean label, did_label;
+       gboolean label_do, label_did, label_names;
        gboolean time;
        gboolean do_trigger;
        gboolean dedup;
@@ -113,6 +113,7 @@ static int init(struct sr_output *o, GHashTable *options)
        unsigned int i, analog_channels, logic_channels;
        struct context *ctx;
        struct sr_channel *ch;
+       const char *label_string;
        GSList *l;
 
        if (!o || !o->sdi)
@@ -136,7 +137,8 @@ static int init(struct sr_output *o, GHashTable *options)
        ctx->header = g_variant_get_boolean(g_hash_table_lookup(options, "header"));
        ctx->time = g_variant_get_boolean(g_hash_table_lookup(options, "time"));
        ctx->do_trigger = g_variant_get_boolean(g_hash_table_lookup(options, "trigger"));
-       ctx->label = g_variant_get_boolean(g_hash_table_lookup(options, "label"));
+       label_string = g_variant_get_string(
+               g_hash_table_lookup(options, "label"), NULL);
        ctx->dedup = g_variant_get_boolean(g_hash_table_lookup(options, "dedup"));
        ctx->dedup &= ctx->time;
 
@@ -146,11 +148,15 @@ static int init(struct sr_output *o, GHashTable *options)
        if (*ctx->gnuplot && strlen(ctx->value) > 1)
                sr_warn("gnuplot doesn't support multichar value separators.");
 
+       if ((ctx->label_did = ctx->label_do = g_strcmp0(label_string, "off") != 0))
+               ctx->label_names = g_strcmp0(label_string, "units") != 0;
+
        sr_dbg("gnuplot = '%s', scale = %d", ctx->gnuplot, ctx->scale);
        sr_dbg("value = '%s', record = '%s', frame = '%s', comment = '%s'",
               ctx->value, ctx->record, ctx->frame, ctx->comment);
-       sr_dbg("header = %d, label = %d, time = %d, do_trigger = %d, dedup = %d",
-              ctx->header, ctx->label, ctx->time, ctx->do_trigger, ctx->dedup);
+       sr_dbg("header = %d, time = %d, do_trigger = %d, dedup = %d",
+              ctx->header, ctx->time, ctx->do_trigger, ctx->dedup);
+       sr_dbg("label_do = %d, label_names = %d", ctx->label_do, ctx->label_names);
 
        analog_channels = logic_channels = 0;
        /* Get the number of channels, and the unitsize. */
@@ -189,6 +195,8 @@ static int init(struct sr_output *o, GHashTable *options)
                        } else {
                                sr_warn("Unknown channel type %d.", ch->type);
                        }
+                       if (ctx->label_do && ctx->label_names)
+                               ctx->channels[i].label = ch->name;
                        ctx->channels[i++].ch = ch;
                }
        }
@@ -208,7 +216,7 @@ static GString *gen_header(const struct sr_output *o,
        struct sr_channel *ch;
        GVariant *gvar;
        GString *header;
-       GSList *l;
+       GSList *channels, *l;
        unsigned int num_channels, i;
        uint64_t samplerate = 0, sr;
        char *samplerate_s;
@@ -244,22 +252,24 @@ static GString *gen_header(const struct sr_output *o,
                g_string_append_printf(header,
                        "%s CSV generated by %s %s\n%s from %s on %s",
                        ctx->comment, PACKAGE_NAME,
-                       SR_PACKAGE_VERSION_STRING, ctx->comment,
+                       sr_package_version_string_get(), ctx->comment,
                        ctx->title, ctime(&hdr->starttime.tv_sec));
 
                /* Columns / channels */
-               num_channels = g_slist_length(o->sdi->channels);
+               channels = o->sdi ? o->sdi->channels : NULL;
+               num_channels = g_slist_length(channels);
                g_string_append_printf(header, "%s Channels (%d/%d):",
                        ctx->comment, ctx->num_analog_channels +
                        ctx->num_logic_channels, num_channels);
-               for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
+               for (l = channels; l; l = l->next) {
                        ch = l->data;
                        if (ch->enabled)
                                g_string_append_printf(header, " %s,", ch->name);
                }
-               if (o->sdi->channels)
+               if (channels) {
                        /* Drop last separator. */
                        g_string_truncate(header, header->len - 1);
+               }
                g_string_append_printf(header, "\n");
                if (samplerate != 0) {
                        samplerate_s = sr_samplerate_string(samplerate);
@@ -300,10 +310,13 @@ static void process_analog(struct context *ctx,
                           const struct sr_datafeed_analog *analog)
 {
        int ret;
-       unsigned int i, j, c, num_channels;
+       size_t num_rcvd_ch, num_have_ch;
+       size_t idx_have, idx_smpl, idx_rcvd;
+       size_t idx_send;
        struct sr_analog_meaning *meaning;
        GSList *l;
        float *fdata = NULL;
+       struct sr_channel *ch;
 
        if (!ctx->analog_samples) {
                ctx->analog_samples = g_malloc(analog->num_samples
@@ -316,27 +329,34 @@ static void process_analog(struct context *ctx,
                        ctx->num_samples, analog->num_samples);
 
        meaning = analog->meaning;
-       num_channels = g_slist_length(meaning->channels);
-       ctx->channels_seen += num_channels;
-       sr_dbg("Processing packet of %u analog channels", num_channels);
-       fdata = g_malloc(analog->num_samples * num_channels);
+       num_rcvd_ch = g_slist_length(meaning->channels);
+       ctx->channels_seen += num_rcvd_ch;
+       sr_dbg("Processing packet of %zu analog channels", num_rcvd_ch);
+       fdata = g_malloc(analog->num_samples * num_rcvd_ch * sizeof(float));
        if ((ret = sr_analog_to_float(analog, fdata)) != SR_OK)
                sr_warn("Problems converting data to floating point values.");
 
-       for (i = 0; i < ctx->num_analog_channels + ctx->num_logic_channels; i++) {
-               if (ctx->channels[i].ch->type == SR_CHANNEL_ANALOG) {
-                       sr_dbg("Looking for channel %s",
-                              ctx->channels[i].ch->name);
-                       for (l = meaning->channels, c = 0; l; l = l->next, c++) {
-                               struct sr_channel *ch = l->data;
-                               sr_dbg("Checking %s", ch->name);
-                               if (ctx->channels[i].ch == l->data) {
-                                       for (j = 0; j < analog->num_samples; j++)
-                                               ctx->analog_samples[j * ctx->num_analog_channels + i] = fdata[j * num_channels + c];
-                                       break;
-                               }
+       num_have_ch = ctx->num_analog_channels + ctx->num_logic_channels;
+       idx_send = 0;
+       for (idx_have = 0; idx_have < num_have_ch; idx_have++) {
+               if (ctx->channels[idx_have].ch->type != SR_CHANNEL_ANALOG)
+                       continue;
+               sr_dbg("Looking for channel %s",
+                      ctx->channels[idx_have].ch->name);
+               for (l = meaning->channels, idx_rcvd = 0; l; l = l->next, idx_rcvd++) {
+                       ch = l->data;
+                       sr_dbg("Checking %s", ch->name);
+                       if (ctx->channels[idx_have].ch != ch)
+                               continue;
+                       if (ctx->label_do && !ctx->label_names) {
+                               sr_analog_unit_to_string(analog,
+                                       &ctx->channels[idx_have].label);
                        }
+                       for (idx_smpl = 0; idx_smpl < analog->num_samples; idx_smpl++)
+                               ctx->analog_samples[idx_smpl * ctx->num_analog_channels + idx_send] = fdata[idx_smpl * num_rcvd_ch + idx_rcvd];
+                       break;
                }
+               idx_send++;
        }
        g_free(fdata);
 }
@@ -366,9 +386,11 @@ static void process_logic(struct context *ctx,
 
        for (j = ch = 0; ch < ctx->num_logic_channels; j++) {
                if (ctx->channels[j].ch->type == SR_CHANNEL_LOGIC) {
-                       for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
-                               sample = logic->data + i;
-                               idx = ctx->channels[ch].ch->index;
+                       for (i = 0; i < num_samples; i++) {
+                               sample = logic->data + i * logic->unitsize;
+                               idx = ctx->channels[j].ch->index;
+                               if (ctx->label_do && !ctx->label_names)
+                                       ctx->channels[j].label = "logic";
                                ctx->logic_samples[i * ctx->num_logic_channels + ch] = sample[idx / 8] & (1 << (idx % 8));
                        }
                        ch++;
@@ -393,13 +415,17 @@ static void dump_saved_values(struct context *ctx, GString **out)
                num_channels =
                    ctx->num_logic_channels + ctx->num_analog_channels;
 
-               if (ctx->label && !ctx->did_label) {
+               if (ctx->label_do) {
                        if (ctx->time)
-                               g_string_append_printf(*out, "Time%s",
-                                                      ctx->value);
+                               g_string_append_printf(*out, "%s%s",
+                                       ctx->label_names ? "Time" :
+                                       ctx->xlabel, ctx->value);
                        for (i = 0; i < num_channels; i++) {
                                g_string_append_printf(*out, "%s%s",
-                                       ctx->channels[i].ch->name, ctx->value);
+                                       ctx->channels[i].label, ctx->value);
+                               if (ctx->channels[i].ch->type == SR_CHANNEL_ANALOG
+                                               && ctx->label_names)
+                                       g_free(ctx->channels[i].label);
                        }
                        if (ctx->do_trigger)
                                g_string_append_printf(*out, "Trigger%s",
@@ -408,7 +434,7 @@ static void dump_saved_values(struct context *ctx, GString **out)
                        g_string_truncate(*out, (*out)->len - 1);
                        g_string_append(*out, ctx->record);
 
-                       ctx->did_label = TRUE;
+                       ctx->label_do = FALSE;
                }
 
                analog_size = ctx->num_analog_channels * sizeof(float);
@@ -439,7 +465,7 @@ static void dump_saved_values(struct context *ctx, GString **out)
                        }
 
                        if (ctx->time)
-                               g_string_append_printf(*out, "%lu%s",
+                               g_string_append_printf(*out, "%" PRIu64 "%s",
                                        ctx->sample_time, ctx->value);
 
                        for (j = 0; j < num_channels; j++) {
@@ -490,7 +516,7 @@ static void save_gnuplot(struct context *ctx)
        script = g_string_sized_new(512);
        g_string_append_printf(script, "set datafile separator '%s'\n",
                               ctx->value);
-       if (ctx->did_label)
+       if (ctx->label_did)
                g_string_append(script, "set key autotitle columnhead\n");
        if (ctx->xlabel && ctx->time)
                g_string_append_printf(script, "set xlabel '%s'\n",
@@ -560,7 +586,7 @@ static int receive(const struct sr_output *o,
                break;
        case SR_DF_FRAME_BEGIN:
                *out = g_string_new(ctx->frame);
-               /* And then fall through to... */
+               /* Fallthrough */
        case SR_DF_END:
                /* Got to end of frame/session with part of the data. */
                if (ctx->channels_seen)
@@ -590,6 +616,7 @@ static int cleanup(struct sr_output *o)
                g_free((gpointer)ctx->frame);
                g_free((gpointer)ctx->comment);
                g_free((gpointer)ctx->gnuplot);
+               g_free((gpointer)ctx->value);
                g_free(ctx->previous_sample);
                g_free(ctx->channels);
                g_free(o->priv);
@@ -604,10 +631,10 @@ static struct sr_option options[] = {
        {"scale", "scale", "Scale gnuplot graphs", NULL, NULL},
        {"value", "Value separator", "Character to print between values", NULL, NULL},
        {"record", "Record separator", "String to print between records", NULL, NULL},
-       {"frame", "Frame seperator", "String to print between frames", NULL, NULL},
+       {"frame", "Frame separator", "String to print between frames", NULL, NULL},
        {"comment", "Comment start string", "String used at start of comment lines", NULL, NULL},
        {"header", "Output header", "Output header comment with capture metdata", NULL, NULL},
-       {"label", "Label values", "Output labels for each value", NULL, NULL},
+       {"label", "Label values", "Type of column labels", NULL, NULL},
        {"time", "Time column", "Output sample time as column 1", NULL, NULL},
        {"trigger", "Trigger column", "Output trigger indicator as last column ", NULL, NULL},
        {"dedup", "Dedup rows", "Set to false to output duplicate rows", NULL, NULL},
@@ -616,6 +643,8 @@ static struct sr_option options[] = {
 
 static const struct sr_option *get_options(void)
 {
+       GSList *l = NULL;
+
        if (!options[0].def) {
                options[0].def = g_variant_ref_sink(g_variant_new_string(""));
                options[1].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
@@ -624,10 +653,14 @@ static const struct sr_option *get_options(void)
                options[4].def = g_variant_ref_sink(g_variant_new_string("\n"));
                options[5].def = g_variant_ref_sink(g_variant_new_string(";"));
                options[6].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
-               options[7].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
+               options[7].def = g_variant_ref_sink(g_variant_new_string("units"));
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("units")));
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("channel")));
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("off")));
+               options[7].values = l;
                options[8].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
                options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
-               options[10].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
+               options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
        }
 
        return options;