Fxpt_nco vs rotator?

I’m experimenting with very fine frequency measurements (i.e. uHz
resolution)
at high sample rates. For instance, down-converting all the WWV signals
from a 100 Msps chunk of HF spectrum to near 0 Hz then comparing their
drift.

I have a handle on the DDC within the USRP and the sig_source_c within
GR,
both of which use fixed point, 32-bit NCOs, thus the frequency accuracy
is
quantifiable, for example:

nco_freq = int(target_freq/(samp_rate/232)) * (samp_rate/232)

Now the Frequency Translating FIR filter uses a rotator, and from what I
can
tell by looking at the source (and I may be wrong), that rotator uses
VOLK
or falls back to the normal code, both of which use a floating point
phase
accumulator, with the latter being double precision.

So is there anyway to quantify the frequency, over a long number of
cycles,
with the floating point phase accumulator?

I ran this GRC experiment:

https://dl.dropboxusercontent.com/u/49570443/rotator_test_grc.png

I generated a 1 Hz tone at 100 Msps with both the sig_source_c and
rotator,
decimating down to 10 sps, and analysing with Baudline FFT to 8 digits
of
precision. The sig_source_c was spot on at 0.97788870 Hz, i.e.
int(1/(100E6/232))*(100E6/232)

For the rotator I used a phase increment of (1/100E6)2numpy.pi, which
resulted in 0.99917424 Hz via Baudline.

So I concluded the rotator has higher actuary to the target frequency of
1
Hz, but…

Does it converge, or does it slowly drift away?
Does it have more phase noise than the fixed point NCO?
I noticed it has some spurs whereas the fixed point was spur-free?

I suppose in cases like this I should use an separate NCO and decimating
FIR
for down conversion, instead of the Frequency Translating FIR?

As a side experiment I tried the following python code, which shows
shows an
accumulating error over 10 cycles, but I’m not sure if this is even a
valid
experiment:

import numpy as np

target_freq = np.float64(1)
samp_rate = np.float64(100E6)
two_pi = np.float64(2*np.pi)
phase = np.float64(0)

cycles = 10
phase_inc = target_freq/samp_rate*two_pi

for cycle in range(cycles):
samp = 0
while samp < samp_rate:
samp = samp + 1
phase = phase + phase_inc
freq = phase/two_pi/(cycle+1)
print “The frequency over %i cycles is %.15g” % (cycle+1, freq)

#################################################

The frequency over 1 cycles is 1.00000000229562
The frequency over 2 cycles is 1.00000000453839
The frequency over 3 cycles is 1.00000000101206
The frequency over 4 cycles is 0.999999995386438
The frequency over 5 cycles is 0.999999992011064
The frequency over 6 cycles is 0.999999989760814
The frequency over 7 cycles is 0.999999988153493
The frequency over 8 cycles is 0.999999986948002
The frequency over 9 cycles is 0.999999986010398
The frequency over 10 cycles is 0.999999985260315


View this message in context:
http://gnuradio.4.n7.nabble.com/fxpt-nco-vs-rotator-tp51750.html
Sent from the GnuRadio mailing list archive at Nabble.com.

On Tue, Dec 30, 2014 at 11:55 PM, Sylvain M. [email protected]
wrote:

periodic renormalization process, but in any case they’re like more
than -130 dB down which makes them irrelevant for all but the most
demanding applications.

Cheers,

Sylvain

Just as an aside - and a note for other folks who want to investigate
differences between SIMD-optimized vs. generic kernels, VOLK does have
an
interesting (but perhaps not well-documented) feature in that if you
set/create an environment variable called VOLK_GENERIC (e.g. ‘export
VOLK_GENERIC=1’), then the VOLK dispatcher will only call the generic
implementation.
This shows up in most GR and OOT QA tests automatically (at least in the
Python-based QA tests) via the GrTest.cmake module.

Doug

If you need high, deterministic, quantifiable precision, you’re
probably better off reimplementing the critical blocks yourself the
way you want.

Most of the default blocks will have numerous optimization for speed
that can sacrifice precision. And this will actually depend on which
version of the volk kernel gets selected (generic / SSE / AVX / …)
because they use different algorithm and opcodes and such.

I’m pretty sure the rotator SIMD version has spurs because of the
periodic renormalization process, but in any case they’re like more
than -130 dB down which makes them irrelevant for all but the most
demanding applications.

Cheers,

Sylvain