1
0
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:
f4exb 2020-02-12 13:17:15 +01:00
parent a5dc668376
commit 60db0ab868
7 changed files with 95 additions and 30 deletions

View File

@ -137,7 +137,7 @@ void LoRaDemodBaseband::applySettings(const LoRaDemodSettings& settings, bool fo
|| (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{ {
m_channelizer->setChannelization( m_channelizer->setChannelization(
LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex], LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex]*LoRaDemodSettings::oversampling,
settings.m_inputFrequencyOffset settings.m_inputFrequencyOffset
); );
m_sink.applyChannelSettings( m_sink.applyChannelSettings(

View File

@ -282,7 +282,7 @@ void LoRaDemodGUI::displaySettings()
void LoRaDemodGUI::setBandwidths() void LoRaDemodGUI::setBandwidths()
{ {
int maxBandwidth = m_basebandSampleRate; int maxBandwidth = m_basebandSampleRate/LoRaDemodSettings::oversampling;
int maxIndex = 0; int maxIndex = 0;
for (; (maxIndex < LoRaDemodSettings::nbBandwidths) && (LoRaDemodSettings::bandwidths[maxIndex] <= maxBandwidth); maxIndex++) for (; (maxIndex < LoRaDemodSettings::nbBandwidths) && (LoRaDemodSettings::bandwidths[maxIndex] <= maxBandwidth); maxIndex++)

View File

@ -26,6 +26,7 @@
const int LoRaDemodSettings::bandwidths[] = {2500, 7813, 10417, 15625, 20833, 31250, 41667, 62500, 125000, 250000, 500000}; const int LoRaDemodSettings::bandwidths[] = {2500, 7813, 10417, 15625, 20833, 31250, 41667, 62500, 125000, 250000, 500000};
const int LoRaDemodSettings::nbBandwidths = 11; const int LoRaDemodSettings::nbBandwidths = 11;
const int LoRaDemodSettings::oversampling = 2;
LoRaDemodSettings::LoRaDemodSettings() : LoRaDemodSettings::LoRaDemodSettings() :
m_inputFrequencyOffset(0), m_inputFrequencyOffset(0),

View File

@ -40,6 +40,7 @@ struct LoRaDemodSettings
static const int bandwidths[]; static const int bandwidths[];
static const int nbBandwidths; static const int nbBandwidths;
static const int oversampling;
LoRaDemodSettings(); LoRaDemodSettings();
void resetToDefaults(); void resetToDefaults();

View File

@ -30,7 +30,8 @@ LoRaDemodSink::LoRaDemodSink() :
m_spectrumBuffer(nullptr), m_spectrumBuffer(nullptr),
m_downChirps(nullptr), m_downChirps(nullptr),
m_upChirps(nullptr), m_upChirps(nullptr),
m_fftBuffer(nullptr) m_fftBuffer(nullptr),
m_spectrumLine(nullptr)
{ {
m_bandwidth = LoRaDemodSettings::bandwidths[0]; m_bandwidth = LoRaDemodSettings::bandwidths[0];
m_channelSampleRate = 96000; m_channelSampleRate = 96000;
@ -47,7 +48,7 @@ LoRaDemodSink::LoRaDemodSink() :
m_fft = FFTEngine::create(); m_fft = FFTEngine::create();
m_fftSFD = FFTEngine::create(); m_fftSFD = FFTEngine::create();
initSF(m_settings.m_spreadFactor); initSF(m_settings.m_spreadFactor, m_settings.m_deBits);
} }
LoRaDemodSink::~LoRaDemodSink() LoRaDemodSink::~LoRaDemodSink()
@ -57,9 +58,10 @@ LoRaDemodSink::~LoRaDemodSink()
delete[] m_downChirps; delete[] m_downChirps;
delete[] m_upChirps; delete[] m_upChirps;
delete[] m_spectrumBuffer; delete[] m_spectrumBuffer;
delete[] m_spectrumLine;
} }
void LoRaDemodSink::initSF(unsigned int sf) void LoRaDemodSink::initSF(unsigned int sf, unsigned int deBits)
{ {
if (m_downChirps) { if (m_downChirps) {
delete[] m_downChirps; delete[] m_downChirps;
@ -73,8 +75,12 @@ void LoRaDemodSink::initSF(unsigned int sf)
if (m_spectrumBuffer) { if (m_spectrumBuffer) {
delete[] m_spectrumBuffer; delete[] m_spectrumBuffer;
} }
if (m_spectrumLine) {
delete[] m_spectrumLine;
}
m_nbSymbols = 1 << sf; m_nbSymbols = 1 << sf;
m_nbSymbolsEff = 1 << (sf - deBits);
m_fftLength = m_nbSymbols; m_fftLength = m_nbSymbols;
m_fft->configure(m_fftInterpolation*m_fftLength, false); m_fft->configure(m_fftInterpolation*m_fftLength, false);
m_fftSFD->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_sfdSkip = m_fftLength / 4;
m_fftWindow.create(FFTWindow::Function::Kaiser, m_fftLength); m_fftWindow.create(FFTWindow::Function::Kaiser, m_fftLength);
m_fftWindow.setKaiserAlpha(M_PI); 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_upChirps = new Complex[2*m_nbSymbols];
m_fftBuffer = new Complex[m_fftInterpolation*m_fftLength]; m_fftBuffer = new Complex[m_fftInterpolation*m_fftLength];
m_spectrumBuffer = new Complex[m_nbSymbols]; 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 halfAngle = M_PI;
float phase = -halfAngle; float phase = -halfAngle;
double accumulator = 0; 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); accumulator = fmod(accumulator + phase, 2*M_PI);
m_downChirps[i] = Complex(std::conj(std::polar(1.0, accumulator))); m_downChirps[i] = Complex(std::conj(std::polar(1.0, accumulator)));
m_upChirps[i] = Complex(std::polar(1.0, accumulator)); m_upChirps[i] = Complex(std::polar(1.0, accumulator));
phase += (2*halfAngle) / m_nbSymbols; 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) 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) { if (m_spectrumSink) {
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols); m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
} }
qDebug("LoRaDemodSink::processSample: preamble found: %u|%f", m_argMaxHistory[0], m_magsq); qDebug("LoRaDemodSink::processSample: preamble found: %u|%f", m_argMaxHistory[0], m_magsq);
m_chirp0 = m_argMaxHistory[0]; m_chirp = m_argMaxHistory[0];
m_chirp = m_chirp0; m_fftCounter = m_chirp;
m_fftCounter = 0; m_chirp0 = 0;
m_chirpCount = 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; m_state = LoRaStatePreamble;
} }
} }
}
}
else if (m_state == LoRaStatePreamble) // preamble found look for SFD start 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 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 = round(m_preambleHistory[m_chirpCount-2] / 8.0);
m_syncWord += 16 * round(m_preambleHistory[m_chirpCount-3] / 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_sfdSkipCounter = 0;
m_fftCounter = m_fftLength - m_sfdSkip; m_fftCounter = m_fftLength - m_sfdSkip + zadj;
std::copy(m_fftBuffer+m_sfdSkip, m_fftBuffer+(m_fftLength-m_sfdSkip), m_fftBuffer); // prepare sliding fft m_chirp += zadj;
m_state = LoRaStateSlideSFD; //std::copy(m_fftBuffer+m_sfdSkip, m_fftBuffer+(m_fftLength-m_sfdSkip), m_fftBuffer); // prepare sliding fft
m_magsq = magsqSFD; m_magsq = magsqSFD;
m_state = LoRaStateSkipSFD; //LoRaStateSlideSFD;
} }
} }
else if (m_chirpCount > m_maxSFDSearchChirps) // SFD missed start over 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); 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; m_magsq = magsq;
} }
} }
@ -302,7 +345,11 @@ void LoRaDemodSink::processSample(const Complex& ci)
m_fftInterpolation m_fftInterpolation
) / 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 if (m_sfdSkipCounter == m_sfdFourths) // 1.25 SFD chips length
{ {
@ -328,7 +375,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
m_fftCounter = 0; m_fftCounter = 0;
double magsq; double magsq;
unsigned int symbol = round( unsigned int symbol = evalSymbol(
argmax( argmax(
m_fft->out(), m_fft->out(),
m_fftInterpolation, m_fftInterpolation,
@ -336,8 +383,8 @@ void LoRaDemodSink::processSample(const Complex& ci)
magsq, magsq,
m_spectrumBuffer, m_spectrumBuffer,
m_fftInterpolation m_fftInterpolation
) / ((float) m_fftInterpolation * (1<<m_settings.m_deBits)) )
); ) % m_nbSymbolsEff;
if (m_spectrumSink) { if (m_spectrumSink) {
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols); 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) void LoRaDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force)
{ {
qDebug() << "LoRaDemodSink::applyChannelSettings:" qDebug() << "LoRaDemodSink::applyChannelSettings:"
@ -455,7 +513,7 @@ void LoRaDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, i
if ((channelSampleRate != m_channelSampleRate) || if ((channelSampleRate != m_channelSampleRate) ||
(bandwidth != m_bandwidth) || force) (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_interpolatorDistance = (Real) channelSampleRate / (Real) bandwidth;
m_sampleDistanceRemain = 0; m_sampleDistanceRemain = 0;
qDebug() << "LoRaDemodSink::applyChannelSettings: m_interpolator.create:" qDebug() << "LoRaDemodSink::applyChannelSettings: m_interpolator.create:"
@ -477,8 +535,9 @@ void LoRaDemodSink::applySettings(const LoRaDemodSettings& settings, bool force)
<< " m_title: " << settings.m_title << " m_title: " << settings.m_title
<< " force: " << force; << " force: " << force;
if ((settings.m_spreadFactor != m_settings.m_spreadFactor) || force) { if ((settings.m_spreadFactor != m_settings.m_spreadFactor)
initSF(settings.m_spreadFactor); || (settings.m_deBits != m_settings.m_deBits) || force) {
initSF(settings.m_spreadFactor, settings.m_deBits);
} }
m_settings = settings; m_settings = settings;

View File

@ -36,7 +36,6 @@ public:
LoRaDemodSink(); LoRaDemodSink();
~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); virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; } void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; }
@ -48,6 +47,7 @@ private:
{ {
LoRaStateReset, //!< Reset everything to start all over LoRaStateReset, //!< Reset everything to start all over
LoRaStateDetectPreamble, //!< Look for preamble LoRaStateDetectPreamble, //!< Look for preamble
LoRaStatePreambleResyc, //!< Synchronize with what is left of preamble chirp
LoRaStatePreamble, //!< Preamble is found and look for SFD start LoRaStatePreamble, //!< Preamble is found and look for SFD start
LoRaStateSkipSFD, //!< Skip SFD LoRaStateSkipSFD, //!< Skip SFD
LoRaStateSlideSFD, //!< Sliding FFTs while going through SFD (not the skip option) 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_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_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_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_fft;
FFTEngine *m_fftSFD; FFTEngine *m_fftSFD;
@ -74,6 +74,7 @@ private:
Complex *m_downChirps; Complex *m_downChirps;
Complex *m_upChirps; Complex *m_upChirps;
Complex *m_fftBuffer; Complex *m_fftBuffer;
Complex *m_spectrumLine;
unsigned int m_fftCounter; unsigned int m_fftCounter;
unsigned int m_argMaxHistory[m_requiredPreambleChirps]; unsigned int m_argMaxHistory[m_requiredPreambleChirps];
unsigned int m_argMaxHistoryCounter; unsigned int m_argMaxHistoryCounter;
@ -93,9 +94,11 @@ private:
Complex *m_spectrumBuffer; Complex *m_spectrumBuffer;
unsigned int m_nbSymbols; unsigned int m_nbSymbols;
unsigned int m_nbSymbolsEff; //!< effective symbols considering DE bits
unsigned int m_fftLength; unsigned int m_fftLength;
void processSample(const Complex& ci); void processSample(const Complex& ci);
void initSF(unsigned int sf, unsigned int deBits); //!< Init tables, FFTs, depending on spread factor
void reset(); void reset();
unsigned int argmax( unsigned int argmax(
const Complex *fftBins, const Complex *fftBins,
@ -107,6 +110,7 @@ private:
); );
void decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation); void decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation);
int toSigned(int u, int intSize); 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 Interleaving is "easiest" if the same number of bits is used per symbol as for FEC

View File

@ -8,11 +8,11 @@
const PluginDescriptor LoRaPlugin::m_pluginDescriptor = { const PluginDescriptor LoRaPlugin::m_pluginDescriptor = {
LoRaDemod::m_channelId, LoRaDemod::m_channelId,
QString("LoRa Demodulator"), QString("LoRa Demodulator"),
QString("4.12.3"), QString("5.2.0"),
QString("(c) 2015 John Greb"), QString("(c) Edouard Griffiths, F4EXB"),
QString("http://www.maintech.de"), QString("https://github.com/f4exb/sdrangel"),
true, true,
QString("github.com/hexameron/rtl-sdrangelove") QString("https://github.com/f4exb/sdrangel")
}; };
LoRaPlugin::LoRaPlugin(QObject* parent) : LoRaPlugin::LoRaPlugin(QObject* parent) :