0
$\begingroup$

Let's suppose that I want to generate sine and cosine look up tables for some reason. I have sampling frequency fs=855 kHz. Sine and cosine frequency is f=200 kHz. Based on that fs divided by f is equal to fs/f=4.275. It's not an integer value. In this particular case whatever number of samples I put into look up table I face phase discontinuity when I have to reset samples counter that is entry to my lookup table.

How can I solve this issue?


Let me explain what I want to do. My goal is simple SDR (software defined radio) based on STM32F407 microcontroller and R820T TV tuner.

The R820T provides IF signal that has central frequency roughly $f_{c} = 4.5 \textrm{ MHz}$. The STM32F407 has CPU clock frequency equal to $168 \textrm{ MHz}$ and it supports floating point computing. Bandwidth from the R820T is too large (roughly $6 \textrm{ MHz}$) for handle with ADC of STM32F407. I designed analog IF amplifier that has narrow bandwidth equal to $B = 400 \textrm{ kHz}$ with $-60 \textrm{ dB}$ gain drop. It looks reasonable for STM32F407 because I can sample it with lower sampling frequency. I mean that my IF signal can be bandpass sampling.

I calculated minimal sampling frequency $f_{s} = 855 \textrm{ kHz}$. It's without aliasing or very small aliasing ($-60 \textrm{ dB}$ overlapping). After sampling I have new $f_{c}$ equal to around $200 \textrm{ kHz}$. Next obvious step is multiplication each sample by sine and cosine (that's why I need $200 \textrm{ kHz}$ sine and cosine look-up tables).

The last operation is low pass filtering with IIR filters (non linear phase but for small bandwidth is not such bad) and I have I and Q signals. Sampling frequency $f_{s} = 855 \textrm{ kHz}$ is still really large so I decimate it by factor $8$ and the final $f_{s} = \frac{855 \textrm{ kHz}}{8} = 106.9 \textrm{ kHz}$. With I and Q signals I can do some math to create both AM and FM demodulation. To be honest my receiver hardly works so I have to put lots of effort to improve it and proper look-up tables is one issue.

I wrote simple code in Matlab for testing look-up tables:

clear; fs = 855e3; ts = 1/fs; f = 200e3; p = 2*pi()/171; sin_arr = sin(p * (0:170)); cos_arr = cos(p * (0:170)); k = 0; step = 40; N = 30; y_sin = zeros(1, N); y_cos = zeros(1, N); for i=1:N y_sin(i) = sin_arr(k+1); y_cos(i) = cos_arr(k+1); k = mod(k+step, 171); end x = 0:N-1; plot(x, y_sin, '-o', x, y_cos, '-o'); x_ = 0:10*N-1; y_sin_ = sin(2*pi()*f*x_*ts/10); y_cos_ = cos(2*pi()*f*x_*ts/10); plot(x_, y_sin_, '--', x_, y_cos_, '--', 10*x, y_sin, 'o', 10*x, y_cos, 'o'); 

It would be really efficient algorithm in C. The result looks like that. Dotted lines are sine and cosine with sampling frequency $10 f_{s}$ (just for clarification) and dots comes from look-up tables.

$\endgroup$
4
  • 1
    $\begingroup$ You should put in a decent number of points in your circular look-up tables. And then linear interpolate between adjacent points. $\endgroup$ Commented Jun 9 at 0:52
  • 1
    $\begingroup$ It would also help to understand the application and the requirements. There are MUCH better ways to build oscillators the look up tables. If you really do need a table: how good does the lookup need to be? Can you give a target in "signal to noise ratio" or "number of correct bits" ? $\endgroup$ Commented Jun 9 at 0:57
  • $\begingroup$ Yeah look-up tables works exactly like circular buffer. The application of it is simple SDR (software defined radio). In this case after sampling IF (intermediate frequency) by ADC I have to multiply incoming samples by sine and cosine and after that I apply low pass digital IIR filter. I'll explain it in more details. $\endgroup$ Commented Jun 9 at 12:43
  • $\begingroup$ An alternative to lookup tables is a recursive matrix formulation. Check out this paper on the topic by Clay Turner: claysturner.com/dsp/2nd_OSC_paper.pdf $\endgroup$ Commented Jun 9 at 14:55

2 Answers 2

0
$\begingroup$

This is the exact application where a Numerically Controlled Oscillator is typically used, and with good reason. An NCO is an essential component in any Software Defined Radio (SDR). An NCO offers:

Frequency Resolution (step size) according to

$$f_{\Delta} = \frac{f_s}{2^{acc}}$$

Where $f_s$ is the sampling rate (update rate of the accumulator), and $acc$ is the precision (number of bits) in the accumulator.

And a distortion level according to:

$$SFDR = 6.02 \text { dB/bit} $$

Where "bit" refers to the effective size of the Look-up Table (representing the amount of phase truncation from the accumulator output to the look-up table input since the accumulator output is proportional to instantaneous phase), and "SFDR" is the Spurious Free Dynamic Range" as limited by the strongest spurious signal due to phase truncation. This can be reduced further through dithering techniques or interpolation.

For example, if you need spurious noise due to any phase truncation within the NCO to be lower than 90 dB, then the precision of the address to the look up table would be $> 90/6 = 15$ bits. Typically we only need to store a quarter cycle, but functionally it is a black box which provides the required trigonometric sine and cosine functions for the range from $0$ to nearly $2\pi$ associated with the full address size, often performed with a look-up table, with memory reduction through quarter cycle storage and / or interpolation, or the CORDIC rotator. After that the only additional error source for the digital signal is the width of the output word stored; so complexity is traded with performance to whatever requirement is needed.

Please see DSP.SE #37803 for more details on using an NCO including the total noise due to phase truncation as opposed to just the strongest spur listed here.

The OP has mentioned using IIR filters which I would go to lengths to avoid until a operational baseline is completed, given the introduction of group delay distortion (the OP mentioned understanding this, but it just adds another variable that should only be considered if the appropriate linear phase FIR filter is not feasible for the rejection required).

$\endgroup$
4
  • 1
    $\begingroup$ It looks like with improved NCO my receiver works much more better. At the moment AM detector works great unlike FM one but it's another issue for other topic. $\endgroup$ Commented Jun 12 at 14:17
  • $\begingroup$ Oh thanks for letting me know. I’m so glad this helped you. What is ultimately helpful is to have the tools in place to capture the Rx signal at various stages in the receiver for post processing validation, and making accurate SNR assessments based on how the signal is expected to look at that particular place. I go through this in more detail in my presentation at the 2024 DSP Online Conference: dsponlineconference.com/session/… All the Python tools I have developed for this purpose are provided if you happen to use Python as well $\endgroup$ Commented Jun 12 at 15:18
  • $\begingroup$ I agree with you that the tools for capture the RX signals at various stages (like ADC samples and so on) is helpful. In my simple receiver I can only use DAC of microcontroller and oscilloscope to measure what's going on. It's better than nothing. IIR filters for IQ provides issue with group delay that isn't constant. I used Chebyshev filters as analog filter prototype to get better attenuation for f>fc but it's more issues with nonlinear phase. Obviously STM32F407 is too slow for high order FIR. With some improvements I achieved at least understandable audio for FM broadcast radio stations. $\endgroup$ Commented Jun 14 at 12:33
  • $\begingroup$ @elektorix nice and that all makes sense. If you haven’t already, a bit and cycle accurate simulation model of what is happening at each stage could be helpful as well (if performance balanced with resources available is critical). Basically you want to know how bad you can be. $\endgroup$ Commented Jun 14 at 16:01
1
$\begingroup$

It is an integer ratio -- it's $\frac {855}{200}$. You can even simplify that down to $\frac {171}{40}$.

Taking the second case, make a table with 171 entries, then count in steps of 40, modulo 171. I.e. your steps would go $0, 40, 80, 120, 160, 29, 69, 109, 149, 18, \cdots$.

The more usual way to do this, for any arbitrary frequency $f$, is to use a table that's $2^N$ elements long, with $N$ chosen to give you whatever precision you desire (or use a shorter table and linear interpolation -- the choice is yours). Then use integer math with a word length, $M$, that gives you the frequency accuracy you need -- $M = 16$ would give you a frequency accuracy of $\frac {855 \mathrm{kHz}}{2^{16}}$ or close to $13 \mathrm{Hz}$, $M = 32$ would give you a frequency accuracy of $\frac {855 \mathrm{kHz}}{2^{32}}$, etc. 32 bits is cheap these days, and one part in $2^{32}$ is probably way better than any time base you can get your hands on -- and if it's not, go with 64, or 128 bits*.

Then calculate the increment that gives you your desired frequency. It'll be $k = 2^M \frac{855 \mathrm{kHz}}{f}$. Then add that to your phase accumulator at each step. The natural hardware thing to with this operation is to perform it modulo $2^M$, so figure out how to make that happen in your chosen environment (in C, you'd use unsigned int, it's a slam dunk in Verilog and VHDL, and pretty much any other language you choose can do it, too).

Use the most significant $N$ bits in your accumulator as a lookup into your table -- and there you are. Any arbitrary frequency, easily and quickly.


* In 2024 the best atomic clock had a frequency uncertainty of $8.1 \cdot 10^{-19}$. That's just a hair smaller than the uncertainty introduced by an accumulator that's 60 bits wide. So if you're working at NIST (or if you're reading this in 2044) you might want to go with 128 bits. But for mere mortals with crystal oscillators, 32 bits is probably enough.

$\endgroup$
5
  • $\begingroup$ Thank you @TimWescott for your response. It looks like really great solution. I'll explain my application in more details. $\endgroup$ Commented Jun 9 at 12:45
  • $\begingroup$ Just FYI, if your sample rate and oscillator frequency are totally constant, then rather than having logic to do the modulo 171 arithmetic with the index, you can reorder the entries in the table to be in the order that you would draw them with steps (or stride) of 40. $\endgroup$ Commented Jun 9 at 17:54
  • 1
    $\begingroup$ (loudly smacks forehead). Um, yes. What Robert said. $\endgroup$ Commented Jun 9 at 22:01
  • $\begingroup$ It's also good idea to reorder my look-up tables. My sample rate is constant but I can't setup exactly 855 kHz that I want. I have to take into account some analog hardware issues so most probably I have to tune this oscillator frequency. It's unnecessary to do it really fast and tune it "on the fly". Anyway at the moment everything looks really promising. $\endgroup$ Commented Jun 10 at 9:35
  • $\begingroup$ @TimWescott Agree on your 32 bits is more than enough for this application. 60 bits would likely be more than enough for NIST with that optical clock frequency uncertainty (as that's what they can get to after long term averaging; we just need to be under the ADEV as it would be at the much faster update rate of frequency corrections in the clock loop) $\endgroup$ Commented Jun 11 at 11:09

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.