+ uint8_t *sample;
+
+ num_samples = logic->length / logic->unitsize;
+ ctx->channels_seen += ctx->logic_channel_count;
+ sr_dbg("Logic packet had %d channels", logic->unitsize * 8);
+ if (!ctx->logic_samples) {
+ ctx->logic_samples = g_malloc(num_samples * ctx->num_logic_channels);
+ if (!ctx->num_samples)
+ ctx->num_samples = num_samples;
+ }
+ if (ctx->num_samples != num_samples)
+ sr_warn("Expecting %u samples, got %u",
+ ctx->num_samples, num_samples);
+
+ for (j = ch = 0; ch < ctx->num_logic_channels; j++) {
+ if (ctx->channels[j].ch->type == SR_CHANNEL_LOGIC) {
+ 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++;
+ }
+ }
+}
+
+static void dump_saved_values(struct context *ctx, GString **out)
+{
+ unsigned int i, j, analog_size, num_channels;
+ float *analog_sample, value;
+ uint8_t *logic_sample;
+
+ /* If we haven't seen samples we're expecting, skip them. */
+ if ((ctx->num_analog_channels && !ctx->analog_samples) ||
+ (ctx->num_logic_channels && !ctx->logic_samples)) {
+ sr_warn("Discarding partial packet");
+ } else {
+ sr_info("Dumping %u samples", ctx->num_samples);
+
+ *out = g_string_sized_new(512);
+ num_channels =
+ ctx->num_logic_channels + ctx->num_analog_channels;
+
+ if (ctx->label_do) {
+ if (ctx->time)
+ 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].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",
+ ctx->value);
+ /* Drop last separator. */
+ g_string_truncate(*out, (*out)->len - 1);
+ g_string_append(*out, ctx->record);
+
+ ctx->label_do = FALSE;
+ }
+
+ analog_size = ctx->num_analog_channels * sizeof(float);
+ if (ctx->dedup && !ctx->previous_sample)
+ ctx->previous_sample = g_malloc0(analog_size + ctx->num_logic_channels);
+
+ for (i = 0; i < ctx->num_samples; i++) {
+ ctx->sample_time += ctx->period;
+ analog_sample =
+ &ctx->analog_samples[i * ctx->num_analog_channels];
+ logic_sample =
+ &ctx->logic_samples[i * ctx->num_logic_channels];
+
+ if (ctx->dedup) {
+ if (i > 0 && i < ctx->num_samples - 1 &&
+ !memcmp(logic_sample, ctx->previous_sample,
+ ctx->num_logic_channels) &&
+ !memcmp(analog_sample,
+ ctx->previous_sample +
+ ctx->num_logic_channels,
+ analog_size))
+ continue;
+ memcpy(ctx->previous_sample, logic_sample,
+ ctx->num_logic_channels);
+ memcpy(ctx->previous_sample
+ + ctx->num_logic_channels,
+ analog_sample, analog_size);
+ }
+
+ if (ctx->time)
+ g_string_append_printf(*out, "%" PRIu64 "%s",
+ ctx->sample_time, ctx->value);
+
+ for (j = 0; j < num_channels; j++) {
+ if (ctx->channels[j].ch->type == SR_CHANNEL_ANALOG) {
+ value = ctx->analog_samples[i * ctx->num_analog_channels + j];
+ ctx->channels[j].max =
+ fmax(value, ctx->channels[j].max);
+ ctx->channels[j].min =
+ fmin(value, ctx->channels[j].min);
+ g_string_append_printf(*out, "%g%s",
+ value, ctx->value);
+ } else if (ctx->channels[j].ch->type == SR_CHANNEL_LOGIC) {
+ g_string_append_printf(*out, "%c%s",
+ ctx->logic_samples[i * ctx->num_logic_channels + j] ? '1' : '0', ctx->value);
+ } else {
+ sr_warn("Unexpected channel type: %d",
+ ctx->channels[i].ch->type);
+ }
+ }
+
+ if (ctx->do_trigger) {
+ g_string_append_printf(*out, "%d%s",
+ ctx->trigger, ctx->value);
+ ctx->trigger = FALSE;
+ }
+ g_string_truncate(*out, (*out)->len - 1);
+ g_string_append(*out, ctx->record);
+ }
+ }
+
+ /* Discard all of the working space. */
+ g_free(ctx->previous_sample);
+ g_free(ctx->analog_samples);
+ g_free(ctx->logic_samples);
+ ctx->channels_seen = 0;
+ ctx->num_samples = 0;
+ ctx->previous_sample = NULL;
+ ctx->analog_samples = NULL;
+ ctx->logic_samples = NULL;
+}
+
+static void save_gnuplot(struct context *ctx)
+{
+ float offset, max, sum;
+ unsigned int i, num_channels;
+ GString *script;
+
+ script = g_string_sized_new(512);
+ g_string_append_printf(script, "set datafile separator '%s'\n",
+ ctx->value);
+ 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",
+ ctx->xlabel);
+
+ g_string_append(script, "plot ");
+
+ num_channels = ctx->num_analog_channels + ctx->num_logic_channels;
+
+ /* Graph position and scaling. */
+ max = FLT_MIN;
+ sum = 0;
+ for (i = 0; i < num_channels; i++) {
+ ctx->channels[i].max =
+ ctx->channels[i].max - ctx->channels[i].min;
+ max = fmax(max, ctx->channels[i].max);
+ sum += ctx->channels[i].max;
+ }
+ sum = (ctx->scale ? max : sum / num_channels) / 4;
+ offset = sum;
+ for (i = num_channels; i > 0;) {
+ i--;
+ ctx->channels[i].min = offset - ctx->channels[i].min;
+ offset += sum + (ctx->scale ? max : ctx->channels[i].max);
+ }
+
+ for (i = 0; i < num_channels; i++) {
+ sr_spew("Channel %d, min %g, max %g", i, ctx->channels[i].min,
+ ctx->channels[i].max);
+ g_string_append(script, "ARG1 ");
+ if (ctx->did_header)
+ g_string_append(script, "skip 4 ");
+ g_string_append_printf(script, "using %u:($%u * %g + %g), ",
+ ctx->time, i + 1 + ctx->time, ctx->scale ?
+ max / ctx->channels[i].max : 1, ctx->channels[i].min);
+ offset += 1.1 * (ctx->channels[i].max - ctx->channels[i].min);
+ }
+ g_string_truncate(script, script->len - 2);
+ g_file_set_contents(ctx->gnuplot, script->str, script->len, NULL);
+ g_string_free(script, TRUE);
+}
+
+static int receive(const struct sr_output *o,
+ const struct sr_datafeed_packet *packet, GString **out)
+{
+ struct context *ctx;