output/vcd: support smaller timescales with higher resolution

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.
This commit is contained in:
Gerhard Sittig 2018-10-12 12:35:50 +02:00 committed by Uwe Hermann
parent 4ddea31451
commit 17c30d0593
1 changed files with 33 additions and 7 deletions

View 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);