From 7d6a387a77983aa32349f0393d1177c1a9c72df3 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Tue, 29 Dec 2015 20:52:49 -0500 Subject: [PATCH] Updates and improvements for low-bandwidth input usage --- src/AppFrame.cpp | 6 + src/AppFrame.h | 3 +- src/CubicSDR.cpp | 10 + src/audio/AudioThread.cpp | 2 +- src/panel/WaterfallPanel.cpp | 24 +- src/process/SpectrumVisualProcessor.cpp | 105 +++--- src/process/SpectrumVisualProcessor.h | 4 +- src/sdr/SDRPostThread.cpp | 426 +++++++++++++++--------- src/sdr/SDRPostThread.h | 3 + src/sdr/SoapySDRThread.cpp | 4 + src/visual/SpectrumCanvas.cpp | 3 + src/visual/SpectrumCanvas.h | 1 + src/visual/WaterfallCanvas.cpp | 23 +- src/visual/WaterfallCanvas.h | 4 +- 14 files changed, 393 insertions(+), 225 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index c7ded84..b0fecb2 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -1322,3 +1322,9 @@ void AppFrame::updateModemProperties(ModemArgInfoList args) { modemPropertiesUpdated.store(true); } +void AppFrame::setMainWaterfallFFTSize(int fftSize) { + wxGetApp().getSpectrumProcessor()->setFFTSize(fftSize); + spectrumCanvas->setFFTSize(fftSize); + waterfallDataThread->getProcessor()->setFFTSize(fftSize); + waterfallCanvas->setFFTSize(fftSize); +} diff --git a/src/AppFrame.h b/src/AppFrame.h index 454b5e4..fc4b9ed 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -69,7 +69,8 @@ public: FFTVisualDataThread *getWaterfallDataThread(); void updateModemProperties(ModemArgInfoList args); - + void setMainWaterfallFFTSize(int fftSize); + private: void OnMenu(wxCommandEvent& event); void OnClose(wxCloseEvent& event); diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 641e581..80d4420 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -441,6 +441,16 @@ void CubicSDR::setSampleRate(long long rate_in) { sampleRate = rate_in; sdrThread->setSampleRate(sampleRate); setFrequency(frequency); + + if (rate_in <= CHANNELIZER_RATE_MAX) { + appframe->setMainWaterfallFFTSize(1024); + appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false); + spectrumVisualThread->getProcessor()->setHideDC(false); + } else if (rate_in > CHANNELIZER_RATE_MAX) { + appframe->setMainWaterfallFFTSize(2048); + appframe->getWaterfallDataThread()->getProcessor()->setHideDC(true); + spectrumVisualThread->getProcessor()->setHideDC(true); + } } void CubicSDR::setDevice(SDRDeviceInfo *dev) { diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index c330a9d..77c5047 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -208,7 +208,7 @@ void AudioThread::enumerateDevices(std::vector &devs) { std::cout << "Audio Device #" << i << " " << info.name << std::endl; std::cout << "\tDefault Output? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl; - std::cout << "\tDefault Input? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl; + std::cout << "\tDefault Input? " << (info.isDefaultInput ? "Yes" : "No") << std::endl; std::cout << "\tInput channels: " << info.inputChannels << std::endl; std::cout << "\tOutput channels: " << info.outputChannels << std::endl; std::cout << "\tDuplex channels: " << info.duplexChannels << std::endl; diff --git a/src/panel/WaterfallPanel.cpp b/src/panel/WaterfallPanel.cpp index 71d8f06..f9383d3 100644 --- a/src/panel/WaterfallPanel.cpp +++ b/src/panel/WaterfallPanel.cpp @@ -16,14 +16,6 @@ void WaterfallPanel::setup(int fft_size_in, int num_waterfall_lines_in) { points.resize(fft_size); } - for (int i = 0; i < 2; i++) { - if (waterfall[i]) { - glDeleteTextures(1, &waterfall[i]); - waterfall[i] = 0; - } - - waterfall_ofs[i] = waterfall_lines - 1; - } texInitialized.store(false); bufferInitialized.store(false); } @@ -65,7 +57,7 @@ void WaterfallPanel::step() { return; } - if (points.size()) { + if (points.size() && points.size() == fft_size) { for (int j = 0; j < 2; j++) { for (int i = 0, iMax = half_fft_size; i < iMax; i++) { float v = points[j * half_fft_size + i]; @@ -94,6 +86,15 @@ void WaterfallPanel::update() { } if (!texInitialized.load()) { + for (int i = 0; i < 2; i++) { + if (waterfall[i]) { + glDeleteTextures(1, &waterfall[i]); + waterfall[i] = 0; + } + + waterfall_ofs[i] = waterfall_lines - 1; + } + glGenTextures(2, waterfall); unsigned char *waterfall_tex; @@ -110,12 +111,13 @@ void WaterfallPanel::update() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, waterfall[i]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, half_fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); } delete[] waterfall_tex; - + + refreshTheme(); + texInitialized.store(true); } diff --git a/src/process/SpectrumVisualProcessor.cpp b/src/process/SpectrumVisualProcessor.cpp index e0dcd9b..a48e988 100644 --- a/src/process/SpectrumVisualProcessor.cpp +++ b/src/process/SpectrumVisualProcessor.cpp @@ -18,6 +18,8 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last desiredInputSize.store(0); fft_average_rate = 0.65; scaleFactor.store(1.0); + fftSizeChanged.store(false); + newFFTSize.store(0); lastView = false; } @@ -83,24 +85,34 @@ void SpectrumVisualProcessor::setup(int fftSize_in) { fftSize = fftSize_in; fftSizeInternal = fftSize_in * SPECTRUM_VZM; - desiredInputSize.store(fftSizeInternal); + lastDataSize = 0; + + int memSize = sizeof(fftwf_complex) * fftSizeInternal; if (fftwInput) { free(fftwInput); } - fftwInput = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSizeInternal); + fftwInput = (fftwf_complex*) fftwf_malloc(memSize); + memset(fftwInput,0,memSize); + if (fftInData) { free(fftInData); } - fftInData = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSizeInternal); + fftInData = (fftwf_complex*) fftwf_malloc(memSize); + memset(fftwInput,0,memSize); + if (fftLastData) { free(fftLastData); } - fftLastData = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSizeInternal); + fftLastData = (fftwf_complex*) fftwf_malloc(memSize); + memset(fftwInput,0,memSize); + if (fftwOutput) { free(fftwOutput); } - fftwOutput = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSizeInternal); + fftwOutput = (fftwf_complex*) fftwf_malloc(memSize); + memset(fftwInput,0,memSize); + if (fftw_plan) { fftwf_destroy_plan(fftw_plan); } @@ -108,6 +120,14 @@ void SpectrumVisualProcessor::setup(int fftSize_in) { busy_run.unlock(); } +void SpectrumVisualProcessor::setFFTSize(int fftSize_in) { + if (fftSize_in == fftSize) { + return; + } + newFFTSize = fftSize_in; + fftSizeChanged.store(true); +} + void SpectrumVisualProcessor::setHideDC(bool hideDC) { this->hideDC.store(hideDC); } @@ -121,6 +141,11 @@ void SpectrumVisualProcessor::process() { return; } + if (fftSizeChanged.load()) { + setup(newFFTSize); + fftSizeChanged.store(false); + } + DemodulatorThreadIQData *iqData; input->pop(iqData); @@ -135,24 +160,11 @@ void SpectrumVisualProcessor::process() { std::vector *data = &iqData->data; if (data && data->size()) { - SpectrumVisualData *output = outputBuffers.getBuffer(); - - if (output->spectrum_points.size() < fftSize * 2) { - output->spectrum_points.resize(fftSize * 2); - } - unsigned int num_written; long resampleBw = iqData->sampleRate; bool newResampler = false; int bwDiff; -// if (bandwidth > resampleBw) { -// iqData->decRefCount(); -// iqData->busy_rw.unlock(); -// busy_run.unlock(); -// return; -// } - if (is_view.load()) { if (!iqData->frequency || !iqData->sampleRate) { iqData->decRefCount(); @@ -161,7 +173,6 @@ void SpectrumVisualProcessor::process() { return; } -// resamplerRatio = (double) (bandwidth) / (double) iqData->sampleRate; while (resampleBw / SPECTRUM_VZM >= bandwidth) { resampleBw /= SPECTRUM_VZM; } @@ -219,7 +230,7 @@ void SpectrumVisualProcessor::process() { nco_crcf_mix_block_down(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size); } } else { - shiftBuffer.assign(iqData->data.begin(), iqData->data.end()); + shiftBuffer.assign(iqData->data.begin(), iqData->data.begin()+desired_input_size); } if (!resampler || resampleBw != lastBandwidth || lastInputBandwidth != iqData->sampleRate) { @@ -247,11 +258,8 @@ void SpectrumVisualProcessor::process() { resampleBuffer.resize(out_size); } - msresamp_crcf_execute(resampler, &shiftBuffer[0], desired_input_size, &resampleBuffer[0], &num_written); - resampleBuffer.resize(fftSizeInternal); - if (num_written < fftSizeInternal) { for (int i = 0; i < num_written; i++) { fftInData[i][0] = resampleBuffer[i].real; @@ -268,6 +276,8 @@ void SpectrumVisualProcessor::process() { } } } else { + this->desiredInputSize.store(fftSizeInternal); + num_written = data->size(); if (data->size() < fftSizeInternal) { for (int i = 0, iMax = data->size(); i < iMax; i++) { @@ -311,11 +321,22 @@ void SpectrumVisualProcessor::process() { } if (execute) { + SpectrumVisualData *output = outputBuffers.getBuffer(); + + if (output->spectrum_points.size() != fftSize * 2) { + output->spectrum_points.resize(fftSize * 2); + } + fftwf_execute(fftw_plan); float fft_ceil = 0, fft_floor = 1; - if (fft_result.size() < fftSizeInternal) { + if (fft_result.size() != fftSizeInternal) { + if (fft_result.capacity() < fftSizeInternal) { + fft_result.reserve(fftSizeInternal); + fft_result_ma.reserve(fftSizeInternal); + fft_result_maa.reserve(fftSizeInternal); + } fft_result.resize(fftSizeInternal); fft_result_ma.resize(fftSizeInternal); fft_result_maa.resize(fftSizeInternal); @@ -376,30 +397,31 @@ void SpectrumVisualProcessor::process() { } for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) { + if (fft_result_maa[i] != fft_result_maa[i]) fft_result_maa[i] = fft_result[i]; fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; + if (fft_result_ma[i] != fft_result_ma[i]) fft_result_ma[i] = fft_result[i]; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; - if (fft_result_maa[i] > fft_ceil) { + if (fft_result_maa[i] > fft_ceil || fft_ceil != fft_ceil) { fft_ceil = fft_result_maa[i]; } - if (fft_result_maa[i] < fft_floor) { + if (fft_result_maa[i] < fft_floor || fft_floor != fft_floor) { fft_floor = fft_result_maa[i]; } } + if (fft_ceil_ma != fft_ceil_ma) fft_ceil_ma = fft_ceil; fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; + if (fft_ceil_maa != fft_ceil_maa) fft_ceil_maa = fft_ceil; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; + if (fft_floor_ma != fft_floor_ma) fft_floor_ma = fft_floor; fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; + if (fft_floor_maa != fft_floor_maa) fft_floor_maa = fft_floor; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; float sf = scaleFactor.load(); - -// for (int i = 0, iMax = fftSize; i < iMax; i++) { -// float v = (log10(fft_result_maa[i*SPECTRUM_VZM]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))); -// output->spectrum_points[i * 2] = ((float) i / (float) iMax); -// output->spectrum_points[i * 2 + 1] = v*sf; -// } + double visualRatio = (double(bandwidth) / double(resampleBw)); double visualStart = (double(fftSizeInternal) / 2.0) - (double(fftSizeInternal) * (visualRatio / 2.0)); double visualAccum = 0; @@ -408,13 +430,6 @@ void SpectrumVisualProcessor::process() { for (int x = 0, xMax = output->spectrum_points.size() / 2; x < xMax; x++) { visualAccum += visualRatio * double(SPECTRUM_VZM); -// while (visualAccum >= 1.0) { -// visualAccum -= 1.0; -// i++; -// } -// acc = (log10(fft_result_maa[visualStart+i]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))); -// output->spectrum_points[x * 2] = (float(x) / float(xMax)); -// output->spectrum_points[x * 2 + 1] = acc*sf; while (visualAccum >= 1.0) { int idx = round(visualStart+i); @@ -429,8 +444,9 @@ void SpectrumVisualProcessor::process() { visualAccum -= 1.0; i++; } + + output->spectrum_points[x * 2] = ((float) x / (float) xMax); if (accCount) { - output->spectrum_points[x * 2] = ((float) x / (float) xMax); output->spectrum_points[x * 2 + 1] = ((log10((acc/accCount)+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))))*sf; acc = 0.0; accCount = 0.0; @@ -475,11 +491,12 @@ void SpectrumVisualProcessor::process() { output->fft_ceiling = fft_ceil_maa/sf; output->fft_floor = fft_floor_maa; + + output->centerFreq = centerFreq; + output->bandwidth = bandwidth; + + distribute(output); } - - output->centerFreq = centerFreq; - output->bandwidth = bandwidth; - distribute(output); } iqData->decRefCount(); diff --git a/src/process/SpectrumVisualProcessor.h b/src/process/SpectrumVisualProcessor.h index b080689..e900bea 100644 --- a/src/process/SpectrumVisualProcessor.h +++ b/src/process/SpectrumVisualProcessor.h @@ -38,6 +38,7 @@ public: int getDesiredInputSize(); void setup(int fftSize); + void setFFTSize(int fftSize); void setHideDC(bool hideDC); void setScaleFactor(float sf); @@ -48,7 +49,7 @@ protected: ReBuffer outputBuffers; std::atomic_bool is_view; - std::atomic_int fftSize; + std::atomic_int fftSize, newFFTSize; std::atomic_int fftSizeInternal; std::atomic_llong centerFreq; std::atomic_long bandwidth; @@ -82,4 +83,5 @@ private: std::mutex busy_run; std::atomic_bool hideDC; std::atomic scaleFactor; + std::atomic_bool fftSizeChanged; }; diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 1b1a46e..b1b0d10 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -156,186 +156,47 @@ void SDRPostThread::run() { iqActiveDemodVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQActiveDemodVisualDataOutput"); iqDataInQueue->set_max_num_items(0); - - std::vector dcBuf; while (!terminated) { SDRThreadIQData *data_in; iqDataInQueue->pop(data_in); // std::lock_guard < std::mutex > lock(data_in->m_mutex); - - if (data_in && data_in->data.size() && data_in->numChannels) { - if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) { - numChannels = data_in->numChannels; - sampleRate = data_in->sampleRate; - initPFBChannelizer(); - doRefresh.store(true); - } - - int dataSize = data_in->data.size(); - int outSize = data_in->data.size(); - if (outSize > dataOut.capacity()) { - dataOut.reserve(outSize); - } - if (outSize != dataOut.size()) { - dataOut.resize(outSize); - } - int activeVisChannel = -1; - - if (iqDataOutQueue != NULL && !iqDataOutQueue->full() && activeVisChannel < 0) { - DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer(); - - bool doVis = false; - - if (iqVisualQueue != NULL && !iqVisualQueue->full()) { - doVis = true; - } - - iqDataOut->setRefCount(1 + (doVis?1:0)); - - iqDataOut->frequency = data_in->frequency; - iqDataOut->sampleRate = data_in->sampleRate; - iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize); + busy_demod.lock(); - iqDataOutQueue->push(iqDataOut); - if (doVis) { - iqVisualQueue->push(iqDataOut); - } + if (data_in && data_in->data.size()) { + if(data_in->numChannels > 1) { + runPFBCH(data_in); + } else { + runSingleCH(data_in); } - - busy_demod.lock(); - - if (frequency != data_in->frequency) { - frequency = data_in->frequency; - doRefresh.store(true); - } - - if (doRefresh.load()) { - updateActiveDemodulators(); - updateChannels(); - doRefresh.store(false); - } - - DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); - int activeDemodChannel = -1; - - // Find active demodulators - if (nRunDemods || (activeVisChannel >= 0)) { - - // channelize data - // firpfbch output rate is (input rate / channels) - for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) { - firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]); - } - - for (int i = 0, iMax = numChannels+1; i < iMax; i++) { - demodChannelActive[i] = 0; - } - - // Find nearest channel for each demodulator - for (int i = 0; i < nRunDemods; i++) { - DemodulatorInstance *demod = runDemods[i]; - demodChannel[i] = getChannelAt(demod->getFrequency()); - if (demod == activeDemod) { - activeDemodChannel = demodChannel[i]; - } - } - - for (int i = 0; i < nRunDemods; i++) { - // cache channel usage refcounts - if (demodChannel[i] >= 0) { - demodChannelActive[demodChannel[i]]++; - } - } - - // Run channels - for (int i = 0; i < numChannels+1; i++) { - int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full())?1:0; - - if (!doDemodVis && demodChannelActive[i] == 0) { - continue; - } - - DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); - demodDataOut->setRefCount(demodChannelActive[i] + doDemodVis); - demodDataOut->frequency = chanCenters[i]; - demodDataOut->sampleRate = chanBw; - - // Calculate channel buffer size - int chanDataSize = (outSize/numChannels); - - if (demodDataOut->data.size() != chanDataSize) { - if (demodDataOut->data.capacity() < chanDataSize) { - demodDataOut->data.reserve(chanDataSize); - } - demodDataOut->data.resize(chanDataSize); - } - - int idx = i; - - // Extra channel wraps lower side band of lowest channel - // to fix frequency gap on upper side of spectrum - if (i == numChannels) { - idx = (numChannels/2); - } - - // prepare channel data buffer - if (i == 0) { // Channel 0 requires DC correction - if (dcBuf.size() != chanDataSize) { - dcBuf.resize(chanDataSize); - } - for (int j = 0; j < chanDataSize; j++) { - dcBuf[j] = dataOut[idx]; - idx += numChannels; - } - iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]); - } else { - for (int j = 0; j < chanDataSize; j++) { - demodDataOut->data[j] = dataOut[idx]; - idx += numChannels; - } - } - - if (doDemodVis) { - iqActiveDemodVisualQueue->push(demodDataOut); - } - - for (int j = 0; j < nRunDemods; j++) { - if (demodChannel[j] == i) { - DemodulatorInstance *demod = runDemods[j]; - demod->getIQInputDataPipe()->push(demodDataOut); - } - } - } - } - - bool doUpdate = false; - for (int j = 0; j < nRunDemods; j++) { - DemodulatorInstance *demod = runDemods[j]; - if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { - doUpdate = true; - } - } - - if (doUpdate) { - updateActiveDemodulators(); - } - - busy_demod.unlock(); } - data_in->decRefCount(); - } -// buffers.purge(); + data_in->decRefCount(); + + bool doUpdate = false; + for (int j = 0; j < nRunDemods; j++) { + DemodulatorInstance *demod = runDemods[j]; + if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { + doUpdate = true; + } + } + + if (doUpdate) { + updateActiveDemodulators(); + } + + busy_demod.unlock(); + } if (iqVisualQueue && !iqVisualQueue->empty()) { DemodulatorThreadIQData *visualDataDummy; iqVisualQueue->pop(visualDataDummy); } -// visualDataBuffers.purge(); + // buffers.purge(); + // visualDataBuffers.purge(); std::cout << "SDR post-processing thread done." << std::endl; } @@ -345,3 +206,242 @@ void SDRPostThread::terminate() { SDRThreadIQData *dummy = new SDRThreadIQData; iqDataInQueue->push(dummy); } + +void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) { + if (sampleRate != data_in->sampleRate) { + sampleRate = data_in->sampleRate; + doRefresh.store(true); + } + + int dataSize = data_in->data.size(); + int outSize = data_in->data.size(); + + if (outSize > dataOut.capacity()) { + dataOut.reserve(outSize); + } + if (outSize != dataOut.size()) { + dataOut.resize(outSize); + } + + if (frequency != data_in->frequency) { + frequency = data_in->frequency; + doRefresh.store(true); + } + + if (doRefresh.load()) { + updateActiveDemodulators(); + doRefresh.store(false); + } + +// if (iqDataOutQueue != NULL && !iqDataOutQueue->full()) { +// DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer(); +// +// bool doVis = false; +// +// if (iqVisualQueue != NULL && !iqVisualQueue->full()) { +// doVis = true; +// } +// +// iqDataOut->setRefCount(1 + (doVis?1:0)); +// +// iqDataOut->frequency = data_in->frequency; +// iqDataOut->sampleRate = data_in->sampleRate; +// iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize); +// +// iqDataOutQueue->push(iqDataOut); +// if (doVis) { +// iqVisualQueue->push(iqDataOut); +// } +// } + + int refCount = nRunDemods; + bool doIQDataOut = (iqDataOutQueue != NULL && !iqDataOutQueue->full()); + bool doDemodVisOut = (nRunDemods && iqActiveDemodVisualQueue != NULL && !iqActiveDemodVisualQueue->full()); + bool doVisOut = (iqVisualQueue != NULL && !iqVisualQueue->full()); + + if (doIQDataOut) { + refCount++; + } + if (doDemodVisOut) { + refCount++; + } + if (doVisOut) { + refCount++; + } + + if (refCount) { + DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); + demodDataOut->setRefCount(refCount); + demodDataOut->frequency = frequency; + demodDataOut->sampleRate = sampleRate; + + if (demodDataOut->data.size() != dataSize) { + if (demodDataOut->data.capacity() < dataSize) { + demodDataOut->data.reserve(dataSize); + } + demodDataOut->data.resize(dataSize); + } + + iirfilt_crcf_execute_block(dcFilter, &data_in->data[0], dataSize, &demodDataOut->data[0]); + + if (doDemodVisOut) { + iqActiveDemodVisualQueue->push(demodDataOut); + } + + if (doIQDataOut) { + iqDataOutQueue->push(demodDataOut); + } + + if (doVisOut) { + iqVisualQueue->push(demodDataOut); + } + + for (int i = 0; i < nRunDemods; i++) { + runDemods[i]->getIQInputDataPipe()->push(demodDataOut); + } + } +} + +void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) { + if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) { + numChannels = data_in->numChannels; + sampleRate = data_in->sampleRate; + initPFBChannelizer(); + doRefresh.store(true); + } + + int dataSize = data_in->data.size(); + int outSize = data_in->data.size(); + + if (outSize > dataOut.capacity()) { + dataOut.reserve(outSize); + } + if (outSize != dataOut.size()) { + dataOut.resize(outSize); + } + + if (iqDataOutQueue != NULL && !iqDataOutQueue->full()) { + DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer(); + + bool doVis = false; + + if (iqVisualQueue != NULL && !iqVisualQueue->full()) { + doVis = true; + } + + iqDataOut->setRefCount(1 + (doVis?1:0)); + + iqDataOut->frequency = data_in->frequency; + iqDataOut->sampleRate = data_in->sampleRate; + iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize); + + iqDataOutQueue->push(iqDataOut); + if (doVis) { + iqVisualQueue->push(iqDataOut); + } + } + + if (frequency != data_in->frequency) { + frequency = data_in->frequency; + doRefresh.store(true); + } + + if (doRefresh.load()) { + updateActiveDemodulators(); + updateChannels(); + doRefresh.store(false); + } + + DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); + int activeDemodChannel = -1; + + // Find active demodulators + if (nRunDemods) { + + // channelize data + // firpfbch output rate is (input rate / channels) + for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) { + firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]); + } + + for (int i = 0, iMax = numChannels+1; i < iMax; i++) { + demodChannelActive[i] = 0; + } + + // Find nearest channel for each demodulator + for (int i = 0; i < nRunDemods; i++) { + DemodulatorInstance *demod = runDemods[i]; + demodChannel[i] = getChannelAt(demod->getFrequency()); + if (demod == activeDemod) { + activeDemodChannel = demodChannel[i]; + } + } + + for (int i = 0; i < nRunDemods; i++) { + // cache channel usage refcounts + if (demodChannel[i] >= 0) { + demodChannelActive[demodChannel[i]]++; + } + } + + // Run channels + for (int i = 0; i < numChannels+1; i++) { + int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full())?1:0; + + if (!doDemodVis && demodChannelActive[i] == 0) { + continue; + } + + DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); + demodDataOut->setRefCount(demodChannelActive[i] + doDemodVis); + demodDataOut->frequency = chanCenters[i]; + demodDataOut->sampleRate = chanBw; + + // Calculate channel buffer size + int chanDataSize = (outSize/numChannels); + + if (demodDataOut->data.size() != chanDataSize) { + if (demodDataOut->data.capacity() < chanDataSize) { + demodDataOut->data.reserve(chanDataSize); + } + demodDataOut->data.resize(chanDataSize); + } + + int idx = i; + + // Extra channel wraps lower side band of lowest channel + // to fix frequency gap on upper side of spectrum + if (i == numChannels) { + idx = (numChannels/2); + } + + // prepare channel data buffer + if (i == 0) { // Channel 0 requires DC correction + if (dcBuf.size() != chanDataSize) { + dcBuf.resize(chanDataSize); + } + for (int j = 0; j < chanDataSize; j++) { + dcBuf[j] = dataOut[idx]; + idx += numChannels; + } + iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]); + } else { + for (int j = 0; j < chanDataSize; j++) { + demodDataOut->data[j] = dataOut[idx]; + idx += numChannels; + } + } + + if (doDemodVis) { + iqActiveDemodVisualQueue->push(demodDataOut); + } + + for (int j = 0; j < nRunDemods; j++) { + if (demodChannel[j] == i) { + DemodulatorInstance *demod = runDemods[j]; + demod->getIQInputDataPipe()->push(demodDataOut); + } + } + } + } +} \ No newline at end of file diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index f89d6e1..af83bee 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -18,6 +18,8 @@ public: void run(); void terminate(); + void runSingleCH(SDRThreadIQData *data_in); + void runPFBCH(SDRThreadIQData *data_in); void setIQVisualRange(long long frequency, int bandwidth); protected: @@ -54,4 +56,5 @@ private: long long frequency; firpfbch_crcf channelizer; iirfilt_crcf dcFilter; + std::vector dcBuf; }; diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index 0fb0c63..0763ce3 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -333,6 +333,10 @@ int SDRThread::getOptimalElementCount(long long sampleRate, int fps) { } int SDRThread::getOptimalChannelCount(long long sampleRate) { + if (sampleRate <= CHANNELIZER_RATE_MAX) { + return 1; + } + int optimal_rate = CHANNELIZER_RATE_MAX; int optimal_count = int(ceil(double(sampleRate)/double(optimal_rate))); diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 53379de..50c9af9 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -169,6 +169,9 @@ void SpectrumCanvas::setScaleFactorEnabled(bool en) { scaleFactorEnabled = en; } +void SpectrumCanvas::setFFTSize(int fftSize) { + spectrumPanel.setFFTSize(fftSize); +} void SpectrumCanvas::updateScaleFactor(float factor) { SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor(); diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index e212565..bb566e0 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -26,6 +26,7 @@ public: void disableView(); void setScaleFactorEnabled(bool en); + void setFFTSize(int fftSize); SpectrumVisualDataQueue *getVisualDataQueue(); diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index da8302c..2e5bffa 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -33,7 +33,7 @@ EVT_MOUSEWHEEL(WaterfallCanvas::OnMouseWheelMoved) wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : - InteractiveCanvas(parent, attribList), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), waterfall_lines(0), + InteractiveCanvas(parent, attribList), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), new_fft_size(0), waterfall_lines(0), dragOfs(0), mouseZoom(1), zoom(1), freqMoving(false), freqMove(0.0), hoverAlpha(1.0) { glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); @@ -43,6 +43,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : SetCursor(wxCURSOR_CROSS); scaleMove = 0; minBandwidth = 30000; + fft_size_changed.store(false); } WaterfallCanvas::~WaterfallCanvas() { @@ -59,6 +60,14 @@ void WaterfallCanvas::setup(int fft_size_in, int waterfall_lines_in) { gTimer.start(); } +void WaterfallCanvas::setFFTSize(int fft_size_in) { + if (fft_size_in == fft_size) { + return; + } + new_fft_size = fft_size_in; + fft_size_changed.store(true); +} + WaterfallCanvas::DragState WaterfallCanvas::getDragState() { return dragState; } @@ -88,7 +97,9 @@ void WaterfallCanvas::processInputQueue() { visualDataQueue.pop(vData); if (vData) { - waterfallPanel.setPoints(vData->spectrum_points); + if (vData->spectrum_points.size() == fft_size * 2) { + waterfallPanel.setPoints(vData->spectrum_points); + } waterfallPanel.step(); vData->decRefCount(); updated = true; @@ -111,7 +122,7 @@ void WaterfallCanvas::processInputQueue() { void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { tex_update.lock(); wxPaintDC dc(this); - + const wxSize ClientSize = GetClientSize(); long double currentZoom = zoom; @@ -235,6 +246,12 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); + + if (fft_size_changed.load()) { + fft_size = new_fft_size; + waterfallPanel.setup(fft_size, waterfall_lines); + fft_size_changed.store(false); + } glContext->BeginDraw(0,0,0); diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 89333f0..39bbc75 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -20,6 +20,7 @@ public: WaterfallCanvas(wxWindow *parent, int *attribList = NULL); void setup(int fft_size_in, int waterfall_lines_in); + void setFFTSize(int fft_size_in); ~WaterfallCanvas(); DragState getDragState(); @@ -59,7 +60,7 @@ private: DragState dragState; DragState nextDragState; - int fft_size; + int fft_size, new_fft_size; int waterfall_lines; int dragOfs; @@ -77,6 +78,7 @@ private: bool preBuf; std::mutex tex_update; int minBandwidth; + std::atomic_bool fft_size_changed; // event table wxDECLARE_EVENT_TABLE(); };