mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-27 06:08:37 -05:00
Experimenting with max speed waterfall (no dropped FFTs :)
- FFT Detail from zoomed-in views is greatly improved with the additional data
This commit is contained in:
parent
dfd02c1b12
commit
0802f7b756
@ -120,12 +120,18 @@ AppFrame::AppFrame() :
|
||||
vbox->AddSpacer(1);
|
||||
wxGetApp().getSpectrumProcesor()->attachOutput(spectrumCanvas->getVisualDataQueue());
|
||||
|
||||
wxGetApp().getWaterfallProcesor()->setup(2048);
|
||||
waterfallCanvas = new WaterfallCanvas(this, attribList);
|
||||
waterfallCanvas->setup(2048, 512);
|
||||
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
|
||||
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
|
||||
vbox->Add(waterfallCanvas, 20, wxEXPAND | wxALL, 0);
|
||||
wxGetApp().getSpectrumProcesor()->attachOutput(waterfallCanvas->getVisualDataQueue());
|
||||
// wxGetApp().getSpectrumProcesor()->attachOutput(waterfallCanvas->getVisualDataQueue());
|
||||
fftDistrib.setInput(wxGetApp().getWaterfallVisualQueue());
|
||||
fftDistrib.attachOutput(&fftQueue);
|
||||
wxGetApp().getWaterfallProcesor()->setInput(&fftQueue);
|
||||
wxGetApp().getWaterfallProcesor()->attachOutput(waterfallCanvas->getVisualDataQueue());
|
||||
|
||||
/*
|
||||
vbox->AddSpacer(1);
|
||||
testCanvas = new UITestCanvas(this, attribList);
|
||||
@ -690,9 +696,9 @@ void AppFrame::OnTimer(wxTimerEvent& event) {
|
||||
|
||||
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcesor();
|
||||
|
||||
proc->setView(waterfallCanvas->getViewState());
|
||||
proc->setBandwidth(waterfallCanvas->getBandwidth());
|
||||
proc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
|
||||
proc->setView(spectrumCanvas->getViewState());
|
||||
proc->setBandwidth(spectrumCanvas->getBandwidth());
|
||||
proc->setCenterFrequency(spectrumCanvas->getCenterFrequency());
|
||||
|
||||
proc->run();
|
||||
|
||||
@ -704,6 +710,27 @@ void AppFrame::OnTimer(wxTimerEvent& event) {
|
||||
|
||||
dproc->run();
|
||||
|
||||
SpectrumVisualProcessor *wproc = wxGetApp().getWaterfallProcesor();
|
||||
|
||||
int fftSize = wproc->getDesiredInputSize();
|
||||
|
||||
if (fftSize) {
|
||||
fftDistrib.setFFTSize(fftSize);
|
||||
} else {
|
||||
fftDistrib.setFFTSize(DEFAULT_FFT_SIZE);
|
||||
}
|
||||
|
||||
fftDistrib.run();
|
||||
|
||||
wproc->setView(waterfallCanvas->getViewState());
|
||||
wproc->setBandwidth(waterfallCanvas->getBandwidth());
|
||||
wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
|
||||
|
||||
while (!wproc->isInputEmpty()) {
|
||||
wproc->run();
|
||||
}
|
||||
|
||||
|
||||
scopeCanvas->Refresh();
|
||||
|
||||
waterfallCanvas->Refresh();
|
||||
|
@ -96,5 +96,8 @@ private:
|
||||
std::string currentSessionFile;
|
||||
wxTimer frame_timer;
|
||||
|
||||
FFTDataDistributor fftDistrib;
|
||||
DemodulatorThreadInputQueue fftQueue;
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
@ -63,6 +63,8 @@ bool CubicSDR::OnInit() {
|
||||
pipeSpectrumIQVisualData = new DemodulatorThreadInputQueue();
|
||||
pipeIQVisualData->set_max_num_items(1);
|
||||
|
||||
pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
|
||||
|
||||
spectrumDistributor.attachOutput(pipeDemodIQVisualData);
|
||||
spectrumDistributor.attachOutput(pipeSpectrumIQVisualData);
|
||||
|
||||
@ -85,8 +87,9 @@ bool CubicSDR::OnInit() {
|
||||
sdrPostThread = new SDRPostThread();
|
||||
sdrPostThread->setNumVisSamples(16384 * 2);
|
||||
sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
|
||||
sdrPostThread->setOutputQueue("IQVisualDataOut", pipeIQVisualData);
|
||||
|
||||
sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
|
||||
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
|
||||
|
||||
std::vector<SDRDeviceInfo *>::iterator devs_i;
|
||||
|
||||
SDRThread::enumerate_rtl(&devs);
|
||||
@ -276,6 +279,10 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcesor() {
|
||||
return &demodSpectrumProcessor;
|
||||
}
|
||||
|
||||
SpectrumVisualProcessor *CubicSDR::getWaterfallProcesor() {
|
||||
return &waterfallProcessor;
|
||||
}
|
||||
|
||||
VisualDataDistributor<DemodulatorThreadIQData> *CubicSDR::getSpectrumDistributor() {
|
||||
return &spectrumDistributor;
|
||||
}
|
||||
@ -289,6 +296,10 @@ DemodulatorThreadInputQueue* CubicSDR::getIQVisualQueue() {
|
||||
return pipeIQVisualData;
|
||||
}
|
||||
|
||||
DemodulatorThreadInputQueue* CubicSDR::getWaterfallVisualQueue() {
|
||||
return pipeWaterfallIQVisualData;
|
||||
}
|
||||
|
||||
DemodulatorMgr &CubicSDR::getDemodMgr() {
|
||||
return demodMgr;
|
||||
}
|
||||
|
@ -58,10 +58,12 @@ public:
|
||||
ScopeVisualProcessor *getScopeProcessor();
|
||||
SpectrumVisualProcessor *getSpectrumProcesor();
|
||||
SpectrumVisualProcessor *getDemodSpectrumProcesor();
|
||||
SpectrumVisualProcessor *getWaterfallProcesor();
|
||||
VisualDataDistributor<DemodulatorThreadIQData> *getSpectrumDistributor();
|
||||
|
||||
DemodulatorThreadOutputQueue* getAudioVisualQueue();
|
||||
DemodulatorThreadInputQueue* getIQVisualQueue();
|
||||
DemodulatorThreadInputQueue* getWaterfallVisualQueue();
|
||||
DemodulatorMgr &getDemodMgr();
|
||||
|
||||
void bindDemodulator(DemodulatorInstance *demod);
|
||||
@ -101,9 +103,11 @@ private:
|
||||
DemodulatorThreadOutputQueue* pipeAudioVisualData;
|
||||
DemodulatorThreadInputQueue* pipeDemodIQVisualData;
|
||||
DemodulatorThreadInputQueue* pipeSpectrumIQVisualData;
|
||||
DemodulatorThreadInputQueue* pipeWaterfallIQVisualData;
|
||||
|
||||
ScopeVisualProcessor scopeProcessor;
|
||||
SpectrumVisualProcessor spectrumProcessor;
|
||||
SpectrumVisualProcessor waterfallProcessor;
|
||||
SpectrumVisualProcessor demodSpectrumProcessor;
|
||||
|
||||
VisualDataDistributor<DemodulatorThreadIQData> spectrumDistributor;
|
||||
|
@ -14,6 +14,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last
|
||||
|
||||
fft_ceil_ma = fft_ceil_maa = 100.0;
|
||||
fft_floor_ma = fft_floor_maa = 0.0;
|
||||
desiredInputSize = 0;
|
||||
}
|
||||
|
||||
SpectrumVisualProcessor::~SpectrumVisualProcessor() {
|
||||
@ -45,8 +46,13 @@ long SpectrumVisualProcessor::getBandwidth() {
|
||||
return bandwidth.load();
|
||||
}
|
||||
|
||||
int SpectrumVisualProcessor::getDesiredInputSize() {
|
||||
return desiredInputSize;
|
||||
}
|
||||
|
||||
void SpectrumVisualProcessor::setup(int fftSize_in) {
|
||||
fftSize = fftSize_in;
|
||||
desiredInputSize = fftSize;
|
||||
|
||||
if (fftwInput) {
|
||||
free(fftwInput);
|
||||
@ -103,6 +109,8 @@ void SpectrumVisualProcessor::process() {
|
||||
|
||||
int desired_input_size = fftSize / resamplerRatio;
|
||||
|
||||
this->desiredInputSize = desired_input_size;
|
||||
|
||||
if (iqData->data.size() < desired_input_size) {
|
||||
// std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl;
|
||||
desired_input_size = iqData->data.size();
|
||||
@ -280,5 +288,7 @@ void SpectrumVisualProcessor::process() {
|
||||
|
||||
distribute(output);
|
||||
}
|
||||
|
||||
|
||||
iqData->decRefCount();
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,8 @@ public:
|
||||
void setBandwidth(long bandwidth_in);
|
||||
long getBandwidth();
|
||||
|
||||
int getDesiredInputSize();
|
||||
|
||||
void setup(int fftSize);
|
||||
|
||||
protected:
|
||||
@ -59,4 +61,39 @@ private:
|
||||
|
||||
std::vector<liquid_float_complex> shiftBuffer;
|
||||
std::vector<liquid_float_complex> resampleBuffer;
|
||||
int desiredInputSize;
|
||||
};
|
||||
|
||||
|
||||
class FFTDataDistributor : public VisualProcessor<DemodulatorThreadIQData, DemodulatorThreadIQData> {
|
||||
public:
|
||||
void setFFTSize(int fftSize) {
|
||||
this->fftSize = fftSize;
|
||||
}
|
||||
|
||||
protected:
|
||||
void process() {
|
||||
while (!input->empty()) {
|
||||
if (!isAnyOutputEmpty()) {
|
||||
return;
|
||||
}
|
||||
DemodulatorThreadIQData *inp;
|
||||
input->pop(inp);
|
||||
if (inp) {
|
||||
if (inp->data.size() >= fftSize) {
|
||||
for (int i = 0, iMax = inp->data.size()-fftSize; i < iMax; i += fftSize) {
|
||||
DemodulatorThreadIQData *outp = outputBuffers.getBuffer();
|
||||
outp->frequency = inp->frequency;
|
||||
outp->sampleRate = inp->sampleRate;
|
||||
outp->data.assign(inp->data.begin()+i,inp->data.begin()+i+fftSize);
|
||||
distribute(outp);
|
||||
}
|
||||
}
|
||||
inp->decRefCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReBuffer<DemodulatorThreadIQData> outputBuffers;
|
||||
int fftSize;
|
||||
};
|
||||
|
@ -11,6 +11,28 @@ public:
|
||||
virtual ~VisualProcessor() {
|
||||
|
||||
}
|
||||
|
||||
bool isInputEmpty() {
|
||||
return input->empty();
|
||||
}
|
||||
|
||||
bool isOutputEmpty() {
|
||||
for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
|
||||
if ((*outputs_i)->full()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isAnyOutputEmpty() {
|
||||
for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
|
||||
if (!(*outputs_i)->full()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setInput(ThreadQueue<InputDataType *> *vis_in) {
|
||||
busy_update.lock();
|
||||
@ -60,24 +82,6 @@ protected:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isOutputEmpty() {
|
||||
for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
|
||||
if (!(*outputs_i)->empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isAnyOutputEmpty() {
|
||||
for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
|
||||
if ((*outputs_i)->empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ThreadQueue<InputDataType *> *input;
|
||||
std::vector<ThreadQueue<OutputDataType *> *> outputs;
|
||||
@ -90,10 +94,10 @@ template<class OutputDataType = ReferenceCounter>
|
||||
class VisualDataDistributor : public VisualProcessor<OutputDataType, OutputDataType> {
|
||||
protected:
|
||||
void process() {
|
||||
if (!VisualProcessor<OutputDataType, OutputDataType>::isOutputEmpty()) {
|
||||
return;
|
||||
}
|
||||
while (!VisualProcessor<OutputDataType, OutputDataType>::input->empty()) {
|
||||
if (!VisualProcessor<OutputDataType, OutputDataType>::isAnyOutputEmpty()) {
|
||||
return;
|
||||
}
|
||||
OutputDataType *inp;
|
||||
VisualProcessor<OutputDataType, OutputDataType>::input->pop(inp);
|
||||
if (inp) {
|
||||
|
@ -82,18 +82,18 @@ void SDRPostThread::run() {
|
||||
|
||||
iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput");
|
||||
iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput");
|
||||
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOut");
|
||||
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput");
|
||||
|
||||
ReBuffer<DemodulatorThreadIQData> buffers;
|
||||
std::vector<liquid_float_complex> fpData;
|
||||
std::vector<liquid_float_complex> dataOut;
|
||||
|
||||
|
||||
while (!terminated) {
|
||||
SDRThreadIQData *data_in;
|
||||
|
||||
|
||||
iqDataInQueue->pop(data_in);
|
||||
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
||||
|
||||
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
||||
|
||||
if (data_in && data_in->data.size()) {
|
||||
int dataSize = data_in->data.size()/2;
|
||||
if (dataSize > fpData.capacity()) {
|
||||
@ -104,7 +104,7 @@ void SDRPostThread::run() {
|
||||
fpData.resize(dataSize);
|
||||
dataOut.resize(dataSize);
|
||||
}
|
||||
|
||||
|
||||
if (swapIQ) {
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
fpData[i] = _lut_swap[*((uint16_t*)&data_in->data[2*i])];
|
||||
@ -114,106 +114,101 @@ void SDRPostThread::run() {
|
||||
fpData[i] = _lut[*((uint16_t*)&data_in->data[2*i])];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
iirfilt_crcf_execute_block(dcFilter, &fpData[0], dataSize, &dataOut[0]);
|
||||
|
||||
if (iqDataOutQueue != NULL) {
|
||||
DemodulatorThreadIQData *pipeDataOut = new DemodulatorThreadIQData;
|
||||
|
||||
pipeDataOut->frequency = data_in->frequency;
|
||||
pipeDataOut->sampleRate = data_in->sampleRate;
|
||||
pipeDataOut->data.assign(dataOut.begin(), dataOut.end());
|
||||
iqDataOutQueue->push(pipeDataOut);
|
||||
}
|
||||
|
||||
|
||||
if (iqVisualQueue != NULL && iqVisualQueue->empty()) {
|
||||
|
||||
|
||||
visualDataOut->busy_rw.lock();
|
||||
|
||||
|
||||
if (visualDataOut->data.size() < num_vis_samples) {
|
||||
if (visualDataOut->data.capacity() < num_vis_samples) {
|
||||
visualDataOut->data.reserve(num_vis_samples);
|
||||
}
|
||||
visualDataOut->data.resize(num_vis_samples);
|
||||
}
|
||||
|
||||
|
||||
visualDataOut->frequency = data_in->frequency;
|
||||
visualDataOut->sampleRate = data_in->sampleRate;
|
||||
visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + num_vis_samples);
|
||||
|
||||
|
||||
iqVisualQueue->push(visualDataOut);
|
||||
|
||||
|
||||
visualDataOut->busy_rw.unlock();
|
||||
}
|
||||
|
||||
busy_demod.lock();
|
||||
|
||||
|
||||
int activeDemods = 0;
|
||||
bool pushedData = false;
|
||||
|
||||
if (demodulators.size()) {
|
||||
|
||||
std::vector<DemodulatorInstance *>::iterator i;
|
||||
for (i = demodulators.begin(); i != demodulators.end(); i++) {
|
||||
DemodulatorInstance *demod = *i;
|
||||
|
||||
if (demodulators.size() || iqDataOutQueue != NULL) {
|
||||
std::vector<DemodulatorInstance *>::iterator demod_i;
|
||||
for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
|
||||
DemodulatorInstance *demod = *demod_i;
|
||||
if (demod->getFrequency() != data_in->frequency
|
||||
&& abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
||||
&& abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
||||
continue;
|
||||
}
|
||||
activeDemods++;
|
||||
}
|
||||
|
||||
if (demodulators.size()) {
|
||||
|
||||
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
||||
|
||||
// std::lock_guard < std::mutex > lock(demodDataOut->m_mutex);
|
||||
demodDataOut->frequency = data_in->frequency;
|
||||
demodDataOut->sampleRate = data_in->sampleRate;
|
||||
demodDataOut->setRefCount(activeDemods);
|
||||
demodDataOut->data.assign(dataOut.begin(), dataOut.end());
|
||||
|
||||
std::vector<DemodulatorInstance *>::iterator i;
|
||||
for (i = demodulators.begin(); i != demodulators.end(); i++) {
|
||||
DemodulatorInstance *demod = *i;
|
||||
DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe();
|
||||
|
||||
if (abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
||||
if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) {
|
||||
demod->setActive(false);
|
||||
DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData;
|
||||
dummyDataOut->frequency = data_in->frequency;
|
||||
dummyDataOut->sampleRate = data_in->sampleRate;
|
||||
demodQueue->push(dummyDataOut);
|
||||
}
|
||||
|
||||
if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) {
|
||||
wxGetApp().setFrequency(demod->getFrequency());
|
||||
}
|
||||
} else if (!demod->isActive()) {
|
||||
demod->setActive(true);
|
||||
if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) {
|
||||
wxGetApp().getDemodMgr().setActiveDemodulator(demod);
|
||||
}
|
||||
|
||||
if (iqDataOutQueue != NULL) {
|
||||
activeDemods++;
|
||||
}
|
||||
|
||||
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
||||
|
||||
// std::lock_guard < std::mutex > lock(demodDataOut->m_mutex);
|
||||
demodDataOut->frequency = data_in->frequency;
|
||||
demodDataOut->sampleRate = data_in->sampleRate;
|
||||
demodDataOut->setRefCount(activeDemods);
|
||||
demodDataOut->data.assign(dataOut.begin(), dataOut.end());
|
||||
|
||||
for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
|
||||
DemodulatorInstance *demod = *demod_i;
|
||||
DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe();
|
||||
|
||||
if (abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
||||
if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) {
|
||||
demod->setActive(false);
|
||||
DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData;
|
||||
dummyDataOut->frequency = data_in->frequency;
|
||||
dummyDataOut->sampleRate = data_in->sampleRate;
|
||||
demodQueue->push(dummyDataOut);
|
||||
}
|
||||
|
||||
if (!demod->isActive()) {
|
||||
continue;
|
||||
|
||||
if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) {
|
||||
wxGetApp().setFrequency(demod->getFrequency());
|
||||
}
|
||||
if (demod->isFollow()) {
|
||||
demod->setFollow(false);
|
||||
} else if (!demod->isActive()) {
|
||||
demod->setActive(true);
|
||||
if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) {
|
||||
wxGetApp().getDemodMgr().setActiveDemodulator(demod);
|
||||
}
|
||||
|
||||
demodQueue->push(demodDataOut);
|
||||
pushedData = true;
|
||||
}
|
||||
|
||||
if (!pushedData) {
|
||||
demodDataOut->setRefCount(0);
|
||||
|
||||
if (!demod->isActive()) {
|
||||
continue;
|
||||
}
|
||||
if (demod->isFollow()) {
|
||||
demod->setFollow(false);
|
||||
}
|
||||
|
||||
demodQueue->push(demodDataOut);
|
||||
pushedData = true;
|
||||
}
|
||||
|
||||
if (iqDataOutQueue != NULL) {
|
||||
iqDataOutQueue->push(demodDataOut);
|
||||
pushedData = true;
|
||||
}
|
||||
|
||||
if (!pushedData && iqDataOutQueue == NULL) {
|
||||
demodDataOut->setRefCount(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
busy_demod.unlock();
|
||||
}
|
||||
data_in->decRefCount();
|
||||
|
@ -140,7 +140,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
initGLExtensions();
|
||||
glViewport(0, 0, ClientSize.x, ClientSize.y);
|
||||
|
||||
if (!visualDataQueue.empty()) {
|
||||
while (!visualDataQueue.empty()) {
|
||||
SpectrumVisualData *vData;
|
||||
|
||||
visualDataQueue.pop(vData);
|
||||
|
Loading…
Reference in New Issue
Block a user