-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Timestamping #170
Comments
It's worth mentioning that most of the logic in "External reference on digital input" is already implemented in stabilizer-lockin-nucleo. That implementation expects the |
I wrote this down to have one concise place where the design is specified. And Ryan needs this to implement the items 1-4 and the second case. Let's work on merging the DSP code in |
And what do you (@ryan-summers and @matthuszagh ) think about laying a bit of groundwork for multiple binaries (in this case just for the nucleo hardware and the stabilizer hardware). I.e. have |
I was planning on doing the ground work for multiple binaries for the stabilizer WMS demo - getting started with the nucleo binary is probably a great first step though. |
Sounds good - I'll get going on the DSP PRs. I'm also happy to do any part of the work for splitting this up into separate binaries. It seems to me that another benefit of splitting this is that it would move hardware-independent DSP code to a separate library, which would make it easier to perform unit and integration tests on that code. |
@matthuszagh Why do you need to know I've added classification of design/compile-time constants and run-time parameters for |
@jordens I don't. I'd just defined it that way since I wasn't entirely sure which inputs you wanted (everything else was just computed at compile time anyway). But, it's easy to refactor so that |
Ok. Good! |
Note to self: maybe it's cheaper to do the phase conversion in the first case before the pll. Either with cheaper integer math or by building a "reciprocal" pll that does so internally. |
Is |
Timer overflow periods. |
Thanks. I've updated the original post to reflect that. |
I think the equations for Here's some very simple code to test the correctness of the initial phase calculation: def initial_phase(a, lgj, y, f, k, m):
return (
int(round((((a << (32 - lgj)) - y) << 32) / (f + (k << 32))))
+ (m << 32)
) % (1 << 32)
# a == 0, m == 0, k == 0
assert initial_phase(a=0, lgj=1, y=-500, f=2362232012, k=0, m=0) == 909
# a == 0, m == 0, k == 1
assert initial_phase(a=0, lgj=1, y=-400, f=2362232012, k=1, m=0) == 258
assert (
initial_phase(a=0, lgj=1, y=-2362232412, f=2362232012, k=1, m=0)
== 1524020911
)
assert (
initial_phase(a=0, lgj=1, y=-4294977296, f=2362232012, k=1, m=0)
== 2770953095
)
# a == 0, m == 1, k == 0
assert initial_phase(a=0, lgj=1, y=500, f=2362232012, k=0, m=1) == 4294966387
assert (
initial_phase(a=0, lgj=1, y=2147483148, f=2362232012, k=0, m=1)
== 390452480
)
# a == 0, m == 1, k == 1
assert initial_phase(a=0, lgj=1, y=500, f=2362232012, k=1, m=1) == 4294966973
assert (
initial_phase(a=0, lgj=1, y=2147483148, f=2362232012, k=1, m=1)
== 2909494297
)
# a == 0, m > 1, k == 0
assert initial_phase(a=0, lgj=1, y=2147483548, f=195225786, k=0, m=11) == 2156
# a == 1, m == 1, k == 0
assert (
initial_phase(a=1, lgj=1, y=4294967286, f=2362232012, k=0, m=1)
== 390451589
)
# a == 3 (lgj=2), m == 1, k == 0
assert (
initial_phase(a=3, lgj=2, y=4294967286, f=2362232012, k=0, m=1)
== 2342709452
) Let me know if you'd like me to give the derivations for |
#241 Covers the reciprocal pll |
Timestamping architecture
External reference on digital input
t
, integer in units of some timer clock periodτ
(e.g 10 ns) locked to the CPU PLL, and thus to the CPU XO. For examplet = 128 τ ~ 1.28 µs
.τ
is a design constant,t
is a compile-time constant. We should aim at makingt
a power of two.n
DAC and ADC samples per batch, batch periodn*t
,n
being a power of two compile-time constant, e.g.n = 4
.j*n*t
. Locked through the CPU PLL to the CPU XO. Not yet/necessarily deterministic (across reboots, resets, timer reconfigurations) phase w.r.t. ADC/DAC sampling timer.j >= 1
, a power of two, as large as possible within the timer counter size.s
(u16 as i16
), Digital input frequency typically anywhere from 50 Hz (mains) to 500 kHz (less than ADC Nyquist typically). These are samples of the batch phase at zero reference phase.m
timestamps for the current batch. Number of timestamps0 <= m <= n
depends on reference frequency, no new timestamps is explicitly possible.mod j*n*t
to naturali32
mod (1 << 32)
. Pure left shift.s
into the PLL en-bloc and at the end of the batch, store most recent phasey
, frequencyf
(bothi32
) and the timek
in units of full timer overflow periods between the two last PLL updates (i.e.k = 0
necessarily ifm >= 2
, thoughk
can also be0
ifm < 2
).j > 1
will help with that.g = (1 << 64)/(n*f + n*(k << 32))
,v = -(y << 32)/f
. (TODO: distribute exponents or justi64
math).u: i32
.p_i: i32 = u*(i*g + v)
.125 MHz from Pounder on ETR
σ
,σ = r*4*2 ns
, typically the timer ETR prescaler isr = 4
orr = 8
, compile-time constant.σ
is a design constant.q
, integer in units ofσ
(f_demod = 500 MHz/4/r/q
).q = 128
for example.n <= q < u16
.q
should be run-time configurable. It's the difference between two DDS FTWs.q*r*4 = f_a - f_b
. We can restrictq
to a power of two.s
. There will always ben
samples per batch.mod q
to naturalmod (1 << 32)
. Pure left shift.y, f
.g = f
,v = y - n*f
Filtering PLL, done in #188
s_i mod n
timestamp sequence (in either of the two cases above) into NCO frequency and phase (relative to the CPU clock/XO or better ADC/DAC batch clock). Configurable IIR filter, higher resolution, lower bandwidth.p_m
for the ADC samples.maybe it ((doesn't). Implemented.ftw <- IIR(s_i - phase); phase <- phase + ftw
) boils down to natually overflowing integerphase = IIR().update(s_i)
with a biquad.Even better (thanks @SingularitySurfer): Check whether this is possible with the STM32 timers: Set up a wrapping hardware timer with variable increment as the NCO (No such feature and the current PLL looks more flexible.phase <- phase + ftw
) and feed back the timestamps onto the counter increment (ftw += gain_p*(s_i - phase_i_nominal) + gain_d*(s_i - s_i-1)
that'sftw = IIR(s_i)
).Context:
The text was updated successfully, but these errors were encountered: