I have been playing with a downlink receiver flowgraph for the QO-100 narrowband transponder in GNU Radio. There appear to be a multitude of interesting methods people use to cancel LNB drift, ranging from the “classic” GPSDO approach to using a stable 10 GHz source injected into the LNB (PA0SSB does that). Also, some FFT-based approaches for locking to the upper PSK400 beacon are in use as well as systems that lock to the Engineering beacons in the “commercial” Ku band such as the one developed by DB8TF. I wanted to try phaselocking to the 400 baud PSK beacon at the upper edge of the passband, this post describes how it was done.
QO-100 upper beacon
The upper beacon is a Binary Phase Shift Keying (BPSK) beacon at 400 bit/s (uncoded), identical to AO-40’s uncoded beacon. Manchester encoding is used, which is a clever way of embedding the data clock into the signal, and this removes energy at DC which for some applications can be helpful. It is uncoded, meaning that no Forward Error Correction (FEC) is used. For QO-100, that is fine, as there is no spin fading to counteract like on AO-40 and the SNR is large enough such that bit errors are minimal.
Carrier recovery for phase shift keyed signals
BPSK (manchester encoded or not) with 180 degrees phase difference between each symbol is a form of suppressed carrier modulation, and is in fact the digital equivalent of analog Double Sideband (DSB) modulation. This means, that there is no carrier to lock onto. An ordinary PLL (acting on a real signal) is unable to properly track such a signal. However, a Costas loop (which is a complex form of a PLL) is able to do that. In fact, Costas loops are used to coherently demodulate PSK signals, where the Costas loop locks to the (suppressed) carrier, regenerates this carrier coherently (meaning that it is in-phase with the original carrier), and therefore can be used to demodulate BPSK by multiplying that carrier with the incoming data stream and subsequently low pass filtering it. Mathematically, it can be proven that this form of demodulation is optimal, yielding the theoretically expected performance in terms of bit error rate as a function of SNR (or its digital equivalent, the” Energy per bit divided by the noise power spectral density” ratio or Eb/N0 in short).
Application in cancelling LNB drift
Now, if we use a Costas loop to demodulate QO-100’s beacon signal, we can recover the original carrier phase. Since frequency is mathematically speaking the time derivative of phase, we automatically also recover the frequency. And that is what we can use to cancel LNB drift.
All signals coming from the LNB, including the beacon, will experience the same frequency drift as the LNB’s reference crystal (typically 25 or 27 MHz) drifts a bit with frequency. Note that for a 25 MHz crystal, and an LNB set to receive the “low” Ku band between 10700 and 11700 GHz, the LO frequency is 9750 MHz which means that the LNB reference is multiplied by 9750 / 25 = 390. So if the crystal drifts by 100 Hz, the LNB LO drifts by a whopping 39 kHz… way too much for narrowband signals such as SSB or CW (or digimodes).
So, if we can generate a local copy of the beacon carrier, and determine its frequency offset relative to the expected frequency of the beacon, we can then frequency shift the entire incoming frequency band by that amount. The result is that the LNB drift is cancelled.
Loop bandwidth and such
Now there is a bit of a caveat, any feedback control loop (and the Costas loop is a feedback control loop) can only counteract disturbances which fall within its loop bandwidth. So that means that if the LNB LO frequency (and thereby phase) varies a lot with time, the loop is unable to compensate. Time variation of phase is equivalent to phase noise, so theoretically it means that the carrier regenerated by the Costas loop has a phase noise which is dictated by its reference (the beacon signal), within its loop bandwidth. (There is a whole lot more mechanism playing a part here but I am leaving that out for clarity’s sake).
Implementation in GNU Radio
So, let’s get to work. Fortunately, GNU Radio includes all the blocks we need as part of its source tree, so no need to install Out Of Tree (OOT) blocks.
SSB receiver – input stage
First, signals from the LNB enter the flowgraph through the USRP source block. There they are mixed in a complex mixer (i.e. one that simply shifts the frequency spectrum) by a signal generated by the drift correction receiver. But let’s assume that that signal has 0 Hz frequency right now, so the mixer does not do any frequency translation. Then, the signal is passed to a waterfall and FFT sink for displaying the transponder signals. 2 subsequent mixers and associated oscillators perform tuning by translating the input spectrum such that the wanted signal (suppressed carrier) appears at 0Hz (note that the SSB demodulation happens here). 2 mixers are used, one for coarse tuning and one for fine tuning. The complex conjugate operator is used to convert from positive to negative frequencies to ensure the frequency translation direction is correct. The coarse tuning oscillator can be set by double-clicking on a signal in the waterfall. (Can also be done from the FFT, for that, connect the “freq” message port of the QT GUI Frequency sink to the coarse tuning oscillator “freq” message port as well.
The USRP center frequency is determined by a slider which sets the approximate LNB reference frequency (labelled “LNB frequency correction”). From that, the LNB LO is calculated and from that, the IF frequency is calculated (around 739 MHz). This IF frequency is then set in the USRP.
SSB receiver – filtering and decimation
Note that now that the SSB signal has been downconverted, we can reduce the sample rate. This will greatly limit the amount of computations required in filtering that is performed on the SSB signal. A rational resampler is used to convert to 44.2 ksamples/sec. After that, a bandpass filter that filters positive frequencies (USB) between 300 and 2700 Hz is used as the SSB filter. After that, a fast attack / slow decay AGC controls the output level. Then, in order to be able to process the signal as a float (and since we have rejected the opposite sideband), we can convert the signal to a real signal. Optionally, a tone from another oscillator can be added to the audio which is representative of the level of the beacon. This can be used for aiming the dish, more on that later. Note that for the SSB receiver, I was inspired by Alex OZ9AEC’s flowgraph which can be found here, thank Alex! Note that the sample rate of the receiver is slightly higher than that of the audio sink (44.2 kHz instead of 44.1 kHz), in this way, the common two-clock problem can be avoided, at the expense of slight frequency inaccuracy in the reproduced output signal.
Up till now, the flowgraph behaves like a normal SSB receiver, which can be tuned anywhere within the transponder bandwidth. Now, let’s look at the receiver chain for the beacon.
The beacon signal receiver is centered around the expected beacon frequency, assuming that the transponder center frequency is at zero frequency it would appear at + 125 kHz.
A frequency translating FIR filter is used to downconvert the beacon signal to approximately 0 Hz. (Note that in fact, the SSB receiver described above could simply be replaced by such a frequency translating FIR filter, as it does exactly the same). The filter also performs decimation by a factor of 10, to 48 ksamples/sec. Next, the signal passes through an AGC, this AGC is there because the Costas loop behaviour (in particular, its loop gain) is dependent on the magnitude of the input signals. The AGC makes sure that this magnitude is kept constant. The output of the ferquency translating FIR filter is also passed to a block that measures the average power of the beacon signal, this value is then converted (using a probe signal block) to a tone by means of an oscillator whose signal (at audio) can be optionally added to the audio from the SSB receiver. This can act as a beacon strength indicator, helpful for aiming the dish. The downside is that there is a bit of latency on this signal so moving the dish should be done very slowly.
The Costas loop outputs a number of streams, first of all a complex stream which is the “downconverted” spectrum (i.e., the input signal multiplied by the regenerated carrier). This stream is connected to an FFT sink which can act as a lock indicator. When the loop is locked, the “null” of the beacon signal (i.e. the valley in the middle) coincides with 0 Hz.
The other output of the Costas loop labeled “freq” outputs a float representing the frequency of the recovered carrier, in radians per sample. This is the output we will use to correct frequency drift.
Closing the loop
The floats (at 48 ksample/sec) out of the Costas loop representing frequency are passed to a multiplier, which multiplies with either 0 or 1, depending on whether “beacon track” is enabled. a Probe signal block is used to tap off the frequency value, and display it in the GUI. Since we have to ultimately multiply the locally generated carrier with the incoming stream at 480 ksamples/sec, a rational resampler interpolates back to that rate. Finally, a VCO block is used to generate the mixing signal. Its sensitivity is set to 48000 / (radians per sample) to set the proper output frequency scaling. The minus sign is used to ensure that the drift correction term is applied in the proper direction. Like the SSB receiver, a “Complex Conjugate” block could have been used just as well here. The VCO output is fed to the first mixer in the flowgraph, described in the SSB receiver section. Now, when the loop is locked, the input to the SSB receiver is automatically drift-corrected.
Notes on Costas loop bandwidth
Note that in GNU Radio, the Costas loop bandwidth is expressed in terms of normalized frequency relative to the sample rate. Next to that, it is expressed as angular frequency in radians per second. Remember that 1 Hz = (2*PI) rad /sec. So for a sample rate of 48 ksamples/sec, a loop bandwidht of 1 kHz would be expressed as (1000/48000) * (2*PI) = 0.131 radians per sample. The “magic” recommended loop bandwidth for the Costas loop commonly used in GNU Radio is 2*PI / 100 = 0.0628 rad / sample which in our case equates to 480 Hz. I made the loop bandwidth adjustable by means of a slider so I could experiment to find the best setting. For me what works, is to start at 0.01 rad/sec, set the slider labelled “LNB frequency correction” such that the beacon is at approx 125 kHz, and then lower the loop bandwidth to 0.002 after the loop locks.
Notes on input SNR
It appears that the system needs a certain minimum SNR of the beacon to lock reliably. Trials by the author with a 35×45 cm dish have shown that it is possible to achieve lock, but there is not much margin. Larger dishes should work better.
Download & operation
The flowgraph can be found in this Github repository
Operation of the flowgraph is as follows.
- Adjust the slider “LNB frequency correction” such that the upper beacon appears at approximately +125 kHz in the “corrected input spectrum” FFT and waterfall. If another frequency is preferred, change the “expected beacon frequency” value. Bandwidth of the beacon tracking receiver is set to +/- 10 kHz (so 20 kHz in total). Some experimentation may be required to set a good value depending on the total amount of LNB drift.
- Check that the beacon signal is visible in the Costas loop output FFT. Since the loop is not yet locked at this stage, the valley between the 2 “humps” will most likely be not near 0 Hz
- Tick “beacon track enable”, and check that the valley moves to 0 Hz exactly
- Lower the loop bandwidth to 0.002 or any other value that works best. I set the SSB receiver to the lower beacon (tune to -124 kHz to receive it with 1 kHz pitch, assuming a beacon frequency of + 125 kHz), and listen to the quality of the tone while I adjust the loop bandwidth slider
- Tune the SSB receiver to listen to the transponder.
The following steps are planned:
- integrate EA4GPZ’s GNU Radio QO-100 beacon decoder (https://destevez.net/2019/02/decoding-the-qo-100-beacon-with-gr-satellites/ )
- integrate with my uplink generator flowgraph to yield a complete SSB transceiver.