X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=output%2Fanalog.c;h=50189b41334924ed45673ac76baea3c1a7b703fd;hb=1477a9a6dc4f470cb91c64c71c139825e8329adb;hp=58c37dba6ae933d7f5f125744f5a185d01823ac1;hpb=7c1d391c8b33bf76f7c6617fe9d5174e16a04f95;p=libsigrok.git diff --git a/output/analog.c b/output/analog.c index 58c37dba..50189b41 100644 --- a/output/analog.c +++ b/output/analog.c @@ -1,9 +1,7 @@ /* - * This file is part of the sigrok project. + * This file is part of the libsigrok project. * - * Copyright (C) 2010 Bert Vermeulen - * Copyright (C) 2011 HÃ¥vard Espeland - * Copyright (C) 2011 Daniel Ribeiro + * Copyright (C) 2012 Bert Vermeulen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,448 +18,258 @@ */ #include -#include #include +#include #include -#include "sigrok.h" - -#define DEFAULT_BPL_BITS 64 -#define DEFAULT_BPL_HEX 192 -#define DEFAULT_BPL_ASCII 74 - -enum outputmode { - MODE_BITS = 1, - MODE_HEX, - MODE_ASCII, -}; +#include "libsigrok.h" +#include "libsigrok-internal.h" + +/* Message logging helpers with subsystem-specific prefix string. */ +#define LOG_PREFIX "output/analog: " +#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) +#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) +#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) +#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) +#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) +#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) struct context { - unsigned int num_enabled_probes; - int samples_per_line; - unsigned int unitsize; - int line_offset; - int linebuf_len; - char *probelist[65]; - char *linebuf; - int spl_cnt; - uint8_t *linevalues; - char *header; - int mark_trigger; -// struct sr_analog_sample *prevsample; - enum outputmode mode; + int num_enabled_probes; + GPtrArray *probelist; }; -static void flush_linebufs(struct context *ctx, char *outbuf) -{ - static int max_probename_len = 0; - int len, i; - - if (ctx->linebuf[0] == 0) - return; - - if (max_probename_len == 0) { - /* First time through... */ - for (i = 0; ctx->probelist[i]; i++) { - len = strlen(ctx->probelist[i]); - if (len > max_probename_len) - max_probename_len = len; - } - } - - for (i = 0; ctx->probelist[i]; i++) { - sprintf(outbuf + strlen(outbuf), "%*s:%s\n", max_probename_len, - ctx->probelist[i], ctx->linebuf + i * ctx->linebuf_len); - } - - /* Mark trigger with a ^ character. */ - if (ctx->mark_trigger != -1) - { - int space_offset = ctx->mark_trigger / 8; - - if (ctx->mode == MODE_ASCII) - space_offset = 0; - - sprintf(outbuf + strlen(outbuf), "T:%*s^\n", - ctx->mark_trigger + space_offset, ""); - } - - memset(ctx->linebuf, 0, i * ctx->linebuf_len); -} - -static int init(struct sr_output *o, int default_spl, enum outputmode mode) +static int init(struct sr_output *o) { struct context *ctx; struct sr_probe *probe; GSList *l; - uint64_t samplerate; - int num_probes; - char *samplerate_s; - - if (!(ctx = calloc(1, sizeof(struct context)))) - return SR_ERR_MALLOC; - - o->internal = ctx; - ctx->num_enabled_probes = 0; - - for (l = o->device->probes; l; l = l->next) { - probe = l->data; - if (!probe->enabled) - continue; - ctx->probelist[ctx->num_enabled_probes++] = probe->name; - } - ctx->probelist[ctx->num_enabled_probes] = 0; - ctx->unitsize = sizeof(struct sr_analog_sample) + - (ctx->num_enabled_probes * sizeof(struct sr_analog_probe)); - ctx->line_offset = 0; - ctx->spl_cnt = 0; - ctx->mark_trigger = -1; - ctx->mode = mode; + sr_spew("Initializing output module."); - if (o->param && o->param[0]) { - ctx->samples_per_line = strtoul(o->param, NULL, 10); - if (ctx->samples_per_line < 1) - return SR_ERR; - } else - ctx->samples_per_line = default_spl; + if (!o || !o->sdi) + return SR_ERR_ARG; - if (!(ctx->header = malloc(512))) { - free(ctx); + if (!(ctx = g_try_malloc0(sizeof(struct context)))) { + sr_err("Output module context malloc failed."); return SR_ERR_MALLOC; } + o->internal = ctx; - snprintf(ctx->header, 511, "%s\n", PACKAGE_STRING); - num_probes = g_slist_length(o->device->probes); - if (o->device->plugin && sr_device_has_hwcap(o->device, SR_HWCAP_SAMPLERATE)) { - samplerate = *((uint64_t *) o->device->plugin->get_device_info( - o->device->plugin_index, SR_DI_CUR_SAMPLERATE)); - if (!(samplerate_s = sr_samplerate_string(samplerate))) { - free(ctx->header); - free(ctx); - return SR_ERR; - } - snprintf(ctx->header + strlen(ctx->header), - 511 - strlen(ctx->header), - "Acquisition with %d/%d probes at %s\n", - ctx->num_enabled_probes, num_probes, samplerate_s); - free(samplerate_s); - } - - ctx->linebuf_len = ctx->samples_per_line * 2 + 4; - if (!(ctx->linebuf = calloc(1, num_probes * ctx->linebuf_len))) { - free(ctx->header); - free(ctx); - return SR_ERR_MALLOC; - } - if (!(ctx->linevalues = calloc(1, num_probes))) { - free(ctx->header); - free(ctx); - return SR_ERR_MALLOC; + /* Get the number of probes and their names. */ + ctx->probelist = g_ptr_array_new(); + for (l = o->sdi->probes; l; l = l->next) { + probe = l->data; + if (!probe || !probe->enabled) + continue; + g_ptr_array_add(ctx->probelist, probe->name); + ctx->num_enabled_probes++; } return SR_OK; } -static int event(struct sr_output *o, int event_type, char **data_out, - uint64_t *length_out) +static void si_printf(float value, GString *out, char *unitstr) { - struct context *ctx; - int outsize; - char *outbuf; + float v; + + if (signbit(value)) + v = -(value); + else + v = value; + + if (v < 1e-12 || v > 1e+12) + g_string_append_printf(out, "%f %s", value, unitstr); + else if (v > 1e+9) + g_string_append_printf(out, "%f G%s", value / 1e+9, unitstr); + else if (v > 1e+6) + g_string_append_printf(out, "%f M%s", value / 1e+6, unitstr); + else if (v > 1e+3) + g_string_append_printf(out, "%f k%s", value / 1e+3, unitstr); + else if (v < 1e-9) + g_string_append_printf(out, "%f n%s", value * 1e+9, unitstr); + else if (v < 1e-6) + g_string_append_printf(out, "%f u%s", value * 1e+6, unitstr); + else if (v < 1e-3) + g_string_append_printf(out, "%f m%s", value * 1e+3, unitstr); + else + g_string_append_printf(out, "%f %s", value, unitstr); - ctx = o->internal; - switch (event_type) { - case SR_DF_TRIGGER: - ctx->mark_trigger = ctx->spl_cnt; - *data_out = NULL; - *length_out = 0; +} + +static void fancyprint(int unit, int mqflags, float value, GString *out) +{ + switch (unit) { + case SR_UNIT_VOLT: + si_printf(value, out, "V"); + break; + case SR_UNIT_AMPERE: + si_printf(value, out, "A"); + break; + case SR_UNIT_OHM: + si_printf(value, out, ""); + g_string_append_unichar(out, 0x2126); + break; + case SR_UNIT_FARAD: + si_printf(value, out, "F"); + break; + case SR_UNIT_KELVIN: + si_printf(value, out, "K"); + break; + case SR_UNIT_CELSIUS: + si_printf(value, out, ""); + g_string_append_unichar(out, 0x00b0); + g_string_append_c(out, 'C'); + break; + case SR_UNIT_FAHRENHEIT: + si_printf(value, out, ""); + g_string_append_unichar(out, 0x00b0); + g_string_append_c(out, 'F'); + break; + case SR_UNIT_HERTZ: + si_printf(value, out, "Hz"); + break; + case SR_UNIT_PERCENTAGE: + g_string_append_printf(out, "%f%%", value); + break; + case SR_UNIT_BOOLEAN: + if (value > 0) + g_string_append_printf(out, "TRUE"); + else + g_string_append_printf(out, "FALSE"); break; - case SR_DF_END: - outsize = ctx->num_enabled_probes - * (ctx->samples_per_line + 20) + 512; - if (!(outbuf = calloc(1, outsize))) - return SR_ERR_MALLOC; - flush_linebufs(ctx, outbuf); - *data_out = outbuf; - *length_out = strlen(outbuf); - free(o->internal); - o->internal = NULL; + case SR_UNIT_SECOND: + si_printf(value, out, "s"); + break; + case SR_UNIT_SIEMENS: + si_printf(value, out, "S"); + break; + case SR_UNIT_DECIBEL_MW: + si_printf(value, out, "dBu"); + break; + case SR_UNIT_DECIBEL_VOLT: + si_printf(value, out, "dBV"); + break; + case SR_UNIT_DECIBEL_SPL: + if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A) + si_printf(value, out, "dB(A)"); + else if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_C) + si_printf(value, out, "dB(C)"); + else if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_Z) + si_printf(value, out, "dB(Z)"); + else + /* No frequency weighting, or non-standard "flat" */ + si_printf(value, out, "dB(SPL)"); + if (mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_S) + g_string_append(out, " S"); + else if (mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F) + g_string_append(out, " F"); + if (mqflags & SR_MQFLAG_SPL_LAT) + g_string_append(out, " LAT"); + else if (mqflags & SR_MQFLAG_SPL_PCT_OVER_ALARM) + /* Not a standard function for SLMs, so this is + * a made-up notation. */ + g_string_append(out, " %oA"); + break; + case SR_UNIT_CONCENTRATION: + g_string_append_printf(out, "%f ppm", value * 1000000); + break; + case SR_UNIT_REVOLUTIONS_PER_MINUTE: + si_printf(value, out, "RPM"); + case SR_UNIT_VOLT_AMPERE: + si_printf(value, out, "VA"); + break; + case SR_UNIT_WATT: + si_printf(value, out, "W"); + break; + case SR_UNIT_WATT_HOUR: + si_printf(value, out, "Wh"); break; default: - *data_out = NULL; - *length_out = 0; + si_printf(value, out, ""); break; } - return SR_OK; -} - -static int init_bits(struct sr_output *o) -{ - return init(o, DEFAULT_BPL_BITS, MODE_BITS); + if (mqflags & SR_MQFLAG_AC) + g_string_append_printf(out, " AC"); + if (mqflags & SR_MQFLAG_DC) + g_string_append_printf(out, " DC"); + if (mqflags & SR_MQFLAG_RMS) + g_string_append_printf(out, " RMS"); + if (mqflags & SR_MQFLAG_DIODE) + g_string_append_printf(out, " DIODE"); + if (mqflags & SR_MQFLAG_HOLD) + g_string_append_printf(out, " HOLD"); + if (mqflags & SR_MQFLAG_MAX) + g_string_append_printf(out, " MAX"); + if (mqflags & SR_MQFLAG_MIN) + g_string_append_printf(out, " MIN"); + if (mqflags & SR_MQFLAG_AUTORANGE) + g_string_append_printf(out, " AUTO"); + if (mqflags & SR_MQFLAG_RELATIVE) + g_string_append_printf(out, " REL"); + g_string_append_c(out, '\n'); } -static int data_bits(struct sr_output *o, const char *data_in, - uint64_t length_in, char **data_out, uint64_t *length_out) +static int receive(struct sr_output *o, const struct sr_dev_inst *sdi, + const struct sr_datafeed_packet *packet, GString **out) { - struct context *ctx; - unsigned int outsize, offset, p; - int max_linelen; - struct sr_analog_sample *sample; - char *outbuf, c; - - ctx = o->internal; - max_linelen = SR_MAX_PROBENAME_LEN + 3 + ctx->samples_per_line - + ctx->samples_per_line / 8; - /* - * Calculate space needed for probes. Set aside 512 bytes for - * extra output, e.g. trigger. - */ - outsize = 512 + (1 + (length_in / ctx->unitsize) / ctx->samples_per_line) - * (ctx->num_enabled_probes * max_linelen); - - if (!(outbuf = calloc(1, outsize + 1))) - return SR_ERR_MALLOC; - - outbuf[0] = '\0'; - if (ctx->header) { - /* The header is still here, this must be the first packet. */ - strncpy(outbuf, ctx->header, outsize); - free(ctx->header); - ctx->header = NULL; - - /* Ensure first transition. */ -// memcpy(&ctx->prevsample, data_in, ctx->unitsize); -// ctx->prevsample = ~ctx->prevsample; - } + const struct sr_datafeed_analog *analog; + struct sr_probe *probe; + GSList *l; + const float *fdata; + int i, p; - if (length_in >= ctx->unitsize) { - for (offset = 0; offset <= length_in - ctx->unitsize; - offset += ctx->unitsize) { - sample = (struct sr_analog_sample *) (data_in + offset); - for (p = 0; p < ctx->num_enabled_probes; p++) { - int val = sample->probes[p].val; - int res = sample->probes[p].res; - if (res == 1) - c = '0' + (val & ((1 << res) - 1)); - else - /* - * Scale analog resolution down so it - * fits 25 letters - */ - c = 'A' + (((val & ((1 << res) - 1)) / - (res * res)) / 10); - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset] = c; - } - ctx->line_offset++; - ctx->spl_cnt++; + (void)sdi; - /* Add a space every 8th bit. */ - if ((ctx->spl_cnt & 7) == 0) { - for (p = 0; p < ctx->num_enabled_probes; p++) - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset] = ' '; - ctx->line_offset++; - } + *out = NULL; + if (!o || !o->sdi) + return SR_ERR_ARG; - /* End of line. */ - if (ctx->spl_cnt >= ctx->samples_per_line) { - flush_linebufs(ctx, outbuf); - ctx->line_offset = ctx->spl_cnt = 0; - ctx->mark_trigger = -1; + switch (packet->type) { + case SR_DF_FRAME_BEGIN: + *out = g_string_new("FRAME-BEGIN\n"); + break; + case SR_DF_FRAME_END: + *out = g_string_new("FRAME-END\n"); + break; + case SR_DF_ANALOG: + analog = packet->payload; + fdata = (const float *)analog->data; + *out = g_string_sized_new(512); + for (i = 0; i < analog->num_samples; i++) { + for (l = analog->probes, p = 0; l; l = l->next, p++) { + probe = l->data; + g_string_append_printf(*out, "%s: ", probe->name); + fancyprint(analog->unit, analog->mqflags, + fdata[i + p], *out); } } - } else { - sr_info("short buffer (length_in=%" PRIu64 ")", length_in); - } - - *data_out = outbuf; - *length_out = strlen(outbuf); - - return SR_OK; -} -#if 0 -static int init_hex(struct sr_output *o) -{ - return init(o, DEFAULT_BPL_HEX, MODE_HEX); -} - -static int data_hex(struct sr_output *o, const char *data_in, - uint64_t length_in, char **data_out, uint64_t *length_out) -{ - struct context *ctx; - unsigned int outsize, offset, p; - int max_linelen; - uint64_t sample; - char *outbuf; - - ctx = o->internal; - max_linelen = SR_MAX_PROBENAME_LEN + 3 + ctx->samples_per_line - + ctx->samples_per_line / 2; - outsize = length_in / ctx->unitsize * ctx->num_enabled_probes - / ctx->samples_per_line * max_linelen + 512; - - if (!(outbuf = calloc(1, outsize + 1))) - return SR_ERR_MALLOC; - - outbuf[0] = '\0'; - if (ctx->header) { - /* The header is still here, this must be the first packet. */ - strncpy(outbuf, ctx->header, outsize); - free(ctx->header); - ctx->header = NULL; - } - - ctx->line_offset = 0; - for (offset = 0; offset <= length_in - ctx->unitsize; - offset += ctx->unitsize) { - memcpy(&sample, data_in + offset, ctx->unitsize); - for (p = 0; p < ctx->num_enabled_probes; p++) { - ctx->linevalues[p] <<= 1; - if (sample & ((uint64_t) 1 << p)) - ctx->linevalues[p] |= 1; - sprintf(ctx->linebuf + (p * ctx->linebuf_len) + - ctx->line_offset, "%.2x", ctx->linevalues[p]); - } - ctx->spl_cnt++; - - /* Add a space after every complete hex byte. */ - if ((ctx->spl_cnt & 7) == 0) { - for (p = 0; p < ctx->num_enabled_probes; p++) - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset + 2] = ' '; - ctx->line_offset += 3; - } - - /* End of line. */ - if (ctx->spl_cnt >= ctx->samples_per_line) { - flush_linebufs(ctx, outbuf); - ctx->line_offset = ctx->spl_cnt = 0; - } + break; } - *data_out = outbuf; - *length_out = strlen(outbuf); - return SR_OK; } -static int init_ascii(struct sr_output *o) -{ - return init(o, DEFAULT_BPL_ASCII, MODE_ASCII); -} - -static int data_ascii(struct sr_output *o, const char *data_in, - uint64_t length_in, char **data_out, uint64_t *length_out) +static int cleanup(struct sr_output *o) { struct context *ctx; - unsigned int outsize, offset, p; - int max_linelen; - uint64_t sample; - char *outbuf; + if (!o || !o->sdi) + return SR_ERR_ARG; ctx = o->internal; - max_linelen = SR_MAX_PROBENAME_LEN + 3 + ctx->samples_per_line - + ctx->samples_per_line / 8; - /* - * Calculate space needed for probes. Set aside 512 bytes for - * extra output, e.g. trigger. - */ - outsize = 512 + (1 + (length_in / ctx->unitsize) / ctx->samples_per_line) - * (ctx->num_enabled_probes * max_linelen); - - if (!(outbuf = calloc(1, outsize + 1))) - return SR_ERR_MALLOC; - - outbuf[0] = '\0'; - if (ctx->header) { - /* The header is still here, this must be the first packet. */ - strncpy(outbuf, ctx->header, outsize); - free(ctx->header); - ctx->header = NULL; - } - - if (length_in >= ctx->unitsize) { - for (offset = 0; offset <= length_in - ctx->unitsize; - offset += ctx->unitsize) { - memcpy(&sample, data_in + offset, ctx->unitsize); - char tmpval[ctx->num_enabled_probes]; - - for (p = 0; p < ctx->num_enabled_probes; p++) { - uint64_t curbit = (sample & ((uint64_t) 1 << p)); - uint64_t prevbit = (ctx->prevsample & - ((uint64_t) 1 << p)); - - if (curbit < prevbit && ctx->line_offset > 0) { - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset-1] = '\\'; - } - - if (curbit > prevbit) { - tmpval[p] = '/'; - } else { - if (curbit) - tmpval[p] = '"'; - else - tmpval[p] = '.'; - } - } - - /* End of line. */ - if (ctx->spl_cnt >= ctx->samples_per_line) { - flush_linebufs(ctx, outbuf); - ctx->line_offset = ctx->spl_cnt = 0; - ctx->mark_trigger = -1; - } - - for (p = 0; p < ctx->num_enabled_probes; p++) { - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset] = tmpval[p]; - } - - ctx->line_offset++; - ctx->spl_cnt++; - - ctx->prevsample = sample; - } - } else { - sr_info("short buffer (length_in=%" PRIu64 ")", length_in); - } - - *data_out = outbuf; - *length_out = strlen(outbuf); + g_ptr_array_free(ctx->probelist, 1); + g_free(ctx); + o->internal = NULL; return SR_OK; } -#endif - -SR_PRIV struct sr_output_format output_analog_bits = { - .id = "analog_bits", - .description = "Bits (takes argument, default 64)", - .df_type = SR_DF_ANALOG, - .init = init_bits, - .data = data_bits, - .event = event, -}; - -#if 0 -struct sr_output_format output_analog_hex = { - .id = "analog_hex", - .description = "Hexadecimal (takes argument, default 192)", - .df_type = SR_DF_ANALOG, - .init = init_hex, - .data = data_hex, - .event = event, -}; -struct sr_output_format output_analog_ascii = { - .id = "analog_ascii", - .description = "ASCII (takes argument, default 74)", +SR_PRIV struct sr_output_format output_analog = { + .id = "analog", + .description = "Analog data", .df_type = SR_DF_ANALOG, - .init = init_ascii, - .data = data_ascii, - .event = event, + .init = init, + .receive = receive, + .cleanup = cleanup }; -#endif