GNU Radio's DVBS2RX Package
|
Frame Synchronizer. More...
#include <pl_frame_sync.h>
Public Member Functions | |
frame_sync (int debug_level, uint8_t unlock_thresh=3) | |
Construct a new frame sync object. | |
bool | step (const gr_complex &in) |
Process the next input symbol. | |
void | set_frame_len (uint32_t len) |
Set the current PLFRAME length. | |
bool | is_locked () const |
Check whether frame lock has been achieved. | |
bool | is_locked_or_almost () const |
Check whether frame lock has been achieved or a SOF has been found. | |
uint32_t | get_sym_count () const |
Get the symbol count on the internal payload buffer. | |
uint32_t | get_sof_interval () const |
Get the interval between the last two detected SOFs. | |
const gr_complex * | get_plheader () const |
Get the PLHEADER buffered internally. | |
const gr_complex * | get_payload () const |
Get the PLFRAME payload (data + pilots) buffered internally. | |
const gr_complex * | get_sof_corr_taps () const |
Get the SOF correlator taps. | |
const gr_complex * | get_plsc_corr_taps () const |
Get the PLSC correlator taps. | |
float | get_timing_metric () const |
Get the last evaluated timing metric. | |
std::chrono::system_clock::time_point | get_lock_time () |
Get the frame lock timestamp. | |
![]() | |
pl_submodule (const std::string name, int debug_level) | |
Verbose logger. | |
Additional Inherited Members | |
![]() | |
gr::logger_ptr | d_logger |
Debug level. | |
Frame Synchronizer.
Searches for the start of PLFRAMEs by computing and adding independent cross-correlations between the SOF and the PLSC parts of the PLHEADER with respect to their expected values known a priori. The cross-correlations are based on the so-called differential metric given by x[n]*conj(x[n+1])
, namely based on the angle difference between consecutive symbols. This non-coherent approach allows for frame synchronization despite the presence of large frequency offsets. That's crucial because the fine frequency offset estimation requires correct decoding of the PLSC, which, in turn, requires the frame search provided by the frame synchronizer. Hence, the frame synchronizer should act first, before the carrier recovery.
Due to the interleaved Reed-Muller codeword construction (with a XOR given by the 7th PLSC bit), each pair of consecutive PLSC bits is either of equal or opposite bits. When the 7th PLSC bit is 0, all pairs of bits are equal, i.e., b[2i+1] = b[2i]
. In this case, the corresponding pair of scrambled bits are either equal to the original scrambler bits (when b[2i] = b[2i+1] = 0
) or their opposite (when b[2i] = b[2i+1] = 1
). In either case, the complex differential x[2i]*conj(x[2i+1])
is the same due to the pi/2 BPSK mapping (refer to the even-to-odd mapping rules in the body of function demap_bpsk_diff()
from pi2_bpsk.cc
). Hence, when the 7th PLSC bit is 0, the differential is determined by the scrambler sequence, not the actual PLSC value that is unknown at this point.
Next, consider the case when the 7th PLSC bit is 1 such that each pair of PLSC bits is composed of opposite bits, i.e., b[2i+1] = !b[2i]
. In this case, if b[2i] = 0
and b[2i+1] = 1
, the pair of scrambled bits becomes (s[2i], !s[2i+1])
. Otherwise, if b[2i] = 1
and b[2i+1] = 0
, the pair of scrambled bits becomes (!s[2i], s[2i+1])
. In either case, the complex differential is equal to the differential due to the scrambler sequence alone, but shifted by 180 degrees (i.e., by expj(j*pi)
, due to the pi/2 BPSK mapping rules (again, see demap_bpsk_diff()
). Thus, if the differentials due to the PLSC scrambler sequence are used as the correlator taps, the cross-correlation still yields a peak when processing the PLSC. The only difference is that the phase of the peak will be shifted by 180 degrees, but the magnitude will be the same. In this case, the 180-degree shift can be undone by taking the negative of the correlator peak.
In the end, the PLSC correlator is implemented based on the scrambler sequence alone (known a priori), and it is independent of the actual PLSC embedded on each incoming PLHEADER. This correlator is composed of 32 taps only, given that only the pairwise PLSC differentials are known a priori. In contrast, the SOF correlator is based on all the 25 known SOF differentials, given that the entire 26-symbol SOF sequence is known a priori.
The two correlators (SOF and PLSC) are expected to peak when they observe the SOF or PLSC in the input symbol sequence. The final timing metric is given by the sum or difference of these correlators, whichever has the largest magnitude. The sum (SOF + PLSC) peaks when the 7th PLSC bit is 0, and the difference (SOF - PLSC) peaks when the 7th PLSC bit is 1. That is, the difference metric essentially undoes the 180-degree shift on the PLSC correlator peak that would arise when the 7th bit is 1.
Furthermore, as stated before, the implementation is robust to frequency offsets. The input symbol sequence can have any frequency offset, as long as it doesn't change significantly in the course of the PLHEADER, which is typically the case given that the PLHEADER is short enough (for typical DVB-S2 baud rates). If the frequency offset is the same for symbols x[n] and x[n+1], the differential metric includes a factor given by:
Moreover, if the frequency offset remains the same over the entire PLHEADER, all differentials include this factor. Ultimately, the cross-correlation peak is still observed, just with a different phase (shifted by -2*pi*f0
). In fact, the phase of the complex timing metric (sum or difference between the correlator peaks) could be used to estimate the coarse frequency offset affecting the PLHEADER. However, a better method is implemented on the dedicated freq_sync
class.
Lastly, aside from the correlators, the implementation comprises a state machine with three states: "searching", "found", and "locked". As soon as an SOF is found, the state machine changes to the "found" state. At this point, the caller should decode the corresponding PLSC and call method set_frame_len()
to inform the expected PLFRAME length following the detected SOF. Then, if the next SOF comes exactly after the informed frame length, the state machine changes into the "locked" state. From this point on, the frame synchronizer will check the correlation peak (i.e., the so-called "timing metric") at the expected index on every frame.
Whenever the timing metric does not exceed a specific magnitude threshold, the implementation will increment an internal count for unlocking. After a chosen number of consecutive timing metric failures, this block will assume the frame lock has been lost and transition back to the "searching" state. At this point, it takes at least two more PLHEADERs to recover the lock, as the state machine needs to go over the "found" and "locked" states again.
gr::dvbs2rx::frame_sync::frame_sync | ( | int | debug_level, |
uint8_t | unlock_thresh = 3 |
||
) |
Construct a new frame sync object.
debug_level | (int) Target debugging log level (0 disables logs). |
unlock_thresh | (uint8_t) Number of consecutive frame detection failures before unlocking. A failure occurs when the timing metric does not exceed the expected magnitude threshold. By default, 3 failures will lead to unlocking. |
unlock_thresh
frames will likely fail, as the frame synchronizer will search for their PLHEADERs in wrong indexes. Hence, in this example, it is better to unlock reasonably fast than to wait further.
|
inline |
Get the frame lock timestamp.
Referenced by gr::dvbs2rx::plsync_cc_impl::get_lock_time().
|
inline |
Get the PLFRAME payload (data + pilots) buffered internally.
The payload observed between consecutive SOFs is buffered internally. If a SOF is missed such that the last two observed SOFs are spaced by more than the maximum payload length, only up to MAX_PLFRAME_PAYLOAD symbols are buffered internally.
|
inline |
Get the PLHEADER buffered internally.
References gr::dvbs2rx::cdeque< T >::back().
|
inline |
Get the PLSC correlator taps.
|
inline |
Get the SOF correlator taps.
|
inline |
Get the interval between the last two detected SOFs.
|
inline |
Get the symbol count on the internal payload buffer.
References MAX_PLFRAME_PAYLOAD.
|
inline |
Get the last evaluated timing metric.
Once locked, the timing metric updates only once per frame. Before that, it updates after every input symbol.
|
inline |
Check whether frame lock has been achieved.
|
inline |
Check whether frame lock has been achieved or a SOF has been found.
void gr::dvbs2rx::frame_sync::set_frame_len | ( | uint32_t | len | ) |
Set the current PLFRAME length.
This information is used to predict when the next SOF should be observed. If a timing metric peak is indeed observed at the next expected SOF index, the synchronizer achieves frame lock.
len | (uint32_t) Current PLFRAME length. |
bool gr::dvbs2rx::frame_sync::step | ( | const gr_complex & | in | ) |
Process the next input symbol.
in | (gr_complex &) Input symbol. |