mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-12-08 12:03:41 -05:00
LoRa demod: try to enhance detection
This commit is contained in:
parent
a5dc668376
commit
60db0ab868
@ -137,7 +137,7 @@ void LoRaDemodBaseband::applySettings(const LoRaDemodSettings& settings, bool fo
|
||||
|| (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
|
||||
{
|
||||
m_channelizer->setChannelization(
|
||||
LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex],
|
||||
LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex]*LoRaDemodSettings::oversampling,
|
||||
settings.m_inputFrequencyOffset
|
||||
);
|
||||
m_sink.applyChannelSettings(
|
||||
|
||||
@ -282,7 +282,7 @@ void LoRaDemodGUI::displaySettings()
|
||||
|
||||
void LoRaDemodGUI::setBandwidths()
|
||||
{
|
||||
int maxBandwidth = m_basebandSampleRate;
|
||||
int maxBandwidth = m_basebandSampleRate/LoRaDemodSettings::oversampling;
|
||||
int maxIndex = 0;
|
||||
|
||||
for (; (maxIndex < LoRaDemodSettings::nbBandwidths) && (LoRaDemodSettings::bandwidths[maxIndex] <= maxBandwidth); maxIndex++)
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
|
||||
const int LoRaDemodSettings::bandwidths[] = {2500, 7813, 10417, 15625, 20833, 31250, 41667, 62500, 125000, 250000, 500000};
|
||||
const int LoRaDemodSettings::nbBandwidths = 11;
|
||||
const int LoRaDemodSettings::oversampling = 2;
|
||||
|
||||
LoRaDemodSettings::LoRaDemodSettings() :
|
||||
m_inputFrequencyOffset(0),
|
||||
|
||||
@ -40,6 +40,7 @@ struct LoRaDemodSettings
|
||||
|
||||
static const int bandwidths[];
|
||||
static const int nbBandwidths;
|
||||
static const int oversampling;
|
||||
|
||||
LoRaDemodSettings();
|
||||
void resetToDefaults();
|
||||
|
||||
@ -30,7 +30,8 @@ LoRaDemodSink::LoRaDemodSink() :
|
||||
m_spectrumBuffer(nullptr),
|
||||
m_downChirps(nullptr),
|
||||
m_upChirps(nullptr),
|
||||
m_fftBuffer(nullptr)
|
||||
m_fftBuffer(nullptr),
|
||||
m_spectrumLine(nullptr)
|
||||
{
|
||||
m_bandwidth = LoRaDemodSettings::bandwidths[0];
|
||||
m_channelSampleRate = 96000;
|
||||
@ -47,7 +48,7 @@ LoRaDemodSink::LoRaDemodSink() :
|
||||
m_fft = FFTEngine::create();
|
||||
m_fftSFD = FFTEngine::create();
|
||||
|
||||
initSF(m_settings.m_spreadFactor);
|
||||
initSF(m_settings.m_spreadFactor, m_settings.m_deBits);
|
||||
}
|
||||
|
||||
LoRaDemodSink::~LoRaDemodSink()
|
||||
@ -57,9 +58,10 @@ LoRaDemodSink::~LoRaDemodSink()
|
||||
delete[] m_downChirps;
|
||||
delete[] m_upChirps;
|
||||
delete[] m_spectrumBuffer;
|
||||
delete[] m_spectrumLine;
|
||||
}
|
||||
|
||||
void LoRaDemodSink::initSF(unsigned int sf)
|
||||
void LoRaDemodSink::initSF(unsigned int sf, unsigned int deBits)
|
||||
{
|
||||
if (m_downChirps) {
|
||||
delete[] m_downChirps;
|
||||
@ -73,8 +75,12 @@ void LoRaDemodSink::initSF(unsigned int sf)
|
||||
if (m_spectrumBuffer) {
|
||||
delete[] m_spectrumBuffer;
|
||||
}
|
||||
if (m_spectrumLine) {
|
||||
delete[] m_spectrumLine;
|
||||
}
|
||||
|
||||
m_nbSymbols = 1 << sf;
|
||||
m_nbSymbolsEff = 1 << (sf - deBits);
|
||||
m_fftLength = m_nbSymbols;
|
||||
m_fft->configure(m_fftInterpolation*m_fftLength, false);
|
||||
m_fftSFD->configure(m_fftInterpolation*m_fftLength, false);
|
||||
@ -82,22 +88,28 @@ void LoRaDemodSink::initSF(unsigned int sf)
|
||||
m_sfdSkip = m_fftLength / 4;
|
||||
m_fftWindow.create(FFTWindow::Function::Kaiser, m_fftLength);
|
||||
m_fftWindow.setKaiserAlpha(M_PI);
|
||||
m_downChirps = new Complex[2*m_nbSymbols]; // Each table is 2 chirps long to allow memcpying from arbitrary offsets.
|
||||
m_downChirps = new Complex[2*m_nbSymbols]; // Each table is 2 chirps long to allow processing from arbitrary offsets.
|
||||
m_upChirps = new Complex[2*m_nbSymbols];
|
||||
m_fftBuffer = new Complex[m_fftInterpolation*m_fftLength];
|
||||
m_spectrumBuffer = new Complex[m_nbSymbols];
|
||||
m_spectrumLine = new Complex[m_nbSymbols];
|
||||
std::fill(m_spectrumLine, m_spectrumLine+m_nbSymbols, Complex(std::polar(1e-6*SDR_RX_SCALED, 0.0)));
|
||||
|
||||
float halfAngle = M_PI;
|
||||
float phase = -halfAngle;
|
||||
double accumulator = 0;
|
||||
|
||||
for (int i = 0; i < 2*m_nbSymbols; i++)
|
||||
for (int i = 0; i < m_fftLength; i++)
|
||||
{
|
||||
accumulator = fmod(accumulator + phase, 2*M_PI);
|
||||
m_downChirps[i] = Complex(std::conj(std::polar(1.0, accumulator)));
|
||||
m_upChirps[i] = Complex(std::polar(1.0, accumulator));
|
||||
phase += (2*halfAngle) / m_nbSymbols;
|
||||
}
|
||||
|
||||
// Duplicate table to allow processing from arbitrary offsets
|
||||
std::copy(m_downChirps, m_downChirps+m_fftLength, m_downChirps+m_fftLength);
|
||||
std::copy(m_upChirps, m_upChirps+m_fftLength, m_upChirps+m_fftLength);
|
||||
}
|
||||
|
||||
void LoRaDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||
@ -166,22 +178,36 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
}
|
||||
}
|
||||
|
||||
if (preambleFound)
|
||||
if ((preambleFound) && (m_magsq > 1e-9))
|
||||
{
|
||||
if (m_spectrumSink) {
|
||||
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
|
||||
}
|
||||
|
||||
qDebug("LoRaDemodSink::processSample: preamble found: %u|%f", m_argMaxHistory[0], m_magsq);
|
||||
m_chirp0 = m_argMaxHistory[0];
|
||||
m_chirp = m_chirp0;
|
||||
m_fftCounter = 0;
|
||||
m_chirp = m_argMaxHistory[0];
|
||||
m_fftCounter = m_chirp;
|
||||
m_chirp0 = 0;
|
||||
m_chirpCount = 0;
|
||||
m_state = LoRaStatePreambleResyc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_state == LoRaStatePreambleResyc)
|
||||
{
|
||||
m_fftCounter++;
|
||||
|
||||
if (m_fftCounter == m_fftLength)
|
||||
{
|
||||
if (m_spectrumSink) {
|
||||
m_spectrumSink->feed(m_spectrumLine, m_nbSymbols);
|
||||
}
|
||||
|
||||
m_fftCounter = 0;
|
||||
m_state = LoRaStatePreamble;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_state == LoRaStatePreamble) // preamble found look for SFD start
|
||||
{
|
||||
m_fft->in()[m_fftCounter] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
|
||||
@ -234,12 +260,29 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
{
|
||||
m_syncWord = round(m_preambleHistory[m_chirpCount-2] / 8.0);
|
||||
m_syncWord += 16 * round(m_preambleHistory[m_chirpCount-3] / 8.0);
|
||||
qDebug("LoRaDemodSink::processSample: SFD found: up: %u|%f down: %u|%f sync: %x", imax, magsq, imaxSFD, magsqSFD, m_syncWord);
|
||||
qDebug("LoRaDemodSink::processSample: SFD found: up: %4u|%11.6f - down: %4u|%11.6f sync: %x", imax, magsq, imaxSFD, magsqSFD, m_syncWord);
|
||||
|
||||
int sadj = 0;
|
||||
int nadj = 0;
|
||||
int zadj;
|
||||
int sfdSkip = m_sfdSkip;
|
||||
|
||||
for (int i = 0; i < m_chirpCount-3; i++)
|
||||
{
|
||||
sadj += m_preambleHistory[i] > m_nbSymbols/2 ? m_preambleHistory[i] - m_nbSymbols : m_preambleHistory[i];
|
||||
nadj++;
|
||||
}
|
||||
|
||||
zadj = nadj == 0 ? 0 : sadj / nadj;
|
||||
zadj = zadj < -(sfdSkip/2) ? -(sfdSkip/2) : zadj > sfdSkip/2 ? sfdSkip/2 : zadj;
|
||||
qDebug("LoRaDemodSink::processSample: zero adjust: %d (%d)", zadj, nadj);
|
||||
|
||||
m_sfdSkipCounter = 0;
|
||||
m_fftCounter = m_fftLength - m_sfdSkip;
|
||||
std::copy(m_fftBuffer+m_sfdSkip, m_fftBuffer+(m_fftLength-m_sfdSkip), m_fftBuffer); // prepare sliding fft
|
||||
m_state = LoRaStateSlideSFD;
|
||||
m_fftCounter = m_fftLength - m_sfdSkip + zadj;
|
||||
m_chirp += zadj;
|
||||
//std::copy(m_fftBuffer+m_sfdSkip, m_fftBuffer+(m_fftLength-m_sfdSkip), m_fftBuffer); // prepare sliding fft
|
||||
m_magsq = magsqSFD;
|
||||
m_state = LoRaStateSkipSFD; //LoRaStateSlideSFD;
|
||||
}
|
||||
}
|
||||
else if (m_chirpCount > m_maxSFDSearchChirps) // SFD missed start over
|
||||
@ -252,7 +295,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
|
||||
}
|
||||
|
||||
qDebug("LoRaDemodSink::processSample: SFD search: up: %u|%f down: %u|%f", imax, magsq, imaxSFD, magsqSFD);
|
||||
qDebug("LoRaDemodSink::processSample: SFD search: up: %4u|%11.6f - down: %4u|%11.6f", imax, magsq, imaxSFD, magsqSFD);
|
||||
m_magsq = magsq;
|
||||
}
|
||||
}
|
||||
@ -302,7 +345,11 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
m_fftInterpolation
|
||||
) / m_fftInterpolation;
|
||||
|
||||
qDebug("LoRaDemodSink::processSample: SFD slide %u %u|%f", m_sfdSkipCounter, imaxSFD, magsqSFD);
|
||||
if (m_spectrumSink) {
|
||||
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
|
||||
}
|
||||
|
||||
qDebug("LoRaDemodSink::processSample: SFD slide %u %4u|%11.6f", m_sfdSkipCounter, imaxSFD, magsqSFD);
|
||||
|
||||
if (m_sfdSkipCounter == m_sfdFourths) // 1.25 SFD chips length
|
||||
{
|
||||
@ -328,7 +375,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
m_fftCounter = 0;
|
||||
double magsq;
|
||||
|
||||
unsigned int symbol = round(
|
||||
unsigned int symbol = evalSymbol(
|
||||
argmax(
|
||||
m_fft->out(),
|
||||
m_fftInterpolation,
|
||||
@ -336,8 +383,8 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
magsq,
|
||||
m_spectrumBuffer,
|
||||
m_fftInterpolation
|
||||
) / ((float) m_fftInterpolation * (1<<m_settings.m_deBits))
|
||||
);
|
||||
)
|
||||
) % m_nbSymbolsEff;
|
||||
|
||||
if (m_spectrumSink) {
|
||||
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
|
||||
@ -439,6 +486,17 @@ int LoRaDemodSink::toSigned(int u, int intSize)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int LoRaDemodSink::evalSymbol(unsigned int rawSymbol)
|
||||
{
|
||||
unsigned int spread = m_fftInterpolation * (1<<m_settings.m_deBits);
|
||||
|
||||
if (spread < 2 ) {
|
||||
return rawSymbol;
|
||||
} else {
|
||||
return (rawSymbol + spread/2 - 1) / spread; // middle point goes to symbol below (smear to the right)
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force)
|
||||
{
|
||||
qDebug() << "LoRaDemodSink::applyChannelSettings:"
|
||||
@ -455,7 +513,7 @@ void LoRaDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, i
|
||||
if ((channelSampleRate != m_channelSampleRate) ||
|
||||
(bandwidth != m_bandwidth) || force)
|
||||
{
|
||||
m_interpolator.create(16, channelSampleRate, bandwidth / 1.9f);
|
||||
m_interpolator.create(16, channelSampleRate, bandwidth / 1.25f);
|
||||
m_interpolatorDistance = (Real) channelSampleRate / (Real) bandwidth;
|
||||
m_sampleDistanceRemain = 0;
|
||||
qDebug() << "LoRaDemodSink::applyChannelSettings: m_interpolator.create:"
|
||||
@ -477,8 +535,9 @@ void LoRaDemodSink::applySettings(const LoRaDemodSettings& settings, bool force)
|
||||
<< " m_title: " << settings.m_title
|
||||
<< " force: " << force;
|
||||
|
||||
if ((settings.m_spreadFactor != m_settings.m_spreadFactor) || force) {
|
||||
initSF(settings.m_spreadFactor);
|
||||
if ((settings.m_spreadFactor != m_settings.m_spreadFactor)
|
||||
|| (settings.m_deBits != m_settings.m_deBits) || force) {
|
||||
initSF(settings.m_spreadFactor, settings.m_deBits);
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
|
||||
@ -36,7 +36,6 @@ public:
|
||||
LoRaDemodSink();
|
||||
~LoRaDemodSink();
|
||||
|
||||
void initSF(unsigned int sf); //!< Init tables, FFTs, depending on spread factor
|
||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
||||
|
||||
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; }
|
||||
@ -48,6 +47,7 @@ private:
|
||||
{
|
||||
LoRaStateReset, //!< Reset everything to start all over
|
||||
LoRaStateDetectPreamble, //!< Look for preamble
|
||||
LoRaStatePreambleResyc, //!< Synchronize with what is left of preamble chirp
|
||||
LoRaStatePreamble, //!< Preamble is found and look for SFD start
|
||||
LoRaStateSkipSFD, //!< Skip SFD
|
||||
LoRaStateSlideSFD, //!< Sliding FFTs while going through SFD (not the skip option)
|
||||
@ -66,7 +66,7 @@ private:
|
||||
static const unsigned int m_requiredPreambleChirps = 4; //!< Number of chirps required to estimate preamble
|
||||
static const unsigned int m_maxSFDSearchChirps = 8; //!< Maximum number of chirps when looking for SFD after preamble detection
|
||||
static const unsigned int m_sfdFourths = 5; //!< Number of SFD chip period fourths to skip until payload
|
||||
static const unsigned int m_fftInterpolation = 1; //!< FFT interpolation factor (usually a power of 2)
|
||||
static const unsigned int m_fftInterpolation = 2; //!< FFT interpolation factor (usually a power of 2)
|
||||
|
||||
FFTEngine *m_fft;
|
||||
FFTEngine *m_fftSFD;
|
||||
@ -74,6 +74,7 @@ private:
|
||||
Complex *m_downChirps;
|
||||
Complex *m_upChirps;
|
||||
Complex *m_fftBuffer;
|
||||
Complex *m_spectrumLine;
|
||||
unsigned int m_fftCounter;
|
||||
unsigned int m_argMaxHistory[m_requiredPreambleChirps];
|
||||
unsigned int m_argMaxHistoryCounter;
|
||||
@ -93,9 +94,11 @@ private:
|
||||
Complex *m_spectrumBuffer;
|
||||
|
||||
unsigned int m_nbSymbols;
|
||||
unsigned int m_nbSymbolsEff; //!< effective symbols considering DE bits
|
||||
unsigned int m_fftLength;
|
||||
|
||||
void processSample(const Complex& ci);
|
||||
void initSF(unsigned int sf, unsigned int deBits); //!< Init tables, FFTs, depending on spread factor
|
||||
void reset();
|
||||
unsigned int argmax(
|
||||
const Complex *fftBins,
|
||||
@ -107,6 +110,7 @@ private:
|
||||
);
|
||||
void decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation);
|
||||
int toSigned(int u, int intSize);
|
||||
unsigned int evalSymbol(unsigned int rawSymbol);
|
||||
|
||||
/*
|
||||
Interleaving is "easiest" if the same number of bits is used per symbol as for FEC
|
||||
|
||||
@ -8,11 +8,11 @@
|
||||
const PluginDescriptor LoRaPlugin::m_pluginDescriptor = {
|
||||
LoRaDemod::m_channelId,
|
||||
QString("LoRa Demodulator"),
|
||||
QString("4.12.3"),
|
||||
QString("(c) 2015 John Greb"),
|
||||
QString("http://www.maintech.de"),
|
||||
QString("5.2.0"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QString("github.com/hexameron/rtl-sdrangelove")
|
||||
QString("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
LoRaPlugin::LoRaPlugin(QObject* parent) :
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user