mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-11-12 21:42:51 +01:00
more work on DAB decoding
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
This commit is contained in:
parent
064f25ee73
commit
f8078ac3f0
@ -5,139 +5,139 @@
|
||||
#include "dab_phase_sym.h"
|
||||
|
||||
namespace dab {
|
||||
class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||
public:
|
||||
CyclicSync() {}
|
||||
// class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||
// using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||
// public:
|
||||
// CyclicSync() {}
|
||||
|
||||
// TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
|
||||
CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
|
||||
// // TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
|
||||
// CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
|
||||
|
||||
void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
|
||||
// Computer the number of samples for the symbol and its cyclic prefix
|
||||
symbolSamps = round(samplerate * symbolLength);
|
||||
prefixSamps = round(samplerate * cyclicPrefixLength);
|
||||
// void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
|
||||
// // Computer the number of samples for the symbol and its cyclic prefix
|
||||
// symbolSamps = round(samplerate * symbolLength);
|
||||
// prefixSamps = round(samplerate * cyclicPrefixLength);
|
||||
|
||||
// Allocate and clear the delay buffer
|
||||
delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
|
||||
dsp::buffer::clear(delayBuf, symbolSamps);
|
||||
// // Allocate and clear the delay buffer
|
||||
// delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
|
||||
// dsp::buffer::clear(delayBuf, symbolSamps);
|
||||
|
||||
// Allocate and clear the history buffer
|
||||
histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
|
||||
dsp::buffer::clear(histBuf, prefixSamps);
|
||||
// // Allocate and clear the history buffer
|
||||
// histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
|
||||
// dsp::buffer::clear(histBuf, prefixSamps);
|
||||
|
||||
// Compute the delay input addresses
|
||||
delayBufInput = &delayBuf[symbolSamps];
|
||||
// // Compute the delay input addresses
|
||||
// delayBufInput = &delayBuf[symbolSamps];
|
||||
|
||||
// Compute the correlation AGC configuration
|
||||
this->agcRate = agcRate;
|
||||
agcRateInv = 1.0f - agcRate;
|
||||
// // Compute the correlation AGC configuration
|
||||
// this->agcRate = agcRate;
|
||||
// agcRateInv = 1.0f - agcRate;
|
||||
|
||||
base_type::init(in);
|
||||
}
|
||||
// base_type::init(in);
|
||||
// }
|
||||
|
||||
void reset() {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
base_type::tempStop();
|
||||
// void reset() {
|
||||
// assert(base_type::_block_init);
|
||||
// std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
// base_type::tempStop();
|
||||
|
||||
base_type::tempStart();
|
||||
}
|
||||
// base_type::tempStart();
|
||||
// }
|
||||
|
||||
int run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
// int run() {
|
||||
// int count = base_type::_in->read();
|
||||
// if (count < 0) { return -1; }
|
||||
|
||||
// Copy the data into the normal delay buffer
|
||||
memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
|
||||
// // Copy the data into the normal delay buffer
|
||||
// memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
|
||||
|
||||
// Flush the input stream
|
||||
base_type::_in->flush();
|
||||
// // Flush the input stream
|
||||
// base_type::_in->flush();
|
||||
|
||||
// Do cross-correlation
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Get the current history slot
|
||||
dsp::complex_t* slot = &histBuf[histId++];
|
||||
// // Do cross-correlation
|
||||
// for (int i = 0; i < count; i++) {
|
||||
// // Get the current history slot
|
||||
// dsp::complex_t* slot = &histBuf[histId++];
|
||||
|
||||
// Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
|
||||
histId %= prefixSamps;
|
||||
// // Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
|
||||
// histId %= prefixSamps;
|
||||
|
||||
// Kick out last value from the correlation
|
||||
corr -= *slot;
|
||||
// // Kick out last value from the correlation
|
||||
// corr -= *slot;
|
||||
|
||||
// Save input value and compute the new prodct
|
||||
dsp::complex_t val = delayBuf[i];
|
||||
dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
|
||||
// // Save input value and compute the new prodct
|
||||
// dsp::complex_t val = delayBuf[i];
|
||||
// dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
|
||||
|
||||
// Add the new value to the correlation
|
||||
*slot = prod;
|
||||
// // Add the new value to the correlation
|
||||
// *slot = prod;
|
||||
|
||||
// Add the new value to the history buffer
|
||||
corr += prod;
|
||||
// // Add the new value to the history buffer
|
||||
// corr += prod;
|
||||
|
||||
// Compute sample amplitude
|
||||
float rcorr = corr.amplitude();
|
||||
// // Compute sample amplitude
|
||||
// float rcorr = corr.amplitude();
|
||||
|
||||
// If a high enough peak is reached, reset the symbol counter
|
||||
if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
|
||||
peakCorr = rcorr;
|
||||
peakLCorr = lastCorr;
|
||||
samplesSincePeak = 0;
|
||||
}
|
||||
// // If a high enough peak is reached, reset the symbol counter
|
||||
// if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
|
||||
// peakCorr = rcorr;
|
||||
// peakLCorr = lastCorr;
|
||||
// samplesSincePeak = 0;
|
||||
// }
|
||||
|
||||
// If this is the sample right after the peak, save it
|
||||
if (samplesSincePeak == 1) {
|
||||
peakRCorr = rcorr;
|
||||
}
|
||||
// // If this is the sample right after the peak, save it
|
||||
// if (samplesSincePeak == 1) {
|
||||
// peakRCorr = rcorr;
|
||||
// }
|
||||
|
||||
// Write the sample to the output
|
||||
out.writeBuf[samplesSincePeak++] = val;
|
||||
// // Write the sample to the output
|
||||
// out.writeBuf[samplesSincePeak++] = val;
|
||||
|
||||
// If the end of the symbol is reached, send it off
|
||||
if (samplesSincePeak >= symbolSamps) {
|
||||
if (!out.swap(symbolSamps)) {
|
||||
return -1;
|
||||
}
|
||||
samplesSincePeak = 0;
|
||||
peakCorr = 0;
|
||||
}
|
||||
// // If the end of the symbol is reached, send it off
|
||||
// if (samplesSincePeak >= symbolSamps) {
|
||||
// if (!out.swap(symbolSamps)) {
|
||||
// return -1;
|
||||
// }
|
||||
// samplesSincePeak = 0;
|
||||
// peakCorr = 0;
|
||||
// }
|
||||
|
||||
// Update the average correlation
|
||||
lastCorr = rcorr;
|
||||
// // Update the average correlation
|
||||
// lastCorr = rcorr;
|
||||
|
||||
// Update the average correlation value
|
||||
avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
|
||||
}
|
||||
// // Update the average correlation value
|
||||
// avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
|
||||
// }
|
||||
|
||||
// Move unused data
|
||||
memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
|
||||
// // Move unused data
|
||||
// memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
|
||||
|
||||
return count;
|
||||
}
|
||||
// return count;
|
||||
// }
|
||||
|
||||
protected:
|
||||
int symbolSamps;
|
||||
int prefixSamps;
|
||||
// protected:
|
||||
// int symbolSamps;
|
||||
// int prefixSamps;
|
||||
|
||||
int histId = 0;
|
||||
dsp::complex_t* histBuf;
|
||||
// int histId = 0;
|
||||
// dsp::complex_t* histBuf;
|
||||
|
||||
dsp::complex_t* delayBuf;
|
||||
dsp::complex_t* delayBufInput;
|
||||
// dsp::complex_t* delayBuf;
|
||||
// dsp::complex_t* delayBufInput;
|
||||
|
||||
dsp::complex_t corr = { 0.0f, 0.0f };
|
||||
// dsp::complex_t corr = { 0.0f, 0.0f };
|
||||
|
||||
int samplesSincePeak = 0;
|
||||
float lastCorr = 0.0f;
|
||||
float peakCorr = 0.0f;
|
||||
float peakLCorr = 0.0f;
|
||||
float peakRCorr = 0.0f;
|
||||
// int samplesSincePeak = 0;
|
||||
// float lastCorr = 0.0f;
|
||||
// float peakCorr = 0.0f;
|
||||
// float peakLCorr = 0.0f;
|
||||
// float peakRCorr = 0.0f;
|
||||
|
||||
// Note only required for DAB
|
||||
float avgCorr = 0.0f;
|
||||
float agcRate;
|
||||
float agcRateInv;
|
||||
};
|
||||
// // Note only required for DAB
|
||||
// float avgCorr = 0.0f;
|
||||
// float agcRate;
|
||||
// float agcRateInv;
|
||||
// };
|
||||
|
||||
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <chrono>
|
||||
#include "dab_dsp.h"
|
||||
#include <gui/widgets/constellation_diagram.h>
|
||||
#include "ofdm.h"
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
@ -35,7 +36,7 @@ public:
|
||||
M17DecoderModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary);
|
||||
file = std::ofstream("sync5.f32", std::ios::out | std::ios::binary);
|
||||
|
||||
// Load config
|
||||
config.acquire();
|
||||
@ -47,7 +48,7 @@ public:
|
||||
vfo->setSnapInterval(250);
|
||||
|
||||
// Initialize DSP here
|
||||
csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE);
|
||||
csync.init(vfo->output, 2048, 504, 1e-3, INPUT_SAMPLE_RATE, 1e-6, 0.01, 0.005);
|
||||
ffsync.init(&csync.out);
|
||||
ns.init(&ffsync.out, handler, this);
|
||||
|
||||
@ -131,8 +132,9 @@ private:
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
|
||||
dab::CyclicSync csync;
|
||||
//dab::CyclicSync csync;
|
||||
dab::FrameFreqSync ffsync;
|
||||
dsp::ofdm::CyclicTimeSync csync;
|
||||
dsp::sink::Handler<dsp::complex_t> ns;
|
||||
|
||||
ImGui::ConstellationDiagram constDiagram;
|
||||
|
@ -11,67 +11,93 @@ namespace dsp::ofdm {
|
||||
public:
|
||||
CyclicTimeSync() {}
|
||||
|
||||
CyclicTimeSync(stream<complex_t>* in, int fftSize, double usefulSymbolTime, double cyclicPrefixRatio, double samplerate,
|
||||
CyclicTimeSync(stream<complex_t>* in, int fftSize, int cpSize, double usefulSymbolTime, double samplerate,
|
||||
double omegaGain, double muGain, double omegaRelLimit, int interpPhaseCount = 128, int interpTapCount = 8) {
|
||||
init(in, fftSize, usefulSymbolTime, cyclicPrefixRatio, samplerate, omegaGain, muGain, omegaRelLimit, interpPhaseCount, interpTapCount);
|
||||
init(in, fftSize, cpSize, usefulSymbolTime, samplerate, omegaGain, muGain, omegaRelLimit, interpPhaseCount, interpTapCount);
|
||||
}
|
||||
|
||||
~CyclicTimeSync() {
|
||||
if (!base_type::_block_init) { return; }
|
||||
base_type::stop();
|
||||
dsp::multirate::freePolyphaseBank(interpBank);
|
||||
buffer::free(corrSampCache);
|
||||
buffer::free(corrProdCache);
|
||||
buffer::free(buffer);
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, int fftSize, double usefulSymbolTime, double cyclicPrefixRatio, double samplerate,
|
||||
void init(stream<complex_t>* in, int fftSize, int cpSize, double usefulSymbolTime, double samplerate,
|
||||
double omegaGain, double muGain, double omegaRelLimit, int interpPhaseCount = 128, int interpTapCount = 8) {
|
||||
omega = 0; // TODO
|
||||
_omegaGain = omegaGain;
|
||||
_muGain = muGain;
|
||||
_omegaRelLimit = omegaRelLimit;
|
||||
_interpPhaseCount = interpPhaseCount;
|
||||
_interpTapCount = interpTapCount;
|
||||
// Save parameters
|
||||
this->fftSize = fftSize;
|
||||
this->cpSize = cpSize;
|
||||
period = fftSize + cpSize;
|
||||
|
||||
// Compute the interpolator settings
|
||||
omega = (usefulSymbolTime * samplerate) / (double)fftSize;
|
||||
this->omegaGain = omegaGain;
|
||||
this->muGain = muGain;
|
||||
this->omegaRelLimit = omegaRelLimit;
|
||||
this->interpPhaseCount = interpPhaseCount;
|
||||
this->interpTapCount = interpTapCount;
|
||||
|
||||
pcl.init(_muGain, _omegaGain, 0.0, 0.0, 1.0, omega, omega * (1.0 - omegaRelLimit), omega * (1.0 + omegaRelLimit));
|
||||
// Compute the correlator AGC settings
|
||||
// TODO: Compute it using he FFT and CP sizes
|
||||
this->corrAgcRate = 1e-4;
|
||||
corrAgcInvRate = 1.0f - corrAgcRate;
|
||||
|
||||
// Initialize the control loop
|
||||
pcl.init(muGain, omegaGain, 0.0, 0.0, 1.0, omega, omega * (1.0 - omegaRelLimit), omega * (1.0 + omegaRelLimit));
|
||||
|
||||
// Generate the interpolation taps
|
||||
generateInterpTaps();
|
||||
buffer = buffer::alloc<complex_t>(STREAM_BUFFER_SIZE + _interpTapCount);
|
||||
bufStart = &buffer[_interpTapCount - 1];
|
||||
|
||||
// Allocate the buffers
|
||||
corrSampCache = buffer::alloc<complex_t>(fftSize);
|
||||
corrProdCache = buffer::alloc<complex_t>(cpSize);
|
||||
buffer = buffer::alloc<complex_t>(STREAM_BUFFER_SIZE + interpTapCount);
|
||||
bufStart = &buffer[interpTapCount - 1];
|
||||
|
||||
// Clear the buffers
|
||||
buffer::clear(corrSampCache, fftSize);
|
||||
buffer::clear(corrProdCache, cpSize);
|
||||
buffer::clear(buffer, interpTapCount - 1);
|
||||
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
|
||||
void setOmegaGain(double omegaGain) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
_omegaGain = omegaGain;
|
||||
pcl.setCoefficients(_muGain, _omegaGain);
|
||||
this->omegaGain = omegaGain;
|
||||
pcl.setCoefficients(muGain, omegaGain);
|
||||
}
|
||||
|
||||
void setMuGain(double muGain) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
_muGain = muGain;
|
||||
pcl.setCoefficients(_muGain, _omegaGain);
|
||||
this->muGain = muGain;
|
||||
pcl.setCoefficients(muGain, omegaGain);
|
||||
}
|
||||
|
||||
void setOmegaRelLimit(double omegaRelLimit) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
_omegaRelLimit = omegaRelLimit;
|
||||
pcl.setFreqLimits(omega * (1.0 - _omegaRelLimit), omega * (1.0 + _omegaRelLimit));
|
||||
this->omegaRelLimit = omegaRelLimit;
|
||||
pcl.setFreqLimits(omega * (1.0 - omegaRelLimit), omega * (1.0 + omegaRelLimit));
|
||||
}
|
||||
|
||||
void setInterpParams(int interpPhaseCount, int interpTapCount) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
base_type::tempStop();
|
||||
_interpPhaseCount = interpPhaseCount;
|
||||
_interpTapCount = interpTapCount;
|
||||
this->interpPhaseCount = interpPhaseCount;
|
||||
this->interpTapCount = interpTapCount;
|
||||
dsp::multirate::freePolyphaseBank(interpBank);
|
||||
buffer::free(buffer);
|
||||
generateInterpTaps();
|
||||
buffer = buffer::alloc<complex_t>(STREAM_BUFFER_SIZE + _interpTapCount);
|
||||
bufStart = &buffer[_interpTapCount - 1];
|
||||
buffer = buffer::alloc<complex_t>(STREAM_BUFFER_SIZE + interpTapCount);
|
||||
bufStart = &buffer[interpTapCount - 1];
|
||||
base_type::tempStart();
|
||||
}
|
||||
|
||||
@ -82,59 +108,139 @@ namespace dsp::ofdm {
|
||||
offset = 0;
|
||||
pcl.phase = 0.0f;
|
||||
pcl.freq = omega;
|
||||
// TODO: The rest
|
||||
base_type::tempStart();
|
||||
}
|
||||
|
||||
inline int process(int count, const complex_t* in, complex_t* out) {
|
||||
int run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
// Copy data to work buffer
|
||||
memcpy(bufStart, in, count * sizeof(complex_t));
|
||||
memcpy(bufStart, base_type::_in->readBuf, count * sizeof(complex_t));
|
||||
|
||||
// Process all samples
|
||||
int outCount = 0;
|
||||
while (offset < count) {
|
||||
// Get the cache slots
|
||||
complex_t* sampSlot = &corrSampCache[corrSampCacheId++];
|
||||
complex_t* prodSlot = &corrProdCache[corrProdCacheId++];
|
||||
corrSampCacheId %= fftSize;
|
||||
corrProdCacheId %= cpSize;
|
||||
|
||||
// Compute the interpolated sample
|
||||
complex_t sample;
|
||||
int phase = std::clamp<int>(floorf(pcl.phase * (float)_interpPhaseCount), 0, _interpPhaseCount - 1);
|
||||
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&sample, (lv_32fc_t*)&buffer[offset], interpBank.phases[phase], _interpTapCount);
|
||||
int phase = std::clamp<int>(floorf(pcl.phase * (float)interpPhaseCount), 0, interpPhaseCount - 1);
|
||||
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&sample, (lv_32fc_t*)&buffer[offset], interpBank.phases[phase], interpTapCount);
|
||||
|
||||
// Update autocorrelation
|
||||
// Write the sample to the output
|
||||
if (outCount >= cpSize) {
|
||||
out.writeBuf[outCount - cpSize] = sample;
|
||||
}
|
||||
|
||||
// Send out a symbol when it's fully received
|
||||
if ((++outCount) >= fftSize+cpSize) {
|
||||
if (!out.swap(outCount)) { break; }
|
||||
outCount = 0;
|
||||
}
|
||||
|
||||
// Run autocorrelation
|
||||
complex_t prod = sample.conj()*(*sampSlot);
|
||||
corr += prod;
|
||||
corr -= *prodSlot;
|
||||
|
||||
// Write back the new sample and product value to the cache
|
||||
*sampSlot = sample;
|
||||
*prodSlot = prod;
|
||||
|
||||
// Compute the correlation level
|
||||
float corrLvl = corr.amplitude();
|
||||
|
||||
// Detect peak in autocorrelation
|
||||
if (0/*TODO*/) {
|
||||
// Detect peak in autocorrelation (TODO: level check maybe not needed now that corrPeak is reset to corrLvl)
|
||||
if (corrLvl > corrAvg && corrLvl > corrPeak) {
|
||||
// Save the current correlation as the peak
|
||||
corrPeak = corrLvl;
|
||||
|
||||
// Save the value of the previous correlation as the left side of the peak
|
||||
corrPeakL = corrLastLvl;
|
||||
corrPeakL = corrLast;
|
||||
|
||||
// Save the symbol period
|
||||
measuredSymbolPeriod = sampCount;
|
||||
|
||||
// Reset the sample count
|
||||
sampCount = 0;
|
||||
|
||||
// TODO: Maybe save the error to apply it at the end of the frame? (will cause issues with the longer null symbol in DAB)
|
||||
// Reset the peak distance counter
|
||||
sincePeak = 0;
|
||||
}
|
||||
|
||||
// Write the sample to the frame if within it
|
||||
if (sampCount < symbolSize) {
|
||||
symbol[sampCount++] = sample;
|
||||
// The first sample after a peak is the right-side sample
|
||||
if (sincePeak == 1) {
|
||||
corrPeakR = corrLvl;
|
||||
}
|
||||
|
||||
// When the end of the symbol is reached
|
||||
if (sampCount == symbolSize) {
|
||||
// Send out the symbol
|
||||
// TODO
|
||||
else if (sincePeak == cpSize) {
|
||||
// Start the useful symbol counter
|
||||
sinceCp = 0;
|
||||
|
||||
// Compute the fractional error (TODO: Probably very inaccurate with noise, use real slopes instead)
|
||||
if (corrPeakL > corrPeakR) {
|
||||
float maxSlope = corrPeakR - corrPeak;
|
||||
float slope = corrPeak - corrPeakL;
|
||||
fracErr = std::clamp<float>(0.5f * (1.0f + slope / maxSlope), -0.5f, 0.5f);
|
||||
}
|
||||
else {
|
||||
float maxSlope = corrPeak - corrPeakL;
|
||||
float slope = corrPeakR - corrPeak;
|
||||
fracErr = std::clamp<float>(-0.5f * (1.0f + slope / maxSlope), -0.5f, 0.5f);
|
||||
}
|
||||
}
|
||||
else if (sincePeak == fftSize) {
|
||||
// Reset the peak detector
|
||||
corrPeak = corrAvg;
|
||||
}
|
||||
// NOTE: THIS IS ONLY NEEDED FOR DAB
|
||||
// Detect a wider-than-normal distance to adapt the output counter
|
||||
else if (sincePeak == 2656) {
|
||||
// Reset the output counter
|
||||
outCount = 50;
|
||||
}
|
||||
|
||||
// Last sample of useful symbol
|
||||
if (sinceCp == fftSize) {
|
||||
// If the fractional error is valid, run closed-loop
|
||||
float err = 0.0f;
|
||||
if (!std::isnan(fracErr)) {
|
||||
// Compute the measured period using the distance to the last symbol
|
||||
float measuredPeriod = (float)sinceLastSym - fracErr;
|
||||
// NOTE: THIS IS ONLY NEEDED FOR DAB
|
||||
if (measuredPeriod > 3828.0f) {
|
||||
// Null symbol
|
||||
err = measuredPeriod - (2552.0f+2656.0f);
|
||||
}
|
||||
else {
|
||||
// Regular symbol
|
||||
err = measuredPeriod - period;
|
||||
}
|
||||
|
||||
// TODO: limit how much the sample count can grow otherwise otherflows will trigger a false frame detection
|
||||
err = std::clamp<float>(err, -10.0f, 10.0f);
|
||||
|
||||
// Run the control loop
|
||||
//pcl.advance(error); // TODO
|
||||
pcl.advancePhase();
|
||||
// Run the control loop in closed-loop mode
|
||||
pcl.advance(err);
|
||||
}
|
||||
else {
|
||||
// Otherwise, run open-loop
|
||||
pcl.advancePhase();
|
||||
}
|
||||
|
||||
// printf("%d\n", outCount);
|
||||
|
||||
// Nudge the symbol window if it's too out of sync
|
||||
if (outCount > 100) {
|
||||
// TODO: MOVE THE LAST SAMPLES OR THE SYMBOL WILL BE CORRUPTED!
|
||||
outCount = 50;
|
||||
flog::debug("NUDGE!");
|
||||
}
|
||||
|
||||
// Reset the period counter
|
||||
sinceLastSym = 0;
|
||||
}
|
||||
else {
|
||||
// Run the control loop in open-loop mode
|
||||
pcl.advancePhase();
|
||||
}
|
||||
|
||||
// Update the offset and phase
|
||||
float delta = floorf(pcl.phase);
|
||||
@ -142,44 +248,45 @@ namespace dsp::ofdm {
|
||||
pcl.phase -= delta;
|
||||
|
||||
// Update the last correlation level
|
||||
corrLastLvl = corrLvl;
|
||||
corrLast = corrLvl;
|
||||
|
||||
// Update correlation AGC
|
||||
corrAvg = corrAvg*corrAgcInvRate + corrLvl*corrAgcRate;
|
||||
|
||||
// Increment the distance counters (TODO: Check if they happen at the right point, eg. after being reset to zero)
|
||||
sincePeak++;
|
||||
sinceLastSym++;
|
||||
sinceCp++;
|
||||
}
|
||||
|
||||
// Prepare offset for next buffer of samples
|
||||
offset -= count;
|
||||
|
||||
// Update delay buffer
|
||||
memmove(buffer, &buffer[count], (_interpTapCount - 1) * sizeof(complex_t));
|
||||
|
||||
return outCount;
|
||||
}
|
||||
|
||||
int run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
int outCount = process(count, base_type::_in->readBuf, base_type::out.writeBuf);
|
||||
memmove(buffer, &buffer[count], (interpTapCount - 1) * sizeof(complex_t));
|
||||
|
||||
// Swap if some data was generated
|
||||
base_type::_in->flush();
|
||||
if (outCount) {
|
||||
if (!base_type::out.swap(outCount)) { return -1; }
|
||||
}
|
||||
return outCount;
|
||||
return count;
|
||||
}
|
||||
|
||||
protected:
|
||||
void generateInterpTaps() {
|
||||
double bw = 0.5 / (double)_interpPhaseCount;
|
||||
dsp::tap<float> lp = dsp::taps::windowedSinc<float>(_interpPhaseCount * _interpTapCount, dsp::math::hzToRads(bw, 1.0), dsp::window::nuttall, _interpPhaseCount);
|
||||
interpBank = dsp::multirate::buildPolyphaseBank<float>(_interpPhaseCount, lp);
|
||||
double bw = 0.5 / (double)interpPhaseCount;
|
||||
dsp::tap<float> lp = dsp::taps::windowedSinc<float>(interpPhaseCount * interpTapCount, dsp::math::hzToRads(bw, 1.0), dsp::window::nuttall, interpPhaseCount);
|
||||
interpBank = dsp::multirate::buildPolyphaseBank<float>(interpPhaseCount, lp);
|
||||
taps::free(lp);
|
||||
}
|
||||
|
||||
// OFDM Configuration
|
||||
int fftSize;
|
||||
int cpSize;
|
||||
float period;
|
||||
|
||||
// Interpolator
|
||||
dsp::multirate::PolyphaseBank<float> interpBank;
|
||||
int _interpPhaseCount;
|
||||
int _interpTapCount;
|
||||
int interpPhaseCount;
|
||||
int interpTapCount;
|
||||
int offset = 0;
|
||||
complex_t* buffer = NULL;
|
||||
complex_t* bufStart;
|
||||
@ -187,24 +294,31 @@ namespace dsp::ofdm {
|
||||
// Control loop
|
||||
loop::PhaseControlLoop<float, false> pcl;
|
||||
double omega;
|
||||
double _omegaGain;
|
||||
double _muGain;
|
||||
double _omegaRelLimit;
|
||||
double omegaGain;
|
||||
double muGain;
|
||||
double omegaRelLimit;
|
||||
float fracErr = 0.0f;
|
||||
|
||||
// Autocorrelator
|
||||
complex_t corr;
|
||||
complex_t* corrProducts = NULL;
|
||||
complex_t corr = {0.0f, 0.0f};
|
||||
complex_t* corrSampCache = NULL;
|
||||
complex_t* corrProdCache = NULL;
|
||||
int corrSampCacheId = 0;
|
||||
int corrProdCacheId = 0;
|
||||
float corrAgcRate;
|
||||
float corrAgcInvRate;
|
||||
float corrLastLvl = 0;
|
||||
float corrAvg = 0;
|
||||
float corrLast = 0;
|
||||
float corrPeakR = 0;
|
||||
float corrPeak = 0;
|
||||
float corrPeakL = 0;
|
||||
|
||||
// Symbol
|
||||
complex_t* symbol; // TODO: Will use output stream buffer instead
|
||||
int symbolSize;
|
||||
int sampCount = 0;
|
||||
int measuredSymbolPeriod = 0;
|
||||
// Peak detection
|
||||
int sincePeak = 0;
|
||||
int sinceLastSym = 0;
|
||||
int sinceCp = 0;
|
||||
|
||||
// Other shit to categorize
|
||||
int outCount = 0;
|
||||
};
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
0123456789
|
||||
--- ---
|
||||
|
||||
0*4
|
||||
1*5
|
||||
2*6
|
||||
|
||||
1*5
|
||||
2*6 = L + 3*7 - 0*4
|
||||
3*7
|
||||
|
||||
2*6
|
||||
3*7 = L + 4*8 - 1*5
|
||||
4*8
|
||||
|
||||
3*7
|
||||
4*8 = L + 5*9 - 2*6
|
||||
5*9
|
||||
|
||||
|
||||
|
||||
0*5
|
||||
1*6
|
||||
2*7
|
||||
|
||||
1*6
|
||||
2*7
|
||||
3*8
|
||||
|
||||
2*7
|
||||
3*8
|
||||
4*9
|
||||
|
||||
=> Use same technique to cache the interpolation results
|
31
decoder_modules/dab_decoder/src/optmized.txt
Normal file
31
decoder_modules/dab_decoder/src/optmized.txt
Normal file
@ -0,0 +1,31 @@
|
||||
cyclicLen = 4
|
||||
usefulLen = 12
|
||||
|
||||
A = 0*12 + 1*13 + 2*14 + 3*15
|
||||
B = 1*13 + 2*14 + 3*15 + 4*16 = A - 0*12 + 4*16
|
||||
C = 2*14 + 3*15 + 4*16 + 5*17 = B - 1*13 + 5*17
|
||||
D = 3*15 + 4*16 + 5*17 + 6*18 = C - 2*14 + 6*18
|
||||
E = 4*16 + 5*17 + 6*18 + 7*19 = D - 3*15 + 7*19
|
||||
F = 5*17 + 6*18 + 7*19 + 8*20 = E - 4*16 + 8*20
|
||||
G = 6*18 + 7*19 + 8*20 + 9*21 = F - 5*17 + 9*21
|
||||
H = 7*19 + 8*20 + 9*21 + 10*22 = G - 6*18 + 10*22
|
||||
I = 8*20 + 9*21 + 10*22 + 11*23 = H - 7*19 + 11*23
|
||||
J = 9*21 + 10*22 + 11*23 + 12*24 = I - 8*20 + 12*24
|
||||
K = 10*22 + 11*23 + 12*24 + 13*25 = J - 9*21 + 13*25
|
||||
L = 11*23 + 12*24 + 13*25 + 14*26 = K - 10*22 + 14*26
|
||||
M = 12*24 + 13*25 + 14*26 + 15*27 = L - 11*23 + 15*27
|
||||
N = 13*25 + 14*26 + 15*27 + 16*28 = M - 12*24 + 16*28
|
||||
O = 14*26 + 15*27 + 16*28 + 17*29 = N - 13*25 + 17*29
|
||||
P = 15*27 + 16*28 + 17*29 + 18*30 = O - 14*26 + 18*30
|
||||
Q = 16*28 + 17*29 + 18*30 + 19*31 = P - 15*27 + 19*31
|
||||
R = 17*29 + 18*30 + 19*31 + 20*32 = Q - 16*28 + 20*32
|
||||
S = 18*30 + 19*31 + 20*32 + 21*33 = R - 17*29 + 21*33
|
||||
T = 19*31 + 20*32 + 21*33 + 22*34 = S - 18*30 + 22*34
|
||||
U = 20*32 + 21*33 + 22*34 + 23*35 = T - 19*31 + 23*35
|
||||
|
||||
Conclusion:
|
||||
sampCacheLen = usefulLen
|
||||
prodCacheLen = cyclicLen
|
||||
|
||||
Peak correlation occurs when the current interpolated value is the last FFT sample
|
||||
|
397
decoder_modules/dab_decoder/src/test.txt
Normal file
397
decoder_modules/dab_decoder/src/test.txt
Normal file
@ -0,0 +1,397 @@
|
||||
5209
|
||||
2553
|
||||
2551
|
||||
2551
|
||||
2552
|
||||
2551
|
||||
2553
|
||||
2555
|
||||
2549
|
||||
2552
|
||||
2552
|
||||
2554
|
||||
2550
|
||||
2553
|
||||
2553
|
||||
2550
|
||||
2553
|
||||
2552
|
||||
2550
|
||||
2552
|
||||
2548
|
||||
2558
|
||||
2551
|
||||
2546
|
||||
2559
|
||||
2551
|
||||
2552
|
||||
2552
|
||||
2551
|
||||
2554
|
||||
2555
|
||||
2549
|
||||
2550
|
||||
2549
|
||||
2557
|
||||
2551
|
||||
2554
|
||||
2550
|
||||
2552
|
||||
2546
|
||||
2559
|
||||
2551
|
||||
2554
|
||||
2550
|
||||
2550
|
||||
2557
|
||||
2550
|
||||
2556
|
||||
2543
|
||||
2556
|
||||
2551
|
||||
2554
|
||||
2551
|
||||
2553
|
||||
2554
|
||||
2550
|
||||
2551
|
||||
2552
|
||||
2552
|
||||
2553
|
||||
2550
|
||||
2553
|
||||
2546
|
||||
2558
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2550
|
||||
2555
|
||||
2551
|
||||
2551
|
||||
2554
|
||||
2553
|
||||
2553
|
||||
2549
|
||||
2552
|
||||
5208
|
||||
2554
|
||||
2550
|
||||
2552
|
||||
2551
|
||||
2553
|
||||
2551
|
||||
2553
|
||||
2551
|
||||
2553
|
||||
2550
|
||||
2554
|
||||
2553
|
||||
2551
|
||||
2553
|
||||
2552
|
||||
2546
|
||||
2557
|
||||
2551
|
||||
2553
|
||||
2552
|
||||
2551
|
||||
2553
|
||||
2551
|
||||
2548
|
||||
2557
|
||||
2553
|
||||
2551
|
||||
2562
|
||||
2539
|
||||
2559
|
||||
2548
|
||||
2551
|
||||
2551
|
||||
2550
|
||||
2556
|
||||
2551
|
||||
2554
|
||||
2550
|
||||
2552
|
||||
2552
|
||||
2548
|
||||
2556
|
||||
2550
|
||||
2554
|
||||
2553
|
||||
2553
|
||||
2550
|
||||
2552
|
||||
2552
|
||||
2550
|
||||
2555
|
||||
2558
|
||||
2545
|
||||
2552
|
||||
2553
|
||||
2548
|
||||
2555
|
||||
2552
|
||||
2552
|
||||
2557
|
||||
2550
|
||||
2548
|
||||
2553
|
||||
2554
|
||||
2550
|
||||
2552
|
||||
2547
|
||||
2558
|
||||
2551
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2555
|
||||
2549
|
||||
2552
|
||||
5208
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2550
|
||||
2555
|
||||
2550
|
||||
2553
|
||||
2553
|
||||
2551
|
||||
2552
|
||||
2547
|
||||
2557
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2546
|
||||
2558
|
||||
2551
|
||||
2552
|
||||
2553
|
||||
2551
|
||||
2553
|
||||
2553
|
||||
2552
|
||||
2551
|
||||
2552
|
||||
2552
|
||||
2551
|
||||
2553
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2556
|
||||
2548
|
||||
2551
|
||||
2555
|
||||
2550
|
||||
2552
|
||||
2555
|
||||
2549
|
||||
2552
|
||||
2552
|
||||
2550
|
||||
2553
|
||||
2551
|
||||
2554
|
||||
2552
|
||||
2551
|
||||
2553
|
||||
2553
|
||||
2551
|
||||
2548
|
||||
2550
|
||||
2555
|
||||
2554
|
||||
2553
|
||||
2556
|
||||
2547
|
||||
2552
|
||||
2554
|
||||
2552
|
||||
2550
|
||||
2552
|
||||
2552
|
||||
2553
|
||||
2552
|
||||
2551
|
||||
2552
|
||||
5209
|
||||
2553
|
||||
2551
|
||||
2551
|
||||
2547
|
||||
2558
|
||||
2550
|
||||
2553
|
||||
2552
|
||||
2553
|
||||
2552
|
||||
2549
|
||||
2551
|
||||
2557
|
||||
2549
|
||||
2554
|
||||
2551
|
||||
2554
|
||||
2550
|
||||
2553
|
||||
2553
|
||||
2551
|
||||
2551
|
||||
2548
|
||||
2559
|
||||
2549
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2551
|
||||
2551
|
||||
2550
|
||||
2557
|
||||
2551
|
||||
2552
|
||||
2555
|
||||
2549
|
||||
2557
|
||||
2549
|
||||
2550
|
||||
2553
|
||||
2549
|
||||
2554
|
||||
2553
|
||||
2553
|
||||
2550
|
||||
2552
|
||||
2553
|
||||
2550
|
||||
2553
|
||||
2553
|
||||
2551
|
||||
2547
|
||||
2557
|
||||
2552
|
||||
2552
|
||||
2551
|
||||
2553
|
||||
2549
|
||||
2555
|
||||
2556
|
||||
2550
|
||||
2550
|
||||
2553
|
||||
2551
|
||||
2552
|
||||
2553
|
||||
2551
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2553
|
||||
2551
|
||||
2541
|
||||
2563
|
||||
2552
|
||||
5210
|
||||
2550
|
||||
2551
|
||||
2552
|
||||
2553
|
||||
2553
|
||||
2551
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2558
|
||||
2547
|
||||
2551
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2550
|
||||
2554
|
||||
2551
|
||||
2553
|
||||
2552
|
||||
2551
|
||||
2553
|
||||
2551
|
||||
2553
|
||||
2553
|
||||
2550
|
||||
2551
|
||||
2555
|
||||
2550
|
||||
2552
|
||||
2552
|
||||
2553
|
||||
2553
|
||||
2550
|
||||
2552
|
||||
2553
|
||||
2550
|
||||
2553
|
||||
2558
|
||||
2546
|
||||
2553
|
||||
2554
|
||||
2550
|
||||
2552
|
||||
2553
|
||||
2548
|
||||
2554
|
||||
2552
|
||||
2552
|
||||
2552
|
||||
2551
|
||||
2554
|
||||
2550
|
||||
2553
|
||||
2553
|
||||
2550
|
||||
2551
|
||||
2552
|
||||
2554
|
||||
2553
|
||||
2551
|
||||
2551
|
||||
2555
|
||||
2550
|
||||
2553
|
||||
2552
|
||||
2550
|
||||
2554
|
||||
2550
|
||||
2553
|
||||
2553
|
||||
2553
|
||||
2550
|
||||
5210
|
||||
2553
|
||||
2549
|
||||
2553
|
||||
2551
|
||||
2552
|
||||
2552
|
||||
2556
|
||||
2548
|
||||
2549
|
||||
2555
|
||||
2551
|
||||
2557
|
||||
2548
|
||||
2552
|
||||
2552
|
||||
2552
|
Loading…
Reference in New Issue
Block a user