even more stuff

This commit is contained in:
AlexandreRouma
2022-06-15 16:08:54 +02:00
parent 343ec6ca1c
commit d1318d3a0f
156 changed files with 24826 additions and 0 deletions

View File

@ -0,0 +1,303 @@
#include "iq_frontend.h"
#include "../dsp/window/blackman.h"
#include "../dsp/window/nuttall.h"
#include <spdlog/spdlog.h>
#include <gui/gui.h>
#include <core.h>
IQFrontEnd::~IQFrontEnd() {
if (!_init) { return; }
stop();
dsp::buffer::free(fftWindowBuf);
fftwf_destroy_plan(fftwPlan);
fftwf_free(fftInBuf);
fftwf_free(fftOutBuf);
}
void IQFrontEnd::init(dsp::stream<dsp::complex_t>* in, double sampleRate, bool buffering, int decimRatio, bool dcBlocking, int fftSize, double fftRate, FFTWindow fftWindow, float* (*acquireFFTBuffer)(void* ctx), void (*releaseFFTBuffer)(void* ctx), void* fftCtx) {
_sampleRate = sampleRate;
_decimRatio = decimRatio;
_fftSize = fftSize;
_fftRate = fftRate;
_fftWindow = fftWindow;
_acquireFFTBuffer = acquireFFTBuffer;
_releaseFFTBuffer = releaseFFTBuffer;
_fftCtx = fftCtx;
effectiveSr = _sampleRate / _decimRatio;
inBuf.init(in);
inBuf.bypass = !buffering;
decim.init(NULL, _decimRatio);
dcBlock.init(NULL, genDCBlockRate(effectiveSr));
preproc.init(&inBuf.out);
preproc.addBlock(&decim, _decimRatio > 1);
preproc.addBlock(&dcBlock, dcBlocking);
split.init(preproc.out);
// TODO: Do something to avoid basically repeating this code twice
int skip;
genReshapeParams(effectiveSr, _fftSize, _fftRate, skip, _nzFFTSize);
reshape.init(&fftIn, fftSize, skip);
fftSink.init(&reshape.out, handler, this);
fftWindowBuf = dsp::buffer::alloc<float>(_nzFFTSize);
if (_fftWindow == FFTWindow::RECTANGULAR) {
for (int i = 0; i < _nzFFTSize; i++) { fftWindowBuf[i] = 0; }
}
else if (_fftWindow == FFTWindow::BLACKMAN) {
for (int i = 0; i < _nzFFTSize; i++) { fftWindowBuf[i] = dsp::window::blackman(i, _nzFFTSize); }
}
else if (_fftWindow == FFTWindow::NUTTALL) {
for (int i = 0; i < _nzFFTSize; i++) { fftWindowBuf[i] = dsp::window::nuttall(i, _nzFFTSize); }
}
fftInBuf = (fftwf_complex*)fftwf_malloc(_fftSize * sizeof(fftwf_complex));
fftOutBuf = (fftwf_complex*)fftwf_malloc(_fftSize * sizeof(fftwf_complex));
fftwPlan = fftwf_plan_dft_1d(_fftSize, fftInBuf, fftOutBuf, FFTW_FORWARD, FFTW_ESTIMATE);
// Clear the rest of the FFT input buffer
dsp::buffer::clear(fftInBuf, _fftSize - _nzFFTSize, _nzFFTSize);
split.bindStream(&fftIn);
_init = true;
}
void IQFrontEnd::setInput(dsp::stream<dsp::complex_t>* in) {
inBuf.setInput(in);
}
void IQFrontEnd::setSampleRate(double sampleRate) {
// Temp stop the necessary blocks
dcBlock.tempStop();
for (auto& [name, vfo] : vfos) {
vfo->tempStop();
}
// Update the samplerate
_sampleRate = sampleRate;
effectiveSr = _sampleRate / _decimRatio;
dcBlock.setRate(genDCBlockRate(effectiveSr));
for (auto& [name, vfo] : vfos) {
vfo->setInSamplerate(effectiveSr);
}
// Reconfigure the FFT
updateFFTPath();
// Restart blocks
dcBlock.tempStart();
for (auto& [name, vfo] : vfos) {
vfo->tempStart();
}
}
void IQFrontEnd::setBuffering(bool enabled) {
inBuf.bypass = !enabled;
}
void IQFrontEnd::setDecimation(int ratio) {
// Temp stop the decimator
decim.tempStop();
// Update the decimation ratio
_decimRatio = ratio;
if (_decimRatio > 1) { decim.setRatio(_decimRatio); }
setSampleRate(_sampleRate);
// Restart the decimator if it was running
decim.tempStart();
// Enable or disable in the chain
preproc.setBlockEnabled(&decim, _decimRatio > 1, [=](dsp::stream<dsp::complex_t>* out){ split.setInput(out); });
// Update the DSP sample rate (TODO: Find a way to get rid of this)
core::setInputSampleRate(_sampleRate);
}
void IQFrontEnd::setDCBlocking(bool enabled) {
preproc.setBlockEnabled(&dcBlock, enabled, [=](dsp::stream<dsp::complex_t>* out){ split.setInput(out); });
}
void IQFrontEnd::bindIQStream(dsp::stream<dsp::complex_t>* stream) {
split.bindStream(stream);
}
void IQFrontEnd::unbindIQStream(dsp::stream<dsp::complex_t>* stream) {
split.unbindStream(stream);
}
dsp::channel::RxVFO* IQFrontEnd::addVFO(std::string name, double sampleRate, double bandwidth, double offset) {
// Make sure no other VFO with that name already exists
if (vfos.find(name) != vfos.end()) {
spdlog::error("[IQFrontEnd] Tried to add VFO with existing name.");
return NULL;
}
// Create VFO and its input stream
dsp::stream<dsp::complex_t>* vfoIn = new dsp::stream<dsp::complex_t>;
dsp::channel::RxVFO* vfo = new dsp::channel::RxVFO(vfoIn, effectiveSr, sampleRate, bandwidth, offset);
// Register them
vfoStreams[name] = vfoIn;
vfos[name] = vfo;
bindIQStream(vfoIn);
// Start VFO
vfo->start();
return vfo;
}
void IQFrontEnd::removeVFO(std::string name) {
// Make sure that a VFO with that name exists
if (vfos.find(name) == vfos.end()) {
spdlog::error("[IQFrontEnd] Tried to remove a VFO that doesn't exist.");
return;
}
// Remove the VFO and stream from registry
dsp::stream<dsp::complex_t>* vfoIn = vfoStreams[name];
dsp::channel::RxVFO* vfo = vfos[name];
// Stop the VFO
vfo->stop();
unbindIQStream(vfoIn);
vfoStreams.erase(name);
vfos.erase(name);
// Delete the VFO and its input stream
delete vfo;
delete vfoIn;
}
void IQFrontEnd::setFFTSize(int size) {
_fftSize = size;
updateFFTPath();
}
void IQFrontEnd::setFFTRate(double rate) {
_fftRate = rate;
updateFFTPath();
}
void IQFrontEnd::setFFTWindow(FFTWindow fftWindow) {
_fftWindow = fftWindow;
updateFFTPath();
}
void IQFrontEnd::flushInputBuffer() {
inBuf.flush();
}
void IQFrontEnd::start() {
// Start input buffer
inBuf.start();
// Start pre-proc chain (automatically start all bound blocks)
preproc.start();
// Start IQ splitter
split.start();
// Start all VFOs
for (auto& [name, vfo] : vfos) {
vfo->start();
}
// Start FFT chain
reshape.start();
fftSink.start();
}
void IQFrontEnd::stop() {
// Stop input buffer
inBuf.stop();
// Stop pre-proc chain (automatically start all bound blocks)
preproc.stop();
// Stop IQ splitter
split.stop();
// Stop all VFOs
for (auto& [name, vfo] : vfos) {
vfo->stop();
}
// Stop FFT chain
reshape.stop();
fftSink.stop();
}
double IQFrontEnd::getEffectiveSamplerate() {
return effectiveSr;
}
void IQFrontEnd::handler(dsp::complex_t* data, int count, void* ctx) {
IQFrontEnd* _this = (IQFrontEnd*)ctx;
// Apply window
volk_32fc_32f_multiply_32fc((lv_32fc_t*)_this->fftInBuf, (lv_32fc_t*)data, _this->fftWindowBuf, _this->_nzFFTSize);
// Execute FFT
fftwf_execute(_this->fftwPlan);
// Aquire buffer
float* fftBuf = _this->_acquireFFTBuffer(_this->_fftCtx);
// Convert the complex output of the FFT to dB amplitude
if (fftBuf) {
volk_32fc_s32f_power_spectrum_32f(fftBuf, (lv_32fc_t*)_this->fftOutBuf, _this->_fftSize, _this->_fftSize);
}
// Release buffer
_this->_releaseFFTBuffer(_this->_fftCtx);
}
void IQFrontEnd::updateFFTPath() {
// Temp stop branch
reshape.tempStop();
fftSink.tempStop();
// Update reshaper settings
int skip;
genReshapeParams(effectiveSr, _fftSize, _fftRate, skip, _nzFFTSize);
reshape.setKeep(_nzFFTSize);
reshape.setSkip(skip);
// Update window
dsp::buffer::free(fftWindowBuf);
fftWindowBuf = dsp::buffer::alloc<float>(_nzFFTSize);
if (_fftWindow == FFTWindow::RECTANGULAR) {
for (int i = 0; i < _nzFFTSize; i++) { fftWindowBuf[i] = 1.0f * ((i % 2) ? -1.0f : 1.0f); }
}
else if (_fftWindow == FFTWindow::BLACKMAN) {
for (int i = 0; i < _nzFFTSize; i++) { fftWindowBuf[i] = dsp::window::blackman(i, _nzFFTSize) * ((i % 2) ? -1.0f : 1.0f); }
}
else if (_fftWindow == FFTWindow::NUTTALL) {
for (int i = 0; i < _nzFFTSize; i++) { fftWindowBuf[i] = dsp::window::nuttall(i, _nzFFTSize) * ((i % 2) ? -1.0f : 1.0f); }
}
// Update FFT plan
fftwf_free(fftInBuf);
fftwf_free(fftOutBuf);
fftInBuf = (fftwf_complex*)fftwf_malloc(_fftSize * sizeof(fftwf_complex));
fftOutBuf = (fftwf_complex*)fftwf_malloc(_fftSize * sizeof(fftwf_complex));
fftwPlan = fftwf_plan_dft_1d(_fftSize, fftInBuf, fftOutBuf, FFTW_FORWARD, FFTW_ESTIMATE);
// Clear the rest of the FFT input buffer
dsp::buffer::clear(fftInBuf, _fftSize - _nzFFTSize, _nzFFTSize);
// Update waterfall (TODO: This is annoying, it makes this module non testable and will constantly clear the waterfall for any reason)
gui::waterfall.setRawFFTSize(_fftSize);
// Restart branch
reshape.tempStart();
fftSink.tempStart();
}

View File

@ -0,0 +1,104 @@
#pragma once
#include "../dsp/buffer/frame_buffer.h"
#include "../dsp/buffer/reshaper.h"
#include "../dsp/multirate/power_decimator.h"
#include "../dsp/correction/dc_blocker.h"
#include "../dsp/chain.h"
#include "../dsp/routing/splitter.h"
#include "../dsp/channel/rx_vfo.h"
#include "../dsp/sink/handler_sink.h"
#include <fftw3.h>
class IQFrontEnd {
public:
~IQFrontEnd();
enum FFTWindow {
RECTANGULAR,
BLACKMAN,
NUTTALL
};
void init(dsp::stream<dsp::complex_t>* in, double sampleRate, bool buffering, int decimRatio, bool dcBlocking, int fftSize, double fftRate, FFTWindow fftWindow, float* (*acquireFFTBuffer)(void* ctx), void (*releaseFFTBuffer)(void* ctx), void* fftCtx);
void setInput(dsp::stream<dsp::complex_t>* in);
void setSampleRate(double sampleRate);
inline double getSampleRate() { return _sampleRate; }
void setBuffering(bool enabled);
void setDecimation(int ratio);
void setDCBlocking(bool enabled);
void bindIQStream(dsp::stream<dsp::complex_t>* stream);
void unbindIQStream(dsp::stream<dsp::complex_t>* stream);
dsp::channel::RxVFO* addVFO(std::string name, double sampleRate, double bandwidth, double offset);
void removeVFO(std::string name);
void setFFTSize(int size);
void setFFTRate(double rate);
void setFFTWindow(FFTWindow fftWindow);
void flushInputBuffer();
void start();
void stop();
double getEffectiveSamplerate();
protected:
static void handler(dsp::complex_t* data, int count, void* ctx);
void updateFFTPath();
static inline double genDCBlockRate(double sampleRate) {
return 50.0 / sampleRate;
}
static inline void genReshapeParams(double sampleRate, int size, double rate, int& skip, int& nzSampCount) {
int fftInterval = round(sampleRate / rate);
nzSampCount = std::min<int>(fftInterval, size);
skip = fftInterval - nzSampCount;
}
// Input buffer
dsp::buffer::SampleFrameBuffer<dsp::complex_t> inBuf;
// Pre-processing chain
dsp::multirate::PowerDecimator<dsp::complex_t> decim;
dsp::correction::DCBlocker<dsp::complex_t> dcBlock;
dsp::chain<dsp::complex_t> preproc;
// Splitting
dsp::routing::Splitter<dsp::complex_t> split;
// FFT
dsp::stream<dsp::complex_t> fftIn;
dsp::buffer::Reshaper<dsp::complex_t> reshape;
dsp::sink::Handler<dsp::complex_t> fftSink;
// VFOs
std::map<std::string, dsp::stream<dsp::complex_t>*> vfoStreams;
std::map<std::string, dsp::channel::RxVFO*> vfos;
// Parameters
double _sampleRate;
double _decimRatio;
int _fftSize;
double _fftRate;
FFTWindow _fftWindow;
float* (*_acquireFFTBuffer)(void* ctx);
void (*_releaseFFTBuffer)(void* ctx);
void* _fftCtx;
// Processing data
int _nzFFTSize;
float* fftWindowBuf;
fftwf_complex *fftInBuf, *fftOutBuf;
fftwf_plan fftwPlan;
float* fftDbOut;
double effectiveSr;
bool _init = false;
};