diff --git a/include-gpl/dsp/fftfilt.h b/include-gpl/dsp/fftfilt.h index fee867d1b..44e7531f4 100644 --- a/include-gpl/dsp/fftfilt.h +++ b/include-gpl/dsp/fftfilt.h @@ -1,5 +1,5 @@ /* - * fftfilt.h -- Fast convolution FIR filter + * Filters from Fldigi. */ #ifndef _FFTFILT_H @@ -50,4 +50,24 @@ public: int runSSB(const cmplx& in, cmplx **out, bool usb); }; + + +/* Sliding FFT filter from Fldigi */ +class sfft { +#define K1 0.99999 +private: + int fftlen; + int first; + int last; + int ptr; + struct vrot_bins_pair; + vrot_bins_pair *vrot_bins; + cmplx *delay; + float k2; +public: + sfft(int len); + ~sfft(); + void run(const cmplx& input, cmplx *result); +}; + #endif diff --git a/plugins/channel/lora/lorademod.cpp b/plugins/channel/lora/lorademod.cpp index 089c7067c..164128106 100644 --- a/plugins/channel/lora/lorademod.cpp +++ b/plugins/channel/lora/lorademod.cpp @@ -35,10 +35,14 @@ LoRaDemod::LoRaDemod(SampleSink* sampleSink) : m_chirp = 0; m_angle = 0; + + loraFilter = new sfft(LORA_SFFT_LEN); } LoRaDemod::~LoRaDemod() { + if (loraFilter) + delete loraFilter; } void LoRaDemod::configure(MessageQueue* messageQueue, Real Bandwidth) @@ -50,7 +54,7 @@ void LoRaDemod::configure(MessageQueue* messageQueue, Real Bandwidth) void LoRaDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool pO) { Complex ci; - + cmplx bins[LORA_SFFT_LEN]; m_sampleBuffer.clear(); for(SampleVector::const_iterator it = begin; it < end; ++it) { Complex c(it->real() / 32768.0, it->imag() / 32768.0); @@ -61,6 +65,7 @@ void LoRaDemod::feed(SampleVector::const_iterator begin, SampleVector::const_ite m_angle = (m_angle + m_chirp) & (SPREADFACTOR - 1); Complex cangle(cos(M_PI*2*m_angle/SPREADFACTOR),-sin(M_PI*2*m_angle/SPREADFACTOR)); ci *= cangle; + loraFilter->run(ci, bins); m_sampleBuffer.push_back(Sample(ci.real() * 32000, ci.imag() * 32000)); m_sampleDistanceRemain += (Real)m_sampleRate / m_Bandwidth; } diff --git a/plugins/channel/lora/lorademod.h b/plugins/channel/lora/lorademod.h index ac33efa48..f3615bd46 100644 --- a/plugins/channel/lora/lorademod.h +++ b/plugins/channel/lora/lorademod.h @@ -23,9 +23,13 @@ #include "dsp/nco.h" #include "dsp/interpolator.h" #include "util/message.h" +#include "dsp/fftfilt.h" #define SPREADFACTOR (1<<8) +/* It takes a lot of CPU to run the sliding FFT */ +#define LORA_SFFT_LEN (128) + class LoRaDemod : public SampleSink { public: LoRaDemod(SampleSink* sampleSink); @@ -66,6 +70,8 @@ private: int m_chirp; int m_angle; + sfft* loraFilter; + NCO m_nco; Interpolator m_interpolator; Real m_sampleDistanceRemain; diff --git a/sdrbase/dsp/fftfilt.cxx b/sdrbase/dsp/fftfilt.cxx index bf3d8aad8..d3f776f34 100644 --- a/sdrbase/dsp/fftfilt.cxx +++ b/sdrbase/dsp/fftfilt.cxx @@ -190,3 +190,56 @@ int fftfilt::runSSB(const cmplx & in, cmplx **out, bool usb) return flen2; } +/* Sliding FFT from Fldigi */ + +struct sfft::vrot_bins_pair { + cmplx vrot; + cmplx bins; +} ; + +sfft::sfft(int len) +{ + vrot_bins = new vrot_bins_pair[len]; + delay = new cmplx[len]; + fftlen = len; + first = 0; + last = len - 1; + ptr = 0; + double phi = 0.0, tau = 2.0 * M_PI/ len; + k2 = 1.0; + for (int i = 0; i < len; i++) { + vrot_bins[i].vrot = cmplx( K1 * cos (phi), K1 * sin (phi) ); + phi += tau; + delay[i] = vrot_bins[i].bins = 0.0; + k2 *= K1; + } +} + +sfft::~sfft() +{ + delete [] vrot_bins; + delete [] delay; +} + +// Sliding FFT, cmplx input, cmplx output +// FFT is computed for each value from first to last +// Values are not stable until more than "len" samples have been processed. +// Copies the frequencies to a pointer. +void sfft::run(const cmplx& input, cmplx *result) +{ + cmplx & de = delay[ptr]; + const cmplx z( input.real() - k2 * de.real(), input.imag() - k2 * de.imag()); + de = input; + + ++ptr ; + if( ptr >= fftlen ) ptr = 0 ; + + // It is more efficient to have vrot and bins very close to each other. + for( vrot_bins_pair + *itr = vrot_bins + first, + *end = vrot_bins + last ; + itr != end ; + ++itr, result++ ) { + *result = itr->bins = itr->bins * itr->vrot + z * itr->vrot; + } +}