1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-11-10 16:20:24 -05:00

197 lines
7.9 KiB
C
Raw Normal View History

2019-12-03 18:49:52 +01:00
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LORADEMODSINK_H
#define INCLUDE_LORADEMODSINK_H
#include <vector>
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "util/message.h"
2020-02-08 18:21:38 +01:00
#include "dsp/fftwindow.h"
2019-12-03 18:49:52 +01:00
#include "lorademodsettings.h"
class BasebandSampleSink;
2020-02-08 18:21:38 +01:00
class FFTEngine;
2019-12-03 18:49:52 +01:00
class LoRaDemodSink : public ChannelSampleSink {
public:
LoRaDemodSink();
~LoRaDemodSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; }
void applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force = false);
void applySettings(const LoRaDemodSettings& settings, bool force = false);
private:
2020-02-08 18:21:38 +01:00
enum LoRaState
{
LoRaStateReset, //!< Reset everything to start all over
LoRaStateDetectPreamble, //!< Look for preamble
2020-02-12 13:17:15 +01:00
LoRaStatePreambleResyc, //!< Synchronize with what is left of preamble chirp
2020-02-08 18:21:38 +01:00
LoRaStatePreamble, //!< Preamble is found and look for SFD start
LoRaStateSkipSFD, //!< Skip SFD
LoRaStateSlideSFD, //!< Sliding FFTs while going through SFD (not the skip option)
LoRaStateReadPayload,
LoRaStateTest
};
2019-12-03 18:49:52 +01:00
LoRaDemodSettings m_settings;
2020-02-08 18:21:38 +01:00
LoRaState m_state;
int m_bandwidth;
2019-12-03 18:49:52 +01:00
int m_channelSampleRate;
int m_channelFrequencyOffset;
int m_chirp;
2020-02-08 18:21:38 +01:00
int m_chirp0;
static const unsigned int m_requiredPreambleChirps = 4; //!< Number of chirps required to estimate preamble
2020-02-08 18:21:38 +01:00
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
2020-02-12 13:17:15 +01:00
static const unsigned int m_fftInterpolation = 2; //!< FFT interpolation factor (usually a power of 2)
2020-02-08 18:21:38 +01:00
FFTEngine *m_fft;
FFTEngine *m_fftSFD;
FFTWindow m_fftWindow;
Complex *m_downChirps;
Complex *m_upChirps;
Complex *m_fftBuffer;
2020-02-12 13:17:15 +01:00
Complex *m_spectrumLine;
2020-02-08 18:21:38 +01:00
unsigned int m_fftCounter;
unsigned int m_argMaxHistory[m_requiredPreambleChirps];
unsigned int m_argMaxHistoryCounter;
unsigned int m_preambleHistory[m_maxSFDSearchChirps];
unsigned int m_syncWord;
double m_magsq;
unsigned int m_chirpCount; //!< Generic chirp counter
unsigned int m_sfdSkip; //!< Number of samples in a SFD skip or slide (1/4) period
unsigned int m_sfdSkipCounter; //!< Counter of skip or slide periods
2019-12-03 18:49:52 +01:00
NCO m_nco;
Interpolator m_interpolator;
Real m_sampleDistanceRemain;
2020-02-08 18:21:38 +01:00
Real m_interpolatorDistance;
2019-12-03 18:49:52 +01:00
BasebandSampleSink* m_spectrumSink;
2020-02-08 18:21:38 +01:00
Complex *m_spectrumBuffer;
2019-12-03 18:49:52 +01:00
2020-01-30 18:26:43 +01:00
unsigned int m_nbSymbols;
2020-02-12 13:17:15 +01:00
unsigned int m_nbSymbolsEff; //!< effective symbols considering DE bits
2020-02-08 18:21:38 +01:00
unsigned int m_fftLength;
void processSample(const Complex& ci);
2020-02-12 13:17:15 +01:00
void initSF(unsigned int sf, unsigned int deBits); //!< Init tables, FFTs, depending on spread factor
2020-02-08 18:21:38 +01:00
void reset();
unsigned int argmax(
const Complex *fftBins,
unsigned int fftMult,
unsigned int fftLength,
double& magsqMax,
Complex *specBuffer,
unsigned int specDecim
);
void decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation);
int toSigned(int u, int intSize);
2020-02-12 13:17:15 +01:00
unsigned int evalSymbol(unsigned int rawSymbol);
2019-12-03 18:49:52 +01:00
/*
Interleaving is "easiest" if the same number of bits is used per symbol as for FEC
Chosen mode "spreading 8, low rate" has 6 bits per symbol, so use 4:6 FEC
More spreading needs higher frequency resolution and longer time on air, increasing drift errors.
Want higher bandwidth when using more spreading, which needs more CPU and a better FFT.
Six bit Hamming can only correct long runs of drift errors when not using interleaving. Interleaving defeats the point of using Gray code and puts multiple bit errors into single FEC blocks. Hardware decoding uses RSSI to detect the symbols most likely to be damaged, so that individual bits can be repaired after de-interleaving.
Using Implicit Mode: explicit starts with a 4:8 block and seems to have a different whitening sequence.
*/
// Six bits per symbol, six chars per block
inline void interleave6(char* inout, int size)
{
int i, j;
char in[6 * 2];
short s;
for (j = 0; j < size; j+=6) {
for (i = 0; i < 6; i++)
in[i] = in[i + 6] = inout[i + j];
// top bits are swapped
for (i = 0; i < 6; i++) {
s = (32 & in[2 + i]) | (16 & in[1 + i]) | (8 & in[3 + i])
| (4 & in[4 + i]) | (2 & in[5 + i]) | (1 & in[6 + i]);
// bits are also rotated
s = (s << 3) | (s >> 3);
s &= 63;
s = (s >> i) | (s << (6 - i));
inout[i + j] = s & 63;
}
}
}
inline short toGray(short num)
{
return (num >> 1) ^ num;
}
// Ignore the FEC bits, just extract the data bits
inline void hamming6(char* c, int size)
{
int i;
for (i = 0; i < size; i++) {
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<0) | ((c[i] & 4)>>0) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<2) | ((c[i] & 2)<<2) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] &32)>>2) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] &16)>>4);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>2) | ((c[i] & 8)>>2);
}
c[i] = 0;
}
// data whitening (6 bit)
inline void prng6(char* inout, int size)
{
const char otp[] = {
//explicit mode
"cOGGg7CM2=b5a?<`i;T2of5jDAB=2DoQ9ko?h_RLQR4@Z\\`9jY\\PX89lHX8h_R]c_^@OB<0`W08ik?Mg>dQZf3kn5Je5R=R4h[<Ph90HHh9j;h:mS^?f:lQ:GG;nU:b?WFU20Lf4@A?`hYJMnW\\QZ\\AMIZ<h:jQk[PP<`6[Z"
#if 0
// implicit mode (offset 2 symbols)
"5^ZSm0=cOGMgUB=bNcb<@a^T;_f=6DEB]2ImPIKg:j]RlYT4YZ<`9hZ\\PPb;@8X8i]Zmc_6B52\\8oUPHIcBOc>dY?d9[n5Lg]b]R8hR<0`T008h9c9QJm[c?a:lQEGa;nU=b_WfUV2?V4@c=8h9B9njlQZDC@9Z<Q8\\iiX\\Rb6k:iY"
#endif
};
int i, maxchars;
maxchars = sizeof( otp );
if (size < maxchars)
maxchars = size;
for (i = 0; i < maxchars; i++)
inout[i] ^= (otp[i] - 48);
}
};
#endif // INCLUDE_LORADEMODSINK_H