Translated from Let's hack it-GOES Satellite Hunt (Part 2 – Demodulator), for learning reference only.
About BPSK
About BPSK
First, we have successfully obtained the LRIT signal from the GOES satellite. The first step in demodulation is to use SDR (Software Defined Radio), such as Airspy or RTLSDR, to convert the baseband signal into a symbol stream. Here, we will use GNU Radio to build our demodulator.
LRIT Signal Detailed Parameters
LRIT Signal Detailed Parameters

From the parameter table, we can see that the LRIT signal uses BPSK (Binary Phase Shift Keying, binary phase shift keying) modulation, with a symbol rate of 293883 symbols/second. The first problem we face is, how does BPSK modulation work?
First, we have two signals: the carrier and the binary stream we want to send. The carrier is a sine wave that will transmit the modulated data along the path. Its frequency is usually much higher than the bit rate or symbol rate. In BPSK modulation, we basically achieve modulation by changing the phase of the signal. Since it is binary phase shift, we basically only reverse the phase of the signal, that is, reverse the local polarity of the signal. This can be seen in the figure below, where we produce a "phase reversal" on the carrier when we change the polarity. This is the method of using binary phase shift to modulate bits onto the carrier. The advantage of changing the phase is that we only need to get the same phase at the receiving end, without requiring a very high signal amplitude.

BPSK Modulation
Therefore:
- When the transmitted value is “1”, the carrier phase shift is 0 degrees.
- When the transmitted value is “0”, the carrier phase shift is 180 degrees (inverted).
It's actually very simple, right? So we can represent our data in a phase diagram. Here are some phase diagrams for PSK modulations (BPSK, QPSK, 8-PSK)

Thus, for the constellation diagram of BPSK, we basically have:
- For positive I (quadrants 0 and 3), representing “0”.
- For negative I (quadrants 1 and 2), representing “1”.
For reference, in QPSK (b) we will use the binary values of Gray coding: 00, 01, 11, 10.
This also applies to 8-PSK, but 8-PSK uses 3 bits to form the data. The binary code is usually the Gray code starting from 0 and increasing counterclockwise.
Demodulating BPSK Signal
So, reversing this modulation process, which is the demodulation method we are going to study.
First, we need to ensure that our carrier is at baseband (spectrum center). You might think that just tuning to 1691MHz would ensure this, but we need to consider the following factors:
- SDR tuning crystals are not 100% accurate and will have some drift from the actual signal.
- Temperature changes on the SDR will also cause signal drift.
- Wind, atmosphere, and other factors will cause the signal to have Doppler shift.
What to do? We need to find the true location of the signal and convert it to baseband (spectrum center). But we encounter a problem:
The LRIT signal actually does not contain a carrier, only the modulated signal (so it is the result of modulating a carrier).
If the signal contained a carrier, we could use a simple PLL (Phase-Locked Loop) to track the carrier and convert the signal. But since the carrier would consume transmission power, to track the signal, the transmission power from the satellite would be "wasted" in the signal. For carrier-less signals like LRIT, we need to use a more complex PLL system. There are several systems that can do this, but I will use the famous Costas loop suggested by Trango in #hearsat.

Costas Loop Basic Diagram
Costas Loop is basically a dual PLL system. The main idea is: if we assume that the carrier is not centered, a phase error will appear, which will accumulate in the orthogonal signals. Using this error, we can convert our signal to a baseband signal. The actual details of how Costas Loop works will not be discussed here, as there are many documents that explain its working principle. For BPSK, we will use a second-order Costas Loop, and there are also third-order Costas Loop for QPSK and fourth-order Costas Loop for 8-PSK.
Okay, after moving the signal to the baseband, we need to recover the second piece of information: the symbol clock. The problem is: we modulated a binary signal on the carrier, but if we have a string of 10 bits, how do we know that the string has 10 bits, rather than 5 bits or 7 bits? We need to use the M&M (Mueller and Müller) algorithm to recover the original clock.
Why can we recover the original data clock?
Because we have two pieces of information:
- The estimated symbol rate (in this case, 293883 symbols/second)
- The carrier phase transitions between 0 and 1 and 1 and 0
How can we recover with these pieces of information? It's simple, we can make an oscillator (clock generator) with the estimated symbol rate and synchronize it with the carrier phase transitions, after which we can think that we have found a synchronized clock. Additionally, since the data is randomized (which will be shown further in this article), the binary data is almost random, which means that we will have roughly the same chance of getting a bit 1 or 0. This will make our clock synchronization more consistent over time. Moreover, the M&M algorithm also does an additional thing, which is clock frequency correction. Since the symbol rate is just an estimated assumption, it means that its value may change over time, and the M&M algorithm can correct it for us.
After recovering the clock, in the I vector of the IQ Sample, we will get the signal corresponding to the symbols.
GNU Radio Design Process
Let's open GNU Radio and build our demodulator. In GNU Radio, we will add some extra blocks in Costas Loop and M&M Recovery. The values of the parameters in this design are suitable for Airspy R2 / Mini, but I will also provide a version for RTLSDR soon (which only takes different values in the first step). As suggested by Trango in #hearsat, to avoid USB packet loss, it's better to use a lower sampling rate in airspy (to be honest, I've never had any USB packet loss when using it, but this will depend largely on your CPU and USB controller, so it's better to be cautious when choosing the sampling rate). For Airspy R2, we will use 2.5Msps, and for Airspy mini, we can use 3Msps. The target sampling rate for our entire process is 1.25Msps (in fact, we can use any value close to this). So, let's start with the osmocom block:

Osmocom Source
Let us set the sampling rate to 3e6, the center frequency to 1691e6, and all gains to 15. For gain settings, you can try your own values, but I found that I got the best signal-to-noise ratio when all parameters were set to their maximum values (which is not very common). Additionally, the Osmocom Source has a "bug" for airspy in that it does not get the mixer gain available (because it is not BB gain, which is stupid). I made a patch (which was actively rejected due to the gain name) that maps the mixer gain to BB gain (like RTLSDR). In the future, I may write a new GRC block to use with airspy with the correct name, but for now, you can compile from source: gr-osmosdr using my fork.
抽取并滤波至理想采样率
The next step is to resample to an ideal sampling rate of 2.5e6. For the airspy mini, at a sampling rate of 3e6, it is 15/18. So, let us create a Rational Resampler block and set 15 as the interpolation and 18 as the decimation. Taps can be left empty because GNU Radio will generate them automatically. This is not ideal, but at least it works for now. I will release better versions for each SDR in the future.

Now we have 2.5 Msps, we need to reduce the sampling rate by half. But we will also pass the input through a low-pass filter to reach our sampling rate. So, let's create a low-pass filter with a decimation factor of 2, a sampling rate of 2.5e6, a cutoff frequency of symbol_rate * 2 (i.e., 587766), and a transition width of 50e3.

After this step, the sampling rate will become 1.25e6
自动增益控制以及根余弦滤波器 Automatic Gain Control and Root Raised Cosine Filter
To get better performance, regardless of the input signal, we should keep the signal at a constant level. To do this, we will use automatic gain control, which will perform software gain (basically just amplifying the signal by multiplication), without changing the resolution (so it will not output a better signal), but can keep our levels constant. We can use GNU Radio's AGC module.

The rate is 10e-3, the reference value is 0.5, the gain is 0.5, and the maximum gain is 4000.
Another step is the RRC filter (Root Raised Cosine filter). This is a filter optimized for nPSK modulation and is used as a parameter for the symbol rate. The filter is not difficult to generate (it is an FIR with some specific Taps), but fortunately, GNU Radio provides a module for us.

RRC filter
For the parameters of the RRC filter, we will use a sampling rate of 1.25e6, a symbol rate of 293883, Alpha=0.5, and Num Taps=361. Alpha and the symbol rate are taken from the parameters of LRIT. The value of Taps can be tried freely, but I found that taking 361 achieves a good balance between quality and performance. After filtering, we should be able to get a signal that only contains BPSK modulation signals (or noise in the same frequency band). Then we can move on to the synchronization and clock recovery steps.
Synchronization and Clock Recovery
As I mentioned earlier, we will use a second-order Costas loop for carrier recovery (synchronization) and the M&M clock recovery algorithm to recover the symbol clock. GNU Radio provides modules for both algorithms. Let's start with the Costas Loop.

Costas Loop
For the parameters of the Costas loop, we only need to set the loop bandwidth to 0.00199 and the order to 2. After that, we should have our virtual carrier in the baseband. Now we just need to use the M&M clock recovery algorithm to synchronize our samples with the clock.

M&M Clock Recovery
For the parameters of the M&M algorithm, we take Omega as 4.25339, which is basically the number of symbols transmitted per sample rate, or sample_rate / symbol_rate. This is the first symbolic guess for the M&M algorithm. For Gain Omega, we use (alpha ^ 2) / 4, where alpha = 3.7e-3, so our Gain Omega is 3.4225e-6, Mu is 0.5, Gain Mu is alpha (or 3.7e-3), and Omega Relative is limited to 5e-3.
So you can notice that I called a new parameter alpha in the M&M algorithm, which is not a direct parameter of the module. This alpha is a parameter used to adjust the degree to which the M&M clock recovery algorithm deviates from the initial guess. You can try using your own values, but 3.7e-3 is the best choice for me.
Now, in the output of M&M, we will extract complex symbols at the correct rate. Now we just need to extract the numerical values from them.
从GNU Radio输出符号
So now we can directly map the obtained signal to binary data, but for reasons I will explain in the next part of the article, I will keep the symbols unchanged and only convert them to bytes. So basically, the output of GNU Radio will be a signed byte, ranging from -128 to 127, with -128 being a 100% chance of being 0, and 127 being a 100% chance of being 1. The middle values will be the corresponding probabilities. Basically, I will have a byte representing the probability of a bit being 0 or 1. I will explain more in the next part of the article.
Now what we need to do is to get the Complex output from the M&M module, only get the Real Part (Component I) converted to byte and output (output to a file or TCP pipe).

This is a simple operation using two modules. First, we use the Complex to Real block, which outputs the Real component of the complex number as a Float, and then converts it to char multiplied by 127 (because the Complex has been normalized). Afterwards, we can use the File Sink to output to a file or create a vector stream to output to a TCP Socket. I will be using the TCP socket.

Before sending through TCP, Stream to Vector only aggregates every 16 bytes. This will reduce TCP packet overhead. The TCP Sink parameters are:
- Input type: bytes
- Address: 127.0.0.1
- Port: 5000
- Mode: client
- Length: 16
Using these parameters, when we run the GRC flow, it will attempt to connect to localhost through port 5000 and send every group of 16 bytes (or you can also choose to send symbols) through TCP. The next part of this article will discuss the software part of decoding this issue and generating packets to create output files. You can see my final flow graph below. It also has some other blocks that can change parameters more flexibly, and can also display the waterfall/fft graph and constellation graph of the input signal.

GRC source file download link
若无法加载请检查网络环境。
若无法加载请检查网络环境,或切回 Disqus 稍后再试。