M17 demodulation improvement
Add logging in syncword sweep, fix unsigned underflow bug, fixed symbol average computation for quantization. Do syncword sweep whenever available. TG-81
This commit is contained in:
parent
d17d683b2d
commit
29ad0830f0
|
|
@ -37,6 +37,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <M17/M17Datatypes.h>
|
#include <M17/M17Datatypes.h>
|
||||||
#include <M17/M17Constants.h>
|
#include <M17/M17Constants.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace M17
|
namespace M17
|
||||||
{
|
{
|
||||||
|
|
@ -116,11 +117,14 @@ private:
|
||||||
*/
|
*/
|
||||||
static constexpr size_t M17_RX_SAMPLE_RATE = 24000;
|
static constexpr size_t M17_RX_SAMPLE_RATE = 24000;
|
||||||
|
|
||||||
|
|
||||||
static constexpr size_t M17_SAMPLES_PER_SYMBOL = M17_RX_SAMPLE_RATE / M17_SYMBOL_RATE;
|
static constexpr size_t M17_SAMPLES_PER_SYMBOL = M17_RX_SAMPLE_RATE / M17_SYMBOL_RATE;
|
||||||
static constexpr size_t M17_FRAME_SAMPLES = M17_FRAME_SYMBOLS * M17_SAMPLES_PER_SYMBOL;
|
static constexpr size_t M17_FRAME_SAMPLES = M17_FRAME_SYMBOLS * M17_SAMPLES_PER_SYMBOL;
|
||||||
static constexpr size_t M17_SAMPLE_BUF_SIZE = M17_FRAME_SAMPLES / 2;
|
static constexpr size_t M17_SAMPLE_BUF_SIZE = M17_FRAME_SAMPLES / 2;
|
||||||
static constexpr size_t M17_SYNCWORD_SAMPLES = M17_SAMPLES_PER_SYMBOL * M17_SYNCWORD_SYMBOLS;
|
static constexpr size_t M17_SYNCWORD_SAMPLES = M17_SAMPLES_PER_SYMBOL * M17_SYNCWORD_SYMBOLS;
|
||||||
static constexpr size_t M17_BRIDGE_SIZE = M17_SYNCWORD_SAMPLES;
|
static constexpr int8_t SYNC_SWEEP_WIDTH = 10;
|
||||||
|
static constexpr int8_t SYNC_SWEEP_OFFSET = ceil(SYNC_SWEEP_WIDTH / M17_SAMPLES_PER_SYMBOL);
|
||||||
|
static constexpr size_t M17_BRIDGE_SIZE = M17_SYNCWORD_SAMPLES + 2 * SYNC_SWEEP_WIDTH;
|
||||||
|
|
||||||
static constexpr float CONV_STATS_ALPHA = 0.005f;
|
static constexpr float CONV_STATS_ALPHA = 0.005f;
|
||||||
static constexpr float CONV_THRESHOLD_FACTOR = 3.40;
|
static constexpr float CONV_THRESHOLD_FACTOR = 3.40;
|
||||||
|
|
@ -146,7 +150,7 @@ private:
|
||||||
bool locked; ///< A syncword was correctly demodulated.
|
bool locked; ///< A syncword was correctly demodulated.
|
||||||
bool newFrame; ///< A new frame has been fully decoded.
|
bool newFrame; ///< A new frame has been fully decoded.
|
||||||
int16_t basebandBridge[M17_BRIDGE_SIZE] = { 0 }; ///< Bridge buffer
|
int16_t basebandBridge[M17_BRIDGE_SIZE] = { 0 }; ///< Bridge buffer
|
||||||
uint16_t phase; ///< Phase of the signal w.r.t. sampling
|
int16_t phase; ///< Phase of the signal w.r.t. sampling
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* State variables
|
* State variables
|
||||||
|
|
@ -161,10 +165,12 @@ private:
|
||||||
/*
|
/*
|
||||||
* Quantization statistics computation
|
* Quantization statistics computation
|
||||||
*/
|
*/
|
||||||
std::deque<int16_t> qnt_pos_fifo;
|
int8_t qnt_pos_cnt; ///< Number of received positive samples
|
||||||
std::deque<int16_t> qnt_neg_fifo;
|
int8_t qnt_neg_cnt; ///< Number of received negative samples
|
||||||
float qnt_pos_avg = 0.0f; ///< Rolling average of positive samples
|
int32_t qnt_pos_acc; ///< Accumulator for quantization average
|
||||||
float qnt_neg_avg = 0.0f; ///< Rolling average of negative samples
|
int32_t qnt_neg_acc; ///< Accumulator for quantization average
|
||||||
|
float qnt_pos_avg = 0.0f; ///< Rolling average of positive samples
|
||||||
|
float qnt_neg_avg = 0.0f; ///< Rolling average of negative samples
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DSP filter state
|
* DSP filter state
|
||||||
|
|
|
||||||
|
|
@ -198,24 +198,31 @@ void M17Demodulator::resetQuantizationStats()
|
||||||
void M17Demodulator::updateQuantizationStats(int32_t frame_index,
|
void M17Demodulator::updateQuantizationStats(int32_t frame_index,
|
||||||
int32_t symbol_index)
|
int32_t symbol_index)
|
||||||
{
|
{
|
||||||
int16_t sample = baseband.data[symbol_index];
|
int16_t sample = 0;
|
||||||
if (sample > 0)
|
// When we are at negative indices use bridge buffer
|
||||||
qnt_pos_fifo.push_front(sample);
|
if (symbol_index < 0)
|
||||||
|
sample = basebandBridge[M17_BRIDGE_SIZE + symbol_index];
|
||||||
else
|
else
|
||||||
qnt_neg_fifo.push_front(sample);
|
sample = baseband.data[symbol_index];
|
||||||
|
if (sample > 0)
|
||||||
|
{
|
||||||
|
qnt_pos_acc += sample;
|
||||||
|
qnt_pos_cnt++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qnt_neg_acc += sample;
|
||||||
|
qnt_neg_cnt++;
|
||||||
|
}
|
||||||
// If we reached end of the syncword, compute average and reset queue
|
// If we reached end of the syncword, compute average and reset queue
|
||||||
if(frame_index == M17_SYNCWORD_SYMBOLS - 1)
|
if(frame_index == M17_SYNCWORD_SYMBOLS - 1)
|
||||||
{
|
{
|
||||||
int32_t acc = 0;
|
qnt_pos_avg = qnt_pos_acc / static_cast<float>(qnt_pos_cnt);
|
||||||
for(auto e : qnt_pos_fifo)
|
qnt_neg_avg = qnt_neg_acc / static_cast<float>(qnt_neg_cnt);
|
||||||
acc += e;
|
qnt_pos_acc = 0;
|
||||||
qnt_pos_avg = acc / static_cast<float>(qnt_pos_fifo.size());
|
qnt_neg_acc = 0;
|
||||||
acc = 0;
|
qnt_pos_cnt = 0;
|
||||||
for(auto e : qnt_neg_fifo)
|
qnt_neg_cnt = 0;
|
||||||
acc += e;
|
|
||||||
qnt_neg_avg = acc / static_cast<float>(qnt_neg_fifo.size());
|
|
||||||
qnt_pos_fifo.clear();
|
|
||||||
qnt_neg_fifo.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,7 +254,7 @@ sync_t M17Demodulator::nextFrameSync(int32_t offset)
|
||||||
// Leverage the fact LSF syncword is the opposite of the frame syncword
|
// Leverage the fact LSF syncword is the opposite of the frame syncword
|
||||||
// to detect both syncwords at once. Stop early because convolution needs
|
// to detect both syncwords at once. Stop early because convolution needs
|
||||||
// access samples ahead of the starting offset.
|
// access samples ahead of the starting offset.
|
||||||
int32_t maxLen = static_cast < int32_t >(baseband.len - M17_BRIDGE_SIZE);
|
int32_t maxLen = static_cast < int32_t >(baseband.len - M17_SYNCWORD_SAMPLES);
|
||||||
for(int32_t i = offset; (syncword.index == -1) && (i < maxLen); i++)
|
for(int32_t i = offset; (syncword.index == -1) && (i < maxLen); i++)
|
||||||
{
|
{
|
||||||
int32_t conv = convolution(i, stream_syncword, M17_SYNCWORD_SYMBOLS);
|
int32_t conv = convolution(i, stream_syncword, M17_SYNCWORD_SYMBOLS);
|
||||||
|
|
@ -291,9 +298,9 @@ int8_t M17Demodulator::quantize(int32_t offset)
|
||||||
sample = basebandBridge[M17_BRIDGE_SIZE + offset];
|
sample = basebandBridge[M17_BRIDGE_SIZE + offset];
|
||||||
else // Otherwise use regular data buffer
|
else // Otherwise use regular data buffer
|
||||||
sample = baseband.data[offset];
|
sample = baseband.data[offset];
|
||||||
if (sample > static_cast< int16_t >(qnt_pos_avg / 2.0))
|
if (sample > static_cast< int16_t >(qnt_pos_avg / 1.5f))
|
||||||
return +3;
|
return +3;
|
||||||
else if (sample < static_cast< int16_t >(qnt_neg_avg / 2.0))
|
else if (sample < static_cast< int16_t >(qnt_neg_avg / 1.5f))
|
||||||
return -3;
|
return -3;
|
||||||
else if (sample > 0)
|
else if (sample > 0)
|
||||||
return +1;
|
return +1;
|
||||||
|
|
@ -308,7 +315,7 @@ const frame_t& M17Demodulator::getFrame()
|
||||||
return *activeFrame;
|
return *activeFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool M17::M17Demodulator::isLocked()
|
bool M17Demodulator::isLocked()
|
||||||
{
|
{
|
||||||
return locked;
|
return locked;
|
||||||
}
|
}
|
||||||
|
|
@ -317,12 +324,30 @@ int32_t M17Demodulator::syncwordSweep(int32_t offset)
|
||||||
{
|
{
|
||||||
int32_t max_conv = 0, max_index = 0;
|
int32_t max_conv = 0, max_index = 0;
|
||||||
// Start from 5 samples behind, end 5 samples after
|
// Start from 5 samples behind, end 5 samples after
|
||||||
for(int i = -5; i <= 5; i++)
|
for(int i = -SYNC_SWEEP_WIDTH; i <= SYNC_SWEEP_WIDTH; i++)
|
||||||
{
|
{
|
||||||
// TODO: Extend for LSF and BER syncwords
|
// TODO: Extend for LSF and BER syncwords
|
||||||
int32_t conv = convolution(offset + i,
|
int32_t conv = convolution(offset + i,
|
||||||
stream_syncword,
|
stream_syncword,
|
||||||
M17_SYNCWORD_SYMBOLS);
|
M17_SYNCWORD_SYMBOLS);
|
||||||
|
#ifdef ENABLE_DEMOD_LOG
|
||||||
|
int16_t sample;
|
||||||
|
if (offset + i < 0)
|
||||||
|
sample = basebandBridge[M17_BRIDGE_SIZE + offset + i];
|
||||||
|
else
|
||||||
|
sample = baseband.data[offset + i];
|
||||||
|
log_entry_t log;
|
||||||
|
log =
|
||||||
|
{
|
||||||
|
sample,
|
||||||
|
conv,
|
||||||
|
0.0,
|
||||||
|
offset + i,
|
||||||
|
0.0,0.0,0,0
|
||||||
|
};
|
||||||
|
|
||||||
|
logBuf.push(log, false);
|
||||||
|
#endif
|
||||||
if (conv > max_conv)
|
if (conv > max_conv)
|
||||||
{
|
{
|
||||||
max_conv = conv;
|
max_conv = conv;
|
||||||
|
|
@ -341,11 +366,11 @@ bool M17Demodulator::update()
|
||||||
// Read samples from the ADC
|
// Read samples from the ADC
|
||||||
baseband = inputStream_getData(basebandId);
|
baseband = inputStream_getData(basebandId);
|
||||||
|
|
||||||
// Apply DC removal filter
|
|
||||||
dsp_dcRemoval(&dsp_state, baseband.data, baseband.len);
|
|
||||||
|
|
||||||
if(baseband.data != NULL)
|
if(baseband.data != NULL)
|
||||||
{
|
{
|
||||||
|
// Apply DC removal filter
|
||||||
|
dsp_dcRemoval(&dsp_state, baseband.data, baseband.len);
|
||||||
|
|
||||||
// Apply RRC on the baseband buffer
|
// Apply RRC on the baseband buffer
|
||||||
for(size_t i = 0; i < baseband.len; i++)
|
for(size_t i = 0; i < baseband.len; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -354,9 +379,7 @@ bool M17Demodulator::update()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the buffer
|
// Process the buffer
|
||||||
while((syncword.index != -1) &&
|
while(syncword.index != -1)
|
||||||
((static_cast< int32_t >(M17_SAMPLES_PER_SYMBOL * decoded_syms) +
|
|
||||||
offset + phase) < static_cast < int32_t >(baseband.len)))
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// If we are not demodulating a syncword, search for one
|
// If we are not demodulating a syncword, search for one
|
||||||
|
|
@ -369,6 +392,7 @@ bool M17Demodulator::update()
|
||||||
offset = syncword.index + 1;
|
offset = syncword.index + 1;
|
||||||
phase = 0;
|
phase = 0;
|
||||||
frame_index = 0;
|
frame_index = 0;
|
||||||
|
decoded_syms = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// While we detected a syncword, demodulate available samples
|
// While we detected a syncword, demodulate available samples
|
||||||
|
|
@ -378,6 +402,8 @@ bool M17Demodulator::update()
|
||||||
int32_t symbol_index = offset
|
int32_t symbol_index = offset
|
||||||
+ phase
|
+ phase
|
||||||
+ (M17_SAMPLES_PER_SYMBOL * decoded_syms);
|
+ (M17_SAMPLES_PER_SYMBOL * decoded_syms);
|
||||||
|
if (symbol_index >= static_cast<int32_t>(baseband.len))
|
||||||
|
break;
|
||||||
// Update quantization stats only on syncwords
|
// Update quantization stats only on syncwords
|
||||||
if (frame_index < M17_SYNCWORD_SYMBOLS)
|
if (frame_index < M17_SYNCWORD_SYMBOLS)
|
||||||
updateQuantizationStats(frame_index, symbol_index);
|
updateQuantizationStats(frame_index, symbol_index);
|
||||||
|
|
@ -393,9 +419,9 @@ bool M17Demodulator::update()
|
||||||
log_entry_t log =
|
log_entry_t log =
|
||||||
{
|
{
|
||||||
baseband.data[symbol_index + i],
|
baseband.data[symbol_index + i],
|
||||||
0,0.0,symbol_index + i,
|
phase,0.0,symbol_index + i,
|
||||||
qnt_pos_avg / 2.0f,
|
qnt_pos_avg / 1.5f,
|
||||||
qnt_neg_avg / 2.0f,
|
qnt_neg_avg / 1.5f,
|
||||||
symbol,
|
symbol,
|
||||||
frame_index
|
frame_index
|
||||||
};
|
};
|
||||||
|
|
@ -409,20 +435,6 @@ bool M17Demodulator::update()
|
||||||
decoded_syms++;
|
decoded_syms++;
|
||||||
frame_index++;
|
frame_index++;
|
||||||
|
|
||||||
// If the frame buffer is full switch active and idle frame
|
|
||||||
if (frame_index == M17_FRAME_SYMBOLS)
|
|
||||||
{
|
|
||||||
std::swap(activeFrame, idleFrame);
|
|
||||||
frame_index = 0;
|
|
||||||
newFrame = true;
|
|
||||||
|
|
||||||
// Locate syncword to correct clock skew between Tx and Rx
|
|
||||||
int32_t expected_sync =
|
|
||||||
offset + phase + M17_SAMPLES_PER_SYMBOL * decoded_syms;
|
|
||||||
int32_t new_sync = syncwordSweep(expected_sync);
|
|
||||||
phase += new_sync;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame_index == M17_SYNCWORD_SYMBOLS)
|
if (frame_index == M17_SYNCWORD_SYMBOLS)
|
||||||
{
|
{
|
||||||
// If syncword is not valid, lock is lost, accept 2 bit errors
|
// If syncword is not valid, lock is lost, accept 2 bit errors
|
||||||
|
|
@ -444,11 +456,33 @@ bool M17Demodulator::update()
|
||||||
std::swap(activeFrame, idleFrame);
|
std::swap(activeFrame, idleFrame);
|
||||||
frame_index = 0;
|
frame_index = 0;
|
||||||
newFrame = true;
|
newFrame = true;
|
||||||
|
phase = 0;
|
||||||
}
|
}
|
||||||
// Correct syncword found
|
// Correct syncword found
|
||||||
else
|
else
|
||||||
locked = true;
|
locked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Locate syncword to correct clock skew between Tx and Rx
|
||||||
|
if (frame_index == M17_SYNCWORD_SYMBOLS + SYNC_SWEEP_OFFSET)
|
||||||
|
{
|
||||||
|
// Find index (possibly negative) of the syncword
|
||||||
|
int32_t expected_sync =
|
||||||
|
offset + phase +
|
||||||
|
M17_SAMPLES_PER_SYMBOL * decoded_syms -
|
||||||
|
M17_SYNCWORD_SAMPLES -
|
||||||
|
SYNC_SWEEP_OFFSET * M17_SAMPLES_PER_SYMBOL;
|
||||||
|
int32_t sync_skew = syncwordSweep(expected_sync);
|
||||||
|
phase += sync_skew;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the frame buffer is full switch active and idle frame
|
||||||
|
if (frame_index == M17_FRAME_SYMBOLS)
|
||||||
|
{
|
||||||
|
std::swap(activeFrame, idleFrame);
|
||||||
|
frame_index = 0;
|
||||||
|
newFrame = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -458,13 +492,10 @@ bool M17Demodulator::update()
|
||||||
// Compute phase of next buffer
|
// Compute phase of next buffer
|
||||||
phase = (static_cast<int32_t> (phase) + offset + baseband.len) % M17_SAMPLES_PER_SYMBOL;
|
phase = (static_cast<int32_t> (phase) + offset + baseband.len) % M17_SAMPLES_PER_SYMBOL;
|
||||||
}
|
}
|
||||||
else
|
// Copy last N samples to bridge buffer
|
||||||
{
|
memcpy(basebandBridge,
|
||||||
// Copy last N samples to bridge buffer
|
baseband.data + (baseband.len - M17_BRIDGE_SIZE),
|
||||||
memcpy(basebandBridge,
|
sizeof(int16_t) * M17_BRIDGE_SIZE);
|
||||||
baseband.data + (baseband.len - M17_BRIDGE_SIZE),
|
|
||||||
sizeof(int16_t) * M17_BRIDGE_SIZE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newFrame;
|
return newFrame;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue