]> sigrok.org Git - libsigrok.git/commitdiff
output/vcd: support smaller timescales with higher resolution
authorGerhard Sittig <redacted>
Fri, 12 Oct 2018 10:35:50 +0000 (12:35 +0200)
committerUwe Hermann <redacted>
Sat, 21 Dec 2019 16:19:50 +0000 (17:19 +0100)
The previous implementation inspected the input stream's samplerate, and
simply used the next 1kHz/1MHz/1GHz timescale for VCD export. Re-import
of the exported file might suffer from rather high an overhead, where
users might have to downsample the input stream. Also exported data
might use an "odd" timescale which doesn't represent the input stream's
timing well.

Rephrase the samplerate to VCD timescale conversion such that the lowest
frequency is used which satisfies the file format's constraints as well
as provides high enough a resolution to communicate the input stream's
timing with minimal loss. Do limit this scaling support to at most three
orders above the input samplerate, to most appropriately cope with odd
rates.

As a byproduct the rephrased implementation transparently supports rates
above 1GHz. Input streams with no samplerate now result in 1s timescale
instead of the 1ms timescale of the previous implementation.

src/output/vcd.c

index 80b2c34a2bca03f74e687b6f86b6276b13d174b9..3de3dcde37b3a044679ed745e498433c987aa047 100644 (file)
@@ -78,6 +78,38 @@ static int init(struct sr_output *o, GHashTable *options)
        return SR_OK;
 }
 
+/*
+ * VCD can only handle 1/10/100 factors in the s to fs range. Find a
+ * suitable timescale which satisfies this resolution constraint, yet
+ * won't result in excessive overhead.
+ */
+static uint64_t get_timescale_freq(uint64_t samplerate)
+{
+       uint64_t timescale;
+       int max_up_scale;
+
+       /* Go to the next full decade. */
+       timescale = 1;
+       while (timescale < samplerate) {
+               timescale *= 10;
+       }
+
+       /*
+        * Avoid loss of precision, go up a few more decades when needed.
+        * For example switch to 10GHz timescale when samplerate is 400MHz.
+        * Stop after at most factor 100 to not loop endlessly for odd
+        * samplerates, yet provide good enough accuracy.
+        */
+       max_up_scale = 2;
+       while (max_up_scale--) {
+               if (timescale / samplerate * samplerate == timescale)
+                       break;
+               timescale *= 10;
+       }
+
+       return timescale;
+}
+
 static GString *gen_header(const struct sr_output *o)
 {
        struct context *ctx;
@@ -121,13 +153,7 @@ static GString *gen_header(const struct sr_output *o)
        g_string_append_printf(header, "\n$end\n");
 
        /* timescale */
-       /* VCD can only handle 1/10/100 (s - fs), so scale up first */
-       if (ctx->samplerate > SR_MHZ(1))
-               ctx->period = SR_GHZ(1);
-       else if (ctx->samplerate > SR_KHZ(1))
-               ctx->period = SR_MHZ(1);
-       else
-               ctx->period = SR_KHZ(1);
+       ctx->period = get_timescale_freq(ctx->samplerate);
        frequency_s = sr_period_string(1, ctx->period);
        g_string_append_printf(header, "$timescale %s $end\n", frequency_s);
        g_free(frequency_s);