Automatic Carrier Frequency Tuning CW TUNE HELPER & SNAP MODE - df8oe/UHSDR GitHub Wiki
Text and figures © DD4WH, under GNU GPLv3
CW TUNE HELPER
This works as a graphical helper to match your CW RX frequency to the frequency of your QSO partner. It matches your CW offset to your sidetone frequency and graphically shows you the difference between the two. This is called "CW SPOT" in the Elecraft KX3 and works very similar in the UHSDR software. The range of the graphical display is +-150Hz and the accuracy of determining the CW carrier frequency is +-2Hz (relatively independent of spectrum/waterfall magnify mode, however accuracy is a little better in higher magnify modes): You will probably be able to match your QSO partners´ frequency within +-5Hz in real time operation.
A small green box is displayed on the left side of the display above the DSP box: tune into a CW station and adjust your frequency, so that the little vertical yellow indicator is exactly in the centre of the box --> Done!
how to use it
- enable "carrier snap" in Debug menu: entry "Do not use: cs" [only use, if you are brave ;-)]
- enable CW decoder in CW menu
- adjust CW decoder threshold, so that CW station modulates the red LED
- change Demod_mode to CW
- for highest frequency estimation accuracy: set display menu "spectrum FFT window" to "Hann"
- adjust frequency of a CW carrier to match your sidetone by graphically matching yellow mark to centre of green box (above the DSP box)
How does the frequency snap mode work ?
The automatic tuning process is based on determining the difference between your tuning frequency and the carrier frequency of the station you are listening to. It is based on the results of a complex FFT (which is executed on demand when you use the SNAP button, but we could also use the FFT of the spectrum display, so the data is already there and does not consume additional computing resources). The values of the complex FFT are magnitude values of signal strength for 256bins across the whole spectrum of 48000Hz. That means, one calculated value for one bin holds the average magnitude for a bin bandwidth (binBW) of 187.5Hz = 48000Hz / 256. A bin bandwidth of 187.5Hz is of course not accurate enough for precision tuning, as necessary for DSB or StereoAM listening. We would need an accuracy of a few Hz to be able to listen to a signal in these modes without annoying heterodynes or oscillations of the signal. This is achievable by inter‐ bin three‐point quadratic interpolation! You use the magnitude values of three bins adjacent to each other and use these values to interpolate a quadratic curve (which stands on its “head”) (Jacobsen & Kootsookos 2007, www.ericjacobsen.org, Thanks vladn for pointing me to this method and this and other papers!). That’s all. Now you would set up a tuning process:
I used a very simple process, only two frequency steps are sufficient for very accurate tuning to the carrier:
- determine the bin numbers, between which the maximum FFT magnitude is being searched for(bins within the passband, as I can use different USB & LSB bandwidths in DSB tuning (passband tuning) mode)
- search for maximum FFT magnitude value in that frequency range
- coarse tune to that bin (delta = deltabins * binBW)
- interpolate with three‐point‐quadratic interpolater (equation (4) of Jacobsen & Kootsookos 2007) with the three bins in the centre of my frequency
- fine tune: set freq to that interpolation ‐‐> delta = binBW * (1.36 * (bin3 ‐ bin1)) / (bin1 + bin2 + bin3); for maximum accuracy, this requires the FFT window type to be set to Hanning !
- done!
I tested several formulas (eg. Lyons (2011)): The paper by Jacobsen & Kootsookos (2007) had the final implementation formula. As I used a complex FFT library, that already calculated magnitudes, I was looking for a formula for magnitudes. Also I wanted to use Hanning, so their equation (4) solved my problem: delta = binBW * (1.36 * (bin3 ‐ bin1)) / (bin1 + bin2 + bin3); This is astonishingly accurate! With a fs = 48000Hz and 256point FFT I have an accuracy of 2‐3Hz with strong signals, and 4‐5Hz with very noisy signals. Thats perfect with a binBW of 187.5Hz in my view.
With this method it was possible to implement a fine tuning graphical helper for zero beat tuning in CW. We do this by the following steps:
• we have the FFT already done for spectrum display/waterfall display
• take the bins from the display FFT and search in the bins that are in the filter passband (which is usually quite narrow in CW) for the loudest signal (do averaging over several FFT loops). Note the number of that bin
• take the signal values of that bin, the bin below and the bin above (do that several times and average to account for pauses between the dits and dahs) and calculate a three-bin quadratic interpolation to exactly determine the frequency of that signal (with the formula shown above)
• calculate the deviation of the received CW signal from Rx frequency +- CW sidetone
• show this deviation graphically inside the spectrum display or waterfall display
This method is pretty accurate to at least 5Hz in the lowest magnify mode (1x) and becomes even more accurate with higher magnify modes.
Jacobsen, & Kootsookos (2007): Fast, accurate frequency estimators. - IEEE SIGNAL PROCESSING MAGAZINE [125] MAY 2007. https://www.researchgate.net/publication/3321864_Fast_Accurate_Frequency_Estimators_DSP_Tips_Tricks
Lyons, R.S. (2011): Understanding Digital Signal Processing, Prentice-Hall, 3rd Ed.
Details for programmers
How do we process for the graphical TUNE HELPER? [2017_09_15]
- Ui_Spectrum_RedrawSpectrum()
- apply window function to 256 samples for spectrum/waterfall display
- do FFT
- calculate complex magnitude values
- do scaling
- do lowpass filtering
- if(we have a valid pulse recognized by the CW_decoder routine) jump to UiSpectrum_CalculateDBm
- calculate bin bandwidth --> bin_BW
- calculate LBin and Ubin from magnify mode & frequency conversion & filterpassband
- rearrange data in sd.FFT_Samples (the buffer with the lowpass-filtered, scaled complex magnitudes from the FFT)
- jump into UiSpectrum_CalculateSnap (Lbin, Ubin, posbin, bin_BW)
- search for max magnitude value inside the passband (between Lbin and Ubin)
- maxbin is the bin number of the bin with that max magnitude
- calculate offset to Rx frequency depending on ts.cw_lsb (CW-L or CW-U)
- delta1 is first offset in steps of bin_BW
- delta2 is second offset determined by quadratic interpolation of the three bins maxbin-1, maxbin and maxbin+1 (delta2 is always < bin_BW !)
- delta = delta1 + delta2
- CW-L: delta = delta + cw_sidetone_freq;
- CW-U: delta = delta - cw_sidetone_freq;
- jump to UiSpectrum_CWSnapDisplay (delta)
- clamp data for display (display is +-140Hz at the moment)
- draw yellow indicator with accuracy of 5Hz/pixel
- done