From edd154296cef053070b96d9f5d3217bc3997d6aa Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 14 Oct 2015 00:54:48 -0400 Subject: [PATCH 01/13] FIR Polyphase filterbank channelizer prototype implementation - Can now handle several 200khz FM streams with SDRPlay at 8Mhz+ on my old 2010 Macbook Pro :) - Demod bandwidth max now limited to 400khz, temporary until alternate path for high-bandwidth is available --- src/CubicSDRDefs.h | 2 + src/demod/DemodulatorPreThread.cpp | 8 +- src/sdr/SDREnumerator.cpp | 2 +- src/sdr/SDRPostThread.cpp | 257 ++++++++++++++++++----------- src/sdr/SDRPostThread.h | 8 +- src/sdr/SoapySDRThread.cpp | 47 ++++-- src/sdr/SoapySDRThread.h | 8 +- src/visual/TuningCanvas.cpp | 4 +- src/visual/WaterfallCanvas.cpp | 4 +- 9 files changed, 214 insertions(+), 126 deletions(-) diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 49df3126..60c43132 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -34,3 +34,5 @@ const char filePathSeparator = #define DEFAULT_DEMOD_BW 200000 #define DEFAULT_WATERFALL_LPS 30 + +#define CHANNELIZER_RATE_MAX 400000 \ No newline at end of file diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index 5c3820f6..f96dc2c8 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -155,18 +155,20 @@ void DemodulatorPreThread::run() { } if (!initialized) { + inp->decRefCount(); continue; } // Requested frequency is not center, shift it into the center! if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) { shiftFrequency = params.frequency - inp->frequency; - if (abs(shiftFrequency) <= (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) { - nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) wxGetApp().getSampleRate()))); + if (abs(shiftFrequency) <= (int) ((double) (inp->sampleRate / 2) * 1.5)) { + nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) inp->sampleRate))); } } - if (abs(shiftFrequency) > (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) { + if (abs(shiftFrequency) > (int) ((double) (inp->sampleRate / 2) * 1.5)) { + inp->decRefCount(); continue; } diff --git a/src/sdr/SDREnumerator.cpp b/src/sdr/SDREnumerator.cpp index 9c369dbe..94dbe9d7 100644 --- a/src/sdr/SDREnumerator.cpp +++ b/src/sdr/SDREnumerator.cpp @@ -124,7 +124,7 @@ std::vector *SDREnumerator::enumerate_devices(std::string remot if (isRemote) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Querying remote " + remoteAddr + " device #" + std::to_string(i)); - deviceArgs["remote"] = remoteAddr; +// deviceArgs["remote"] = remoteAddr; if (deviceArgs.count("rtl") != 0) { streamArgs["remote:mtu"] = "8192"; streamArgs["remote:format"] = "CS8"; diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 7836c2b3..de0f94a1 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -7,24 +7,10 @@ SDRPostThread::SDRPostThread() : IOThread(), iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL){ - swapIQ.store(false); - - // create a lookup table - for (unsigned int i = 0; i <= 0xffff; i++) { - liquid_float_complex tmp,tmp_swap; -# if (__BYTE_ORDER == __LITTLE_ENDIAN) - tmp_swap.imag = tmp.real = (float(i & 0xff) - 127.4f) * (1.0f/128.0f); - tmp_swap.real = tmp.imag = (float(i >> 8) - 127.4f) * (1.0f/128.0f); - _lut.push_back(tmp); - _lut_swap.push_back(tmp_swap); -#else // BIG_ENDIAN - tmp_swap.imag = tmp.real = (float(i >> 8) - 127.4f) * (1.0f/128.0f); - tmp_swap.real = tmp.imag = (float(i & 0xff) - 127.4f) * (1.0f/128.0f); - _lut.push_back(tmp); - _lut_swap.push_back(tmp_swap); -#endif - } + numChannels = 0; + channelizer = NULL; + sampleRate = 0; } SDRPostThread::~SDRPostThread() { @@ -61,7 +47,7 @@ bool SDRPostThread::getSwapIQ() { void SDRPostThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread - int priority = sched_get_priority_max( SCHED_FIFO) - 1; + int priority = sched_get_priority_max( SCHED_FIFO); sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif @@ -79,6 +65,14 @@ void SDRPostThread::run() { std::vector dataOut; iqDataInQueue->set_max_num_items(0); + + std::vector chanCenters; + long long chanBw; + + int nRunDemods = 0; + std::vector runDemods; + std::vector demodChannel; + std::vector demodChannelActive; while (!terminated) { SDRThreadIQData *data_in; @@ -86,17 +80,36 @@ void SDRPostThread::run() { iqDataInQueue->pop(data_in); // 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 (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; + std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl; + if (channelizer) { + firpfbch2_crcf_destroy(channelizer); + } + channelizer = firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 1, 60); + + chanBw = (data_in->sampleRate / numChannels) * 2; + + chanCenters.resize(numChannels); + demodChannelActive.resize(numChannels); + + // firpfbch2 returns 2x sample rate per channel + // so, max demodulation without gaps is 1/2 chanBw ..? + std::cout << "Channel bandwidth spacing: " << (chanBw/2) << " actual bandwidth: " << chanBw << std::endl; + } + + int dataSize = data_in->data.size(); + int outSize = data_in->data.size()*2; - if (dataSize > dataOut.capacity()) { - dataOut.reserve(dataSize); + if (outSize > dataOut.capacity()) { + dataOut.reserve(outSize); } - if (dataSize != dataOut.size()) { - dataOut.resize(dataSize); + if (outSize != dataOut.size()) { + dataOut.resize(outSize); } - // if (swapIQ) { // for (int i = 0; i < dataSize; i++) { // fpData[i] = _lut_swap[*((uint16_t*)&data_in->data[2*i])]; @@ -107,80 +120,57 @@ void SDRPostThread::run() { // } // } + if (dataSize > fpData.capacity()) { + fpData.reserve(dataSize); + } + if (dataSize != fpData.size()) { + fpData.resize(dataSize); + } + if (data_in->dcCorrected) { - for (int i = 0; i < dataSize; i++) { - dataOut[i].real = data_in->data[i*2]; - dataOut[i].imag = data_in->data[i*2+1]; - } + fpData.assign(data_in->data.begin(), data_in->data.end()); } else { - if (dataSize > fpData.capacity()) { - fpData.reserve(dataSize); - } - if (dataSize != fpData.size()) { - fpData.resize(dataSize); - } - - for (int i = 0; i < dataSize; i++) { - fpData[i].real = data_in->data[i*2]; - fpData[i].imag = data_in->data[i*2+1]; - } - - iirfilt_crcf_execute_block(dcFilter, &fpData[0], dataSize, &dataOut[0]); + iirfilt_crcf_execute_block(dcFilter, &data_in->data[0], dataSize, &fpData[0]); } - if (iqVisualQueue != NULL && !iqVisualQueue->full()) { - DemodulatorThreadIQData *visualDataOut = visualDataBuffers.getBuffer(); - visualDataOut->setRefCount(1); + if (iqVisualQueue != NULL || iqDataOutQueue != NULL) { + int num_vis_samples = fpData.size(); + + bool doIQVis = iqVisualQueue && !iqVisualQueue->full(); + bool doIQOut = iqDataOutQueue != NULL; - int num_vis_samples = dataOut.size(); - -// 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); + DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer(); + iqDataOut->setRefCount((doIQVis?1:0) + (doIQOut?1:0)); - iqVisualQueue->push(visualDataOut); + iqDataOut->frequency = data_in->frequency; + iqDataOut->sampleRate = data_in->sampleRate; + iqDataOut->data.assign(fpData.begin(), fpData.begin() + num_vis_samples); + + if (doIQVis) { + iqVisualQueue->push(iqDataOut); + } + + if (doIQOut) { + iqDataOutQueue->push(iqDataOut); + } } - + busy_demod.lock(); - int activeDemods = 0; - bool pushedData = false; - - if (demodulators.size() || iqDataOutQueue != NULL) { + // Find active demodulators + if (demodulators.size()) { + // In range? std::vector::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)) { - continue; - } - activeDemods++; - } - - 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()); + + nRunDemods = 0; 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)) { + + // not in range? + if (abs(data_in->frequency - demod->getFrequency()) > (data_in->sampleRate / 2)) { + // deactivate if active if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) { demod->setActive(false); DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData; @@ -189,10 +179,12 @@ void SDRPostThread::run() { demodQueue->push(dummyDataOut); } + // follow if follow mode if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) { wxGetApp().setFrequency(demod->getFrequency()); + demod->setFollow(false); } - } else if (!demod->isActive()) { + } else if (!demod->isActive()) { // in range, activate if not activated demod->setActive(true); if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) { wxGetApp().getDemodMgr().setActiveDemodulator(demod); @@ -202,25 +194,92 @@ void SDRPostThread::run() { if (!demod->isActive()) { continue; } - if (demod->isFollow()) { - demod->setFollow(false); - } - demodQueue->push(demodDataOut); - pushedData = true; + // Add to the current run + if (nRunDemods == runDemods.size()) { + runDemods.push_back(demod); + demodChannel.push_back(-1); + } else { + runDemods[nRunDemods] = demod; + demodChannel[nRunDemods] = -1; + } + nRunDemods++; } - if (iqDataOutQueue != NULL) { - if (!iqDataOutQueue->full()) { - iqDataOutQueue->push(demodDataOut); - pushedData = true; - } else { - demodDataOut->decRefCount(); + // calculate channel center frequencies, todo: cache + for (int i = 0; i < numChannels/2; i++) { + int ofs = ((chanBw/2) * i); + chanCenters[i] = data_in->frequency + ofs; + chanCenters[i+(numChannels/2)] = data_in->frequency - (data_in->sampleRate/2) + ofs; + } + + // channelize data + // firpfbch2 output rate is 2 x ( input rate / channels ) + for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels/2) { + firpfbch2_crcf_execute(channelizer, &fpData[i], &dataOut[i * 2]); + } + + for (int i = 0, iMax = numChannels; i < iMax; i++) { + demodChannelActive[i] = 0; + } + + // Find nearest channel for each demodulator + for (int i = 0; i < nRunDemods; i++) { + DemodulatorInstance *demod = runDemods[i]; + long long minDelta = data_in->sampleRate; + for (int j = 0, jMax = numChannels; j < jMax; j++) { + // Distance from channel center to demod center + long long fdelta = abs(demod->getFrequency() - chanCenters[j]); + if (fdelta < minDelta) { + minDelta = fdelta; + demodChannel[i] = j; + } } } + + for (int i = 0; i < nRunDemods; i++) { + // cache channel usage refcounts + if (demodChannel[i] >= 0) { + demodChannelActive[demodChannel[i]]++; + } + } + - if (!pushedData && iqDataOutQueue == NULL) { - demodDataOut->setRefCount(0); + // Run channels + for (int i = 0; i < numChannels; i++) { + if (demodChannelActive[i] == 0) { + // Nothing using this channel, skip + continue; + } + + DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); + demodDataOut->setRefCount(demodChannelActive[i]); + 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); + } + + // prepare channel data buffer + for (int j = 0, idx = i; j < chanDataSize; j++) { + idx += numChannels; + demodDataOut->data[j] = dataOut[idx]; + } + + for (int j = 0; j < nRunDemods; j++) { + if (demodChannel[j] == i) { + DemodulatorInstance *demod = runDemods[j]; + demod->getIQInputDataPipe()->push(demodDataOut); +// std::cout << "Demodulator " << j << " in channel #" << i << " ctr: " << chanCenters[i] << " dataSize: " << chanDataSize << std::endl; + } + } } } diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index 6137df36..8a632f46 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -30,10 +30,8 @@ class SDRPostThread : public IOThread { std::vector demodulators; iirfilt_crcf dcFilter; std::atomic_bool swapIQ; - ReBuffer visualDataBuffers; - -private: - std::vector _lut; - std::vector _lut_swap; + ReBuffer visualDataBuffers; + int numChannels, sampleRate; + firpfbch2_crcf channelizer; }; diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index 61da248f..fa2d5c50 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -28,6 +28,7 @@ SDRThread::SDRThread() : IOThread() { hasPPM.store(false); hasHardwareDC.store(false); + numChannels.store(8); } SDRThread::~SDRThread() { @@ -85,7 +86,9 @@ void SDRThread::init() { device->setGainMode(SOAPY_SDR_RX,0,true); - numElems = getOptimalElementCount(sampleRate.load(), 60); + numChannels.store(getOptimalChannelCount(sampleRate.load())); + numElems.store(getOptimalElementCount(sampleRate.load(), 30)); + buffs[0] = malloc(numElems * 2 * sizeof(float)); } @@ -102,15 +105,15 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) { long long timeNs; SDRThreadIQData *dataOut = buffers.getBuffer(); - if (dataOut->data.size() != numElems * 2) { - dataOut->data.resize(numElems * 2); + if (dataOut->data.size() != numElems) { + dataOut->data.resize(numElems); } int n_read = 0; - while (n_read != numElems) { + while (n_read != numElems && !terminated) { int n_stream_read = device->readStream(stream, buffs, numElems-n_read, flags, timeNs); if (n_stream_read > 0) { - memcpy(&dataOut->data[n_read * 2], buffs[0], n_stream_read * sizeof(float) * 2); + memcpy(&dataOut->data[n_read], buffs[0], n_stream_read * sizeof(float) * 2); n_read += n_stream_read; } else { dataOut->data.resize(n_read); @@ -120,11 +123,12 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) { // std::cout << n_read << std::endl; - if (n_read > 0) { + if (n_read > 0 && !terminated) { dataOut->setRefCount(1); - dataOut->frequency = frequency; + dataOut->frequency = frequency.load(); dataOut->sampleRate = sampleRate.load(); - dataOut->dcCorrected = hasHardwareDC; + dataOut->dcCorrected = hasHardwareDC.load(); + dataOut->numChannels = numChannels.load(); iqDataOutQueue->push(dataOut); } @@ -148,6 +152,7 @@ void SDRThread::readLoop() { if (rate_changed.load()) { device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load()); sampleRate.store(device->getSampleRate(SOAPY_SDR_RX,0)); + numChannels.store(getOptimalChannelCount(sampleRate.load())); numElems.store(getOptimalElementCount(sampleRate.load(), 60)); free(buffs[0]); buffs[0] = malloc(numElems.load() * 2 * sizeof(float)); @@ -174,7 +179,7 @@ void SDRThread::readLoop() { void SDRThread::run() { //#ifdef __APPLE__ // pthread_t tID = pthread_self(); // ID of this thread -// int priority = sched_get_priority_max( SCHED_FIFO) - 1; +// int priority = sched_get_priority_max( SCHED_FIFO); // sched_param prio = { priority }; // scheduling priority of thread // pthread_setschedparam(tID, SCHED_FIFO, &prio); //#endif @@ -214,11 +219,31 @@ void SDRThread::setDevice(SDRDeviceInfo *dev) { int SDRThread::getOptimalElementCount(long long sampleRate, int fps) { int elemCount = (int)floor((double)sampleRate/(double)fps); - elemCount = int(ceil((double)elemCount/512.0)*512.0); - std::cout << "Calculated optimal element count of " << elemCount << std::endl; + int nch = numChannels.load(); + elemCount = int(ceil((double)elemCount/(double)nch))*nch; + std::cout << "Calculated optimal " << numChannels.load() << " channel element count of " << elemCount << std::endl; return elemCount; } +int SDRThread::getOptimalChannelCount(long long sampleRate) { + int optimal_rate = CHANNELIZER_RATE_MAX; + int optimal_count = int(ceil(double(sampleRate)/double(optimal_rate))); + + if (optimal_count % 2 == 1) { + optimal_count--; + } + + if (optimal_count < 4) { + optimal_count = 4; + } + + if (optimal_count > 16) { + optimal_count = 16; + } + return optimal_count; +} + + void SDRThread::setFrequency(long long freq) { if (freq < sampleRate.load() / 2) { freq = sampleRate.load() / 2; diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h index 542ce35e..53b34dea 100644 --- a/src/sdr/SoapySDRThread.h +++ b/src/sdr/SoapySDRThread.h @@ -18,10 +18,11 @@ class SDRThreadIQData: public ReferenceCounter { long long frequency; long long sampleRate; bool dcCorrected; - std::vector data; + int numChannels; + std::vector data; SDRThreadIQData() : - frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), dcCorrected(true) { + frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), dcCorrected(true), numChannels(0) { } @@ -54,6 +55,7 @@ class SDRThread : public IOThread { SDRDeviceInfo *getDevice(); void setDevice(SDRDeviceInfo *dev); int getOptimalElementCount(long long sampleRate, int fps); + int getOptimalChannelCount(long long sampleRate); void setFrequency(long long freq); long long getFrequency(); @@ -81,7 +83,7 @@ class SDRThread : public IOThread { std::atomic sampleRate; std::atomic_llong frequency, offset; - std::atomic_int ppm, direct_sampling_mode, numElems; + std::atomic_int ppm, direct_sampling_mode, numElems, numChannels; std::atomic_bool hasPPM, hasHardwareDC; std::atomic_bool rate_changed, freq_changed, offset_changed, diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp index a9fe015e..ffe2a361 100644 --- a/src/visual/TuningCanvas.cpp +++ b/src/visual/TuningCanvas.cpp @@ -190,8 +190,8 @@ void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) { bw += amount; } - if (bw > wxGetApp().getSampleRate()) { - bw = wxGetApp().getSampleRate(); + if (bw > CHANNELIZER_RATE_MAX) { + bw = CHANNELIZER_RATE_MAX; } wxGetApp().getDemodMgr().setLastBandwidth(bw); diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 91e18d6a..f64845ae 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -435,8 +435,8 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { int currentBW = demod->getBandwidth(); currentBW = currentBW + bwDiff; - if (currentBW > wxGetApp().getSampleRate()) { - currentBW = wxGetApp().getSampleRate(); + if (currentBW > CHANNELIZER_RATE_MAX) { + currentBW = CHANNELIZER_RATE_MAX; } if (currentBW < MIN_BANDWIDTH) { currentBW = MIN_BANDWIDTH; From d8c048fecc3fb0023678f8d69a46f370a7772e72 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 14 Oct 2015 18:09:29 -0400 Subject: [PATCH 02/13] SDRPostThread cleanup/refactor - Cache active demodulators and channel info instead of running the whole list each time --- src/sdr/SDRPostThread.cpp | 194 +++++++++++++++++++++----------------- src/sdr/SDRPostThread.h | 18 ++++ 2 files changed, 124 insertions(+), 88 deletions(-) diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index de0f94a1..fc2f8ae6 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -5,12 +5,21 @@ #include #include -SDRPostThread::SDRPostThread() : IOThread(), - iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL){ - swapIQ.store(false); +SDRPostThread::SDRPostThread() : IOThread() { + iqDataInQueue = NULL; + iqDataOutQueue = NULL; + iqVisualQueue = NULL; + + swapIQ.store(false); numChannels = 0; channelizer = NULL; + sampleRate = 0; + nRunDemods = 0; + + doRefresh.store(false); + + dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); } SDRPostThread::~SDRPostThread() { @@ -19,6 +28,7 @@ SDRPostThread::~SDRPostThread() { void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) { busy_demod.lock(); demodulators.push_back(demod); + doRefresh.store(true); busy_demod.unlock(); } @@ -32,6 +42,7 @@ void SDRPostThread::removeDemodulator(DemodulatorInstance *demod) { if (i != demodulators.end()) { demodulators.erase(i); + doRefresh.store(true); } busy_demod.unlock(); } @@ -44,6 +55,81 @@ bool SDRPostThread::getSwapIQ() { return this->swapIQ.load(); } +void SDRPostThread::initPFBChannelizer() { +// std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl; + if (channelizer) { + firpfbch2_crcf_destroy(channelizer); + } + channelizer = firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 1, 60); + + chanBw = (sampleRate / numChannels) * 2; + + chanCenters.resize(numChannels); + demodChannelActive.resize(numChannels); + + // firpfbch2 returns 2x sample rate per channel + // so, max demodulation without gaps is 1/2 chanBw ..? +// std::cout << "Channel bandwidth spacing: " << (chanBw/2) << " actual bandwidth: " << chanBw << std::endl; +} + +void SDRPostThread::updateActiveDemodulators() { + // In range? + std::vector::iterator demod_i; + + nRunDemods = 0; + + for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) { + DemodulatorInstance *demod = *demod_i; + DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe(); + + // not in range? + if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { + // deactivate if active + if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) { + demod->setActive(false); + DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData; + dummyDataOut->frequency = frequency; + dummyDataOut->sampleRate = sampleRate; + demodQueue->push(dummyDataOut); + } + + // follow if follow mode + if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) { + wxGetApp().setFrequency(demod->getFrequency()); + demod->setFollow(false); + } + } else if (!demod->isActive()) { // in range, activate if not activated + demod->setActive(true); + if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) { + wxGetApp().getDemodMgr().setActiveDemodulator(demod); + } + } + + if (!demod->isActive()) { + continue; + } + + // Add to the current run + if (nRunDemods == runDemods.size()) { + runDemods.push_back(demod); + demodChannel.push_back(-1); + } else { + runDemods[nRunDemods] = demod; + demodChannel[nRunDemods] = -1; + } + nRunDemods++; + } +} + +void SDRPostThread::updateChannels() { + // calculate channel center frequencies, todo: cache + for (int i = 0; i < numChannels/2; i++) { + int ofs = ((chanBw/2) * i); + chanCenters[i] = frequency + ofs; + chanCenters[i+(numChannels/2)] = frequency - (sampleRate/2) + ofs; + } +} + void SDRPostThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread @@ -52,27 +138,13 @@ void SDRPostThread::run() { pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif - dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); - std::cout << "SDR post-processing thread started.." << std::endl; iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput"); iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput"); iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput"); - - ReBuffer buffers; - std::vector fpData; - std::vector dataOut; - - iqDataInQueue->set_max_num_items(0); - std::vector chanCenters; - long long chanBw; - - int nRunDemods = 0; - std::vector runDemods; - std::vector demodChannel; - std::vector demodChannelActive; + iqDataInQueue->set_max_num_items(0); while (!terminated) { SDRThreadIQData *data_in; @@ -84,20 +156,8 @@ void SDRPostThread::run() { if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) { numChannels = data_in->numChannels; sampleRate = data_in->sampleRate; - std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl; - if (channelizer) { - firpfbch2_crcf_destroy(channelizer); - } - channelizer = firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 1, 60); - - chanBw = (data_in->sampleRate / numChannels) * 2; - - chanCenters.resize(numChannels); - demodChannelActive.resize(numChannels); - - // firpfbch2 returns 2x sample rate per channel - // so, max demodulation without gaps is 1/2 chanBw ..? - std::cout << "Channel bandwidth spacing: " << (chanBw/2) << " actual bandwidth: " << chanBw << std::endl; + initPFBChannelizer(); + doRefresh.store(true); } int dataSize = data_in->data.size(); @@ -157,62 +217,20 @@ void SDRPostThread::run() { busy_demod.lock(); - // Find active demodulators - if (demodulators.size()) { - // In range? - std::vector::iterator demod_i; + if (frequency != data_in->frequency) { + frequency = data_in->frequency; + doRefresh.store(true); + } - nRunDemods = 0; - - for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) { - DemodulatorInstance *demod = *demod_i; - DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe(); - - // not in range? - if (abs(data_in->frequency - demod->getFrequency()) > (data_in->sampleRate / 2)) { - // deactivate if active - 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); - } - - // follow if follow mode - if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) { - wxGetApp().setFrequency(demod->getFrequency()); - demod->setFollow(false); - } - } else if (!demod->isActive()) { // in range, activate if not activated - demod->setActive(true); - if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) { - wxGetApp().getDemodMgr().setActiveDemodulator(demod); - } - } - - if (!demod->isActive()) { - continue; - } - - // Add to the current run - if (nRunDemods == runDemods.size()) { - runDemods.push_back(demod); - demodChannel.push_back(-1); - } else { - runDemods[nRunDemods] = demod; - demodChannel[nRunDemods] = -1; - } - nRunDemods++; - } - - // calculate channel center frequencies, todo: cache - for (int i = 0; i < numChannels/2; i++) { - int ofs = ((chanBw/2) * i); - chanCenters[i] = data_in->frequency + ofs; - chanCenters[i+(numChannels/2)] = data_in->frequency - (data_in->sampleRate/2) + ofs; - } - + if (doRefresh.load()) { + updateActiveDemodulators(); + updateChannels(); + doRefresh.store(false); + } + + + // Find active demodulators + if (nRunDemods) { // channelize data // firpfbch2 output rate is 2 x ( input rate / channels ) for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels/2) { @@ -277,7 +295,7 @@ void SDRPostThread::run() { if (demodChannel[j] == i) { DemodulatorInstance *demod = runDemods[j]; demod->getIQInputDataPipe()->push(demodDataOut); -// std::cout << "Demodulator " << j << " in channel #" << i << " ctr: " << chanCenters[i] << " dataSize: " << chanDataSize << std::endl; +// std::cout << "Demodulator " << j << " in channel #" << i << " ctr: " << chanCenters[i] << " dataSize: " << chanDataSize << std::endl; } } } diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index 8a632f46..c638c90a 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -31,7 +31,25 @@ class SDRPostThread : public IOThread { iirfilt_crcf dcFilter; std::atomic_bool swapIQ; +private: + void initPFBChannelizer(); + void updateActiveDemodulators(); + void updateChannels(); + + ReBuffer buffers; + std::vector fpData; + std::vector dataOut; + std::vector chanCenters; + long long chanBw; + + int nRunDemods; + std::vector runDemods; + std::vector demodChannel; + std::vector demodChannelActive; + ReBuffer visualDataBuffers; + atomic_bool doRefresh; int numChannels, sampleRate; + long long frequency; firpfbch2_crcf channelizer; }; From 938d10366c02ec1d3e3655b67ce8784786b09113 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 14 Oct 2015 19:00:16 -0400 Subject: [PATCH 03/13] Update README.md --- README.md | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1c075474..5d5f2f90 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ Cross-Platform Software-Defined Radio Application Utilizes: -------- - - liquid-dsp (http://liquidsdr.org/ https://github.com/jgaeddert/liquid-dsp) - - FFTW (http://www.fftw.org/ https://github.com/FFTW/fftw3) - - RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ http://github.com/thestk/rtaudio/) - - Osmocom RTLSDR (http://sdr.osmocom.org/trac/wiki/rtl-sdr) + - liquid-dsp (http://liquidsdr.org/ -- https://github.com/jgaeddert/liquid-dsp) + - SoapySDR (http://www.pothosware.com/ -- https://github.com/pothosware/SoapySDR) + - FFTW (http://www.fftw.org/ -- https://github.com/FFTW/fftw3) + - RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ -- http://github.com/thestk/rtaudio/) - LodePNG (http://lodev.org/lodepng/) - - BMFont (http://www.angelcode.com/ http://www.angelcode.com/products/bmfont/) + - BMFont (http://www.angelcode.com/ -- http://www.angelcode.com/products/bmfont/) - Bitstream Vera font (http://en.wikipedia.org/wiki/Bitstream_Vera) - OpenGL (https://www.opengl.org/) - wxWidgets (https://www.wxwidgets.org/) @@ -20,9 +20,19 @@ Features and Status: -------------------- - Simple UI - Devices - - [x] RTL-SDR + - [x] SoapySDR Device support (known working checked) + - [x] SoapySDRPlay for SDRPlay (Maintained by C.J.) + - [x] SoapyRTLSDR for RTL-SDR (Maintained by C.J.) + - [x] SoapyHackRF for HackRF + - [x] SoapyBladeRF for BladeRF + - [ ] SoapyUHD for Ettus USRP + - [x] SoapyRemote, use any SoapySDR Device via network (works on Pi) + - [x] SoapyOsmo for GrOsmoSDR devices + - [ ] OsmoSDR + - [ ] MiriSDR + - [ ] RFSpace + - [x] AirSpy - [ ] rtl_tcp client - - [ ] gr-osmosdr - Basic Features - [x] Device Selection - [x] Bandwidth @@ -30,11 +40,14 @@ Features and Status: - [x] Load/Save session - [x] Audio sample rate - [x] Device PPM + - [x] Waterfall speed + - [x] Spectrum average speed + - [ ] Gain Controls + - [ ] Bookmarks + - [ ] History - [ ] Default preferences - [ ] Audio defaults - [x] Device defaults - - [ ] Bookmarks - - [ ] History - [ ] Run as rtl_tcp server and visualize control - Neat Visuals - [ ] 2D visuals @@ -68,8 +81,6 @@ Features and Status: - [x] Volume control - [x] Direct frequency input - [x] Mute - - [x] Waterfall speed - - [ ] RTL-SDR Gain - Basic Input Controls - [x] Drag spectrum to change center frequency - [x] Hold shift and click on waterfall to create a new demodulator From 249e04e69cb83537f1cd71a4bcbbfd9a6ae244a9 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Thu, 15 Oct 2015 01:35:03 -0400 Subject: [PATCH 04/13] Move DC blocking filter to SDRThread - DC blocker + Channelizer too heavy together --- src/sdr/SDRPostThread.cpp | 21 ++------------------- src/sdr/SDRPostThread.h | 1 - src/sdr/SoapySDRThread.cpp | 26 +++++++++++++++++--------- src/sdr/SoapySDRThread.h | 3 ++- 4 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index fc2f8ae6..237b7449 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -18,8 +18,6 @@ SDRPostThread::SDRPostThread() : IOThread() { nRunDemods = 0; doRefresh.store(false); - - dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); } SDRPostThread::~SDRPostThread() { @@ -179,23 +177,8 @@ void SDRPostThread::run() { // fpData[i] = _lut[*((uint16_t*)&data_in->data[2*i])]; // } // } - - if (dataSize > fpData.capacity()) { - fpData.reserve(dataSize); - } - if (dataSize != fpData.size()) { - fpData.resize(dataSize); - } - - if (data_in->dcCorrected) { - fpData.assign(data_in->data.begin(), data_in->data.end()); - } else { - iirfilt_crcf_execute_block(dcFilter, &data_in->data[0], dataSize, &fpData[0]); - } if (iqVisualQueue != NULL || iqDataOutQueue != NULL) { - int num_vis_samples = fpData.size(); - bool doIQVis = iqVisualQueue && !iqVisualQueue->full(); bool doIQOut = iqDataOutQueue != NULL; @@ -204,7 +187,7 @@ void SDRPostThread::run() { iqDataOut->frequency = data_in->frequency; iqDataOut->sampleRate = data_in->sampleRate; - iqDataOut->data.assign(fpData.begin(), fpData.begin() + num_vis_samples); + iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize); if (doIQVis) { iqVisualQueue->push(iqDataOut); @@ -234,7 +217,7 @@ void SDRPostThread::run() { // channelize data // firpfbch2 output rate is 2 x ( input rate / channels ) for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels/2) { - firpfbch2_crcf_execute(channelizer, &fpData[i], &dataOut[i * 2]); + firpfbch2_crcf_execute(channelizer, &data_in->data[i], &dataOut[i * 2]); } for (int i = 0, iMax = numChannels; i < iMax; i++) { diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index c638c90a..d42ea54c 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -28,7 +28,6 @@ class SDRPostThread : public IOThread { std::mutex busy_demod; std::vector demodulators; - iirfilt_crcf dcFilter; std::atomic_bool swapIQ; private: diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index fa2d5c50..9104351f 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -29,6 +29,8 @@ SDRThread::SDRThread() : IOThread() { hasPPM.store(false); hasHardwareDC.store(false); numChannels.store(8); + + dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); } SDRThread::~SDRThread() { @@ -88,7 +90,7 @@ void SDRThread::init() { numChannels.store(getOptimalChannelCount(sampleRate.load())); numElems.store(getOptimalElementCount(sampleRate.load(), 30)); - + inpBuffer.data.resize(numElems.load()); buffs[0] = malloc(numElems * 2 * sizeof(float)); } @@ -104,26 +106,31 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) { int flags; long long timeNs; - SDRThreadIQData *dataOut = buffers.getBuffer(); - if (dataOut->data.size() != numElems) { - dataOut->data.resize(numElems); - } int n_read = 0; while (n_read != numElems && !terminated) { int n_stream_read = device->readStream(stream, buffs, numElems-n_read, flags, timeNs); if (n_stream_read > 0) { - memcpy(&dataOut->data[n_read], buffs[0], n_stream_read * sizeof(float) * 2); + memcpy(&inpBuffer.data[n_read], buffs[0], n_stream_read * sizeof(float) * 2); n_read += n_stream_read; } else { - dataOut->data.resize(n_read); break; } } - // std::cout << n_read << std::endl; - if (n_read > 0 && !terminated) { + SDRThreadIQData *dataOut = buffers.getBuffer(); + + if (hasHardwareDC) { + dataOut->data.assign(inpBuffer.data.begin(), inpBuffer.data.begin()+n_read); + } else { + if (dataOut->data.size() != n_read) { + dataOut->data.resize(n_read); + } + iirfilt_crcf_execute_block(dcFilter, &inpBuffer.data[0], n_read, &dataOut->data[0]); + } + + dataOut->setRefCount(1); dataOut->frequency = frequency.load(); dataOut->sampleRate = sampleRate.load(); @@ -154,6 +161,7 @@ void SDRThread::readLoop() { sampleRate.store(device->getSampleRate(SOAPY_SDR_RX,0)); numChannels.store(getOptimalChannelCount(sampleRate.load())); numElems.store(getOptimalElementCount(sampleRate.load(), 60)); + inpBuffer.data.resize(numElems.load()); free(buffs[0]); buffs[0] = malloc(numElems.load() * 2 * sizeof(float)); rate_changed.store(false); diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h index 53b34dea..b64a6849 100644 --- a/src/sdr/SoapySDRThread.h +++ b/src/sdr/SoapySDRThread.h @@ -77,7 +77,8 @@ class SDRThread : public IOThread { SoapySDR::Device *device; void *buffs[1]; ReBuffer buffers; - + iirfilt_crcf dcFilter; + SDRThreadIQData inpBuffer; std::atomic deviceConfig; std::atomic deviceInfo; From a4dc4498fc80d695bb6e21e524f48c9d32706459 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Thu, 15 Oct 2015 21:01:07 -0400 Subject: [PATCH 05/13] Add support for my experimental firpfbch2_crcf_set_channel_state - Needs my fork of liquid-dsp at: https://github.com/cjcliffe/liquid-dsp/tree/firpfbch_toggle_channels --- src/sdr/SDRPostThread.cpp | 5 +++++ src/sdr/SoapySDRThread.cpp | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 237b7449..d8feb965 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -214,6 +214,11 @@ void SDRPostThread::run() { // Find active demodulators if (nRunDemods) { + + for (int i = 0; i < numChannels; i++) { + firpfbch2_crcf_set_channel_state(channelizer, i, (demodChannelActive[i]>0)?1:0); + } + // channelize data // firpfbch2 output rate is 2 x ( input rate / channels ) for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels/2) { diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index 9104351f..f43030a3 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -245,9 +245,9 @@ int SDRThread::getOptimalChannelCount(long long sampleRate) { optimal_count = 4; } - if (optimal_count > 16) { - optimal_count = 16; - } +// if (optimal_count > 16) { +// optimal_count = 16; +// } return optimal_count; } From 9da484e8ce041c16abed47791de4df8aea397798 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Thu, 15 Oct 2015 21:33:36 -0400 Subject: [PATCH 06/13] update version for test bundle release --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf437d55..17ece4e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required (VERSION 2.8) SET(CUBICSDR_VERSION_MAJOR "0") SET(CUBICSDR_VERSION_MINOR "1") -SET(CUBICSDR_VERSION_PATCH "8") -SET(CUBICSDR_VERSION_REL "beta-issue64") +SET(CUBICSDR_VERSION_PATCH "9") +SET(CUBICSDR_VERSION_REL "alpha-pfbch-issue150") SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}") SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}") From beccdf8c6321381f52e881dc1862619aff4bc80b Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 16 Oct 2015 18:40:40 -0400 Subject: [PATCH 07/13] firpfbch2->firpfbch experiment - My assumption about this not working correctly may be wrong, this actually seems to work ok compared to firpfbch2 --- src/sdr/SDRPostThread.cpp | 20 ++++++++++---------- src/sdr/SDRPostThread.h | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index d8feb965..0822aeda 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -56,11 +56,11 @@ bool SDRPostThread::getSwapIQ() { void SDRPostThread::initPFBChannelizer() { // std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl; if (channelizer) { - firpfbch2_crcf_destroy(channelizer); + firpfbch_crcf_destroy(channelizer); } - channelizer = firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 1, 60); + channelizer = firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 4, 60); - chanBw = (sampleRate / numChannels) * 2; + chanBw = (sampleRate / numChannels); chanCenters.resize(numChannels); demodChannelActive.resize(numChannels); @@ -122,7 +122,7 @@ void SDRPostThread::updateActiveDemodulators() { void SDRPostThread::updateChannels() { // calculate channel center frequencies, todo: cache for (int i = 0; i < numChannels/2; i++) { - int ofs = ((chanBw/2) * i); + int ofs = ((chanBw) * i); chanCenters[i] = frequency + ofs; chanCenters[i+(numChannels/2)] = frequency - (sampleRate/2) + ofs; } @@ -159,7 +159,7 @@ void SDRPostThread::run() { } int dataSize = data_in->data.size(); - int outSize = data_in->data.size()*2; + int outSize = data_in->data.size(); if (outSize > dataOut.capacity()) { dataOut.reserve(outSize); @@ -215,14 +215,14 @@ void SDRPostThread::run() { // Find active demodulators if (nRunDemods) { - for (int i = 0; i < numChannels; i++) { - firpfbch2_crcf_set_channel_state(channelizer, i, (demodChannelActive[i]>0)?1:0); - } +// for (int i = 0; i < numChannels; i++) { +// firpfbch_crcf_set_channel_state(channelizer, i, (demodChannelActive[i]>0)?1:0); +// } // channelize data // firpfbch2 output rate is 2 x ( input rate / channels ) - for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels/2) { - firpfbch2_crcf_execute(channelizer, &data_in->data[i], &dataOut[i * 2]); + 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; i < iMax; i++) { diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index d42ea54c..86eeec3b 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -50,5 +50,5 @@ class SDRPostThread : public IOThread { atomic_bool doRefresh; int numChannels, sampleRate; long long frequency; - firpfbch2_crcf channelizer; + firpfbch_crcf channelizer; }; From b438fc5a42ddcee6b19cc281ac8236d5da5c8e2b Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 16 Oct 2015 19:04:38 -0400 Subject: [PATCH 08/13] version update for bundle --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17ece4e5..11f020cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required (VERSION 2.8) SET(CUBICSDR_VERSION_MAJOR "0") SET(CUBICSDR_VERSION_MINOR "1") -SET(CUBICSDR_VERSION_PATCH "9") -SET(CUBICSDR_VERSION_REL "alpha-pfbch-issue150") +SET(CUBICSDR_VERSION_PATCH "10") +SET(CUBICSDR_VERSION_REL "alpha-pfbch-single-issue150") SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}") SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}") From f1475fb9bec241995f20257b0d32dacc1f9c6b7a Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sat, 17 Oct 2015 16:17:12 -0400 Subject: [PATCH 09/13] Move DC correction to channel 0, reduce demod vis CPU usage - Demod vis now uses channelizer output instead of decimation - DC correction now only applied to channel 0 --- src/AppFrame.cpp | 4 +-- src/CubicSDR.cpp | 19 ++-------- src/CubicSDR.h | 7 ++-- src/sdr/SDRPostThread.cpp | 71 ++++++++++++++++++++++++-------------- src/sdr/SDRPostThread.h | 4 ++- src/sdr/SoapySDRThread.cpp | 18 +++++----- src/sdr/SoapySDRThread.h | 1 - 7 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 662d882e..d6ae6fcb 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -671,8 +671,6 @@ void AppFrame::OnIdle(wxIdleEvent& event) { DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (demod) { - DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); - if (demod->isTracking()) { if (spectrumCanvas->getViewState()) { long long diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth()/2) + (demod->getBandwidth()/4); @@ -816,7 +814,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0)); wxGetApp().getScopeProcessor()->run(); - wxGetApp().getSpectrumDistributor()->run(); +// wxGetApp().getSpectrumDistributor()->run(); SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor(); diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 15e411c4..141e02b9 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -59,22 +59,14 @@ bool CubicSDR::OnInit() { pipeIQVisualData = new DemodulatorThreadInputQueue(); pipeIQVisualData->set_max_num_items(1); - spectrumDistributor.setInput(pipeIQVisualData); - pipeDemodIQVisualData = new DemodulatorThreadInputQueue(); pipeDemodIQVisualData->set_max_num_items(1); - pipeSpectrumIQVisualData = new DemodulatorThreadInputQueue(); - pipeSpectrumIQVisualData->set_max_num_items(1); - pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue(); pipeWaterfallIQVisualData->set_max_num_items(128); - spectrumDistributor.attachOutput(pipeDemodIQVisualData); - spectrumDistributor.attachOutput(pipeSpectrumIQVisualData); - getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData); - getSpectrumProcessor()->setInput(pipeSpectrumIQVisualData); + getSpectrumProcessor()->setInput(pipeIQVisualData); pipeAudioVisualData = new DemodulatorThreadOutputQueue(); pipeAudioVisualData->set_max_num_items(1); @@ -89,19 +81,17 @@ bool CubicSDR::OnInit() { sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData); sdrPostThread = new SDRPostThread(); -// sdrPostThread->setNumVisSamples(BUF_SIZE); sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData); sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData); sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData); + sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData); t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread); t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread); t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread); -// t_SDR = new std::thread(&SDRThread::threadMain, sdrThread); sdrEnum = new SDREnumerator(); - appframe = new AppFrame(); t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); @@ -371,11 +361,6 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() { return demodVisualThread->getProcessor(); } -VisualDataDistributor *CubicSDR::getSpectrumDistributor() { - return &spectrumDistributor; -} - - DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() { return pipeAudioVisualData; } diff --git a/src/CubicSDR.h b/src/CubicSDR.h index f892fdd0..f7d7b9ad 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -70,11 +70,11 @@ class CubicSDR: public wxApp { ScopeVisualProcessor *getScopeProcessor(); SpectrumVisualProcessor *getSpectrumProcessor(); SpectrumVisualProcessor *getDemodSpectrumProcessor(); - VisualDataDistributor *getSpectrumDistributor(); DemodulatorThreadOutputQueue* getAudioVisualQueue(); DemodulatorThreadInputQueue* getIQVisualQueue(); DemodulatorThreadInputQueue* getWaterfallVisualQueue(); + DemodulatorThreadInputQueue* getActiveDemodVisualQueue(); DemodulatorMgr &getDemodMgr(); void bindDemodulator(DemodulatorInstance *demod); @@ -122,18 +122,15 @@ class CubicSDR: public wxApp { SpectrumVisualDataThread *spectrumVisualThread; SpectrumVisualDataThread *demodVisualThread; -// SDRThreadCommandQueue* pipeSDRCommand; SDRThreadIQDataQueue* pipeSDRIQData; DemodulatorThreadInputQueue* pipeIQVisualData; DemodulatorThreadOutputQueue* pipeAudioVisualData; DemodulatorThreadInputQueue* pipeDemodIQVisualData; - DemodulatorThreadInputQueue* pipeSpectrumIQVisualData; DemodulatorThreadInputQueue* pipeWaterfallIQVisualData; + DemodulatorThreadInputQueue* pipeActiveDemodIQVisualData; ScopeVisualProcessor scopeProcessor; - VisualDataDistributor spectrumDistributor; - SDRDevicesDialog *deviceSelectorDialog; std::thread *t_SDR, *t_SDREnum, *t_PostSDR, *t_SpectrumVisual, *t_DemodVisual; diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 0822aeda..74175e91 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -18,6 +18,7 @@ SDRPostThread::SDRPostThread() : IOThread() { nRunDemods = 0; doRefresh.store(false); + dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); } SDRPostThread::~SDRPostThread() { @@ -65,9 +66,7 @@ void SDRPostThread::initPFBChannelizer() { chanCenters.resize(numChannels); demodChannelActive.resize(numChannels); - // firpfbch2 returns 2x sample rate per channel - // so, max demodulation without gaps is 1/2 chanBw ..? -// std::cout << "Channel bandwidth spacing: " << (chanBw/2) << " actual bandwidth: " << chanBw << std::endl; +// std::cout << "Channel bandwidth spacing: " << (chanBw) << std::endl; } void SDRPostThread::updateActiveDemodulators() { @@ -141,8 +140,11 @@ void SDRPostThread::run() { iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput"); iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput"); iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput"); + iqActiveDemodVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQActiveDemodVisualDataOutput"); iqDataInQueue->set_max_num_items(0); + + std::vector dcBuf; while (!terminated) { SDRThreadIQData *data_in; @@ -178,24 +180,25 @@ void SDRPostThread::run() { // } // } - if (iqVisualQueue != NULL || iqDataOutQueue != NULL) { - bool doIQVis = iqVisualQueue && !iqVisualQueue->full(); - bool doIQOut = iqDataOutQueue != NULL; - + if (iqDataOutQueue != NULL && !iqDataOutQueue->full()) { DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer(); - iqDataOut->setRefCount((doIQVis?1:0) + (doIQOut?1:0)); + + 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); - if (doIQVis) { + iqDataOutQueue->push(iqDataOut); + if (doVis) { iqVisualQueue->push(iqDataOut); } - - if (doIQOut) { - iqDataOutQueue->push(iqDataOut); - } } busy_demod.lock(); @@ -211,6 +214,8 @@ void SDRPostThread::run() { doRefresh.store(false); } + DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); + int activeDemodChannel = -1; // Find active demodulators if (nRunDemods) { @@ -220,7 +225,7 @@ void SDRPostThread::run() { // } // channelize data - // firpfbch2 output rate is 2 x ( input rate / channels ) + // 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]); } @@ -239,30 +244,29 @@ void SDRPostThread::run() { if (fdelta < minDelta) { minDelta = fdelta; demodChannel[i] = j; + if (demod == activeDemod) { + activeDemodChannel = j; + } } } } - + 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; i++) { - if (demodChannelActive[i] == 0) { - // Nothing using this channel, skip - continue; - } - + bool doVis = (activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full(); + DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); - demodDataOut->setRefCount(demodChannelActive[i]); + demodDataOut->setRefCount(demodChannelActive[i] + (doVis?1:0)); demodDataOut->frequency = chanCenters[i]; demodDataOut->sampleRate = chanBw; - + // Calculate channel buffer size int chanDataSize = (outSize/numChannels); @@ -274,9 +278,24 @@ void SDRPostThread::run() { } // prepare channel data buffer - for (int j = 0, idx = i; j < chanDataSize; j++) { - idx += numChannels; - demodDataOut->data[j] = dataOut[idx]; + if (i == 0) { // Channel 0 requires DC correction + if (dcBuf.size() != chanDataSize) { + dcBuf.resize(chanDataSize); + } + for (int j = 0, idx = i; j < chanDataSize; j++) { + idx += numChannels; + dcBuf[j] = dataOut[idx]; + } + iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]); + } else { + for (int j = 0, idx = i; j < chanDataSize; j++) { + idx += numChannels; + demodDataOut->data[j] = dataOut[idx]; + } + } + + if (doVis) { + iqActiveDemodVisualQueue->push(demodDataOut); } for (int j = 0; j < nRunDemods; j++) { diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index 86eeec3b..b3a2d4c1 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -25,7 +25,8 @@ class SDRPostThread : public IOThread { SDRThreadIQDataQueue *iqDataInQueue; DemodulatorThreadInputQueue *iqDataOutQueue; DemodulatorThreadInputQueue *iqVisualQueue; - + DemodulatorThreadInputQueue *iqActiveDemodVisualQueue; + std::mutex busy_demod; std::vector demodulators; std::atomic_bool swapIQ; @@ -51,4 +52,5 @@ class SDRPostThread : public IOThread { int numChannels, sampleRate; long long frequency; firpfbch_crcf channelizer; + iirfilt_crcf dcFilter; }; diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index f43030a3..8fe79956 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -30,7 +30,7 @@ SDRThread::SDRThread() : IOThread() { hasHardwareDC.store(false); numChannels.store(8); - dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); +// dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); } SDRThread::~SDRThread() { @@ -80,7 +80,7 @@ void SDRThread::init() { } if (chan->hasHardwareDC()) { hasHardwareDC.store(true); - wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Found hardware DC offset correction support, internal disabled.")); +// wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Found hardware DC offset correction support, internal disabled.")); device->setDCOffsetMode(SOAPY_SDR_RX, chan->getChannel(), true); } else { hasHardwareDC.store(false); @@ -121,14 +121,14 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) { if (n_read > 0 && !terminated) { SDRThreadIQData *dataOut = buffers.getBuffer(); - if (hasHardwareDC) { +// if (hasHardwareDC) { dataOut->data.assign(inpBuffer.data.begin(), inpBuffer.data.begin()+n_read); - } else { - if (dataOut->data.size() != n_read) { - dataOut->data.resize(n_read); - } - iirfilt_crcf_execute_block(dcFilter, &inpBuffer.data[0], n_read, &dataOut->data[0]); - } +// } else { +// if (dataOut->data.size() != n_read) { +// dataOut->data.resize(n_read); +// } +// iirfilt_crcf_execute_block(dcFilter, &inpBuffer.data[0], n_read, &dataOut->data[0]); +// } dataOut->setRefCount(1); diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h index b64a6849..d86044f8 100644 --- a/src/sdr/SoapySDRThread.h +++ b/src/sdr/SoapySDRThread.h @@ -77,7 +77,6 @@ class SDRThread : public IOThread { SoapySDR::Device *device; void *buffs[1]; ReBuffer buffers; - iirfilt_crcf dcFilter; SDRThreadIQData inpBuffer; std::atomic deviceConfig; std::atomic deviceInfo; From 6ee51711dbff8ca1425a55aee10b675364e75e4e Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sat, 17 Oct 2015 18:25:14 -0400 Subject: [PATCH 10/13] Fix channel gap on upper edge of spectrum --- src/sdr/SDRPostThread.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 74175e91..3710ef76 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -63,8 +63,8 @@ void SDRPostThread::initPFBChannelizer() { chanBw = (sampleRate / numChannels); - chanCenters.resize(numChannels); - demodChannelActive.resize(numChannels); + chanCenters.resize(numChannels+1); + demodChannelActive.resize(numChannels+1); // std::cout << "Channel bandwidth spacing: " << (chanBw) << std::endl; } @@ -125,6 +125,7 @@ void SDRPostThread::updateChannels() { chanCenters[i] = frequency + ofs; chanCenters[i+(numChannels/2)] = frequency - (sampleRate/2) + ofs; } + chanCenters[numChannels] = frequency + (sampleRate/2); } void SDRPostThread::run() { @@ -238,7 +239,7 @@ void SDRPostThread::run() { for (int i = 0; i < nRunDemods; i++) { DemodulatorInstance *demod = runDemods[i]; long long minDelta = data_in->sampleRate; - for (int j = 0, jMax = numChannels; j < jMax; j++) { + for (int j = 0, jMax = numChannels+1; j < jMax; j++) { // Distance from channel center to demod center long long fdelta = abs(demod->getFrequency() - chanCenters[j]); if (fdelta < minDelta) { @@ -259,7 +260,7 @@ void SDRPostThread::run() { } // Run channels - for (int i = 0; i < numChannels; i++) { + for (int i = 0; i < numChannels+1; i++) { bool doVis = (activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full(); DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); @@ -277,18 +278,26 @@ void SDRPostThread::run() { 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, idx = i; j < chanDataSize; j++) { + for (int j = 0; j < chanDataSize; j++) { idx += numChannels; dcBuf[j] = dataOut[idx]; } iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]); } else { - for (int j = 0, idx = i; j < chanDataSize; j++) { + for (int j = 0; j < chanDataSize; j++) { idx += numChannels; demodDataOut->data[j] = dataOut[idx]; } From 67c184262a76bca950be90116644d1897ed29fbf Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 18 Oct 2015 12:26:07 -0400 Subject: [PATCH 11/13] Minor tweaks and cleanup - add functions to access SDRPostThread --- src/AppFrame.cpp | 1 + src/CubicSDR.cpp | 4 ++ src/CubicSDR.h | 2 + src/demod/DemodulatorThread.cpp | 8 ++-- src/sdr/SDRPostThread.cpp | 67 +++++++++++++++++++++++++-------- src/sdr/SDRPostThread.h | 5 +++ 6 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index d6ae6fcb..2816b240 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -855,6 +855,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { wproc->setView(waterfallCanvas->getViewState()); wproc->setBandwidth(waterfallCanvas->getBandwidth()); wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency()); + wxGetApp().getSDRPostThread()->setIQVisualRange(waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth()); // waterfallCanvas->processInputQueue(); // waterfallCanvas->Refresh(); diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 141e02b9..791b1ba5 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -377,6 +377,10 @@ DemodulatorMgr &CubicSDR::getDemodMgr() { return demodMgr; } +SDRPostThread *CubicSDR::getSDRPostThread() { + return sdrPostThread; +} + void CubicSDR::bindDemodulator(DemodulatorInstance *demod) { if (!demod) { return; diff --git a/src/CubicSDR.h b/src/CubicSDR.h index f7d7b9ad..acfb800c 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -77,6 +77,8 @@ class CubicSDR: public wxApp { DemodulatorThreadInputQueue* getActiveDemodVisualQueue(); DemodulatorMgr &getDemodMgr(); + SDRPostThread *getSDRPostThread(); + void bindDemodulator(DemodulatorInstance *demod); void removeDemodulator(DemodulatorInstance *demod); diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index e1274e29..133beb4b 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -59,7 +59,7 @@ void DemodulatorThread::run() { nco_crcf_pll_set_bandwidth(stereoPilot, 0.25f); // half band filter used for side-band elimination - resamp2_cccf ssbFilt = resamp2_cccf_create(12,-0.25f,60.0f); + resamp2_crcf ssbFilt = resamp2_crcf_create(12,-0.25f,60.0f); // Automatic IQ gain iqAutoGain = agc_crcf_create(); @@ -192,13 +192,13 @@ void DemodulatorThread::run() { switch (demodulatorType.load()) { case DEMOD_TYPE_LSB: for (int i = 0; i < bufSize; i++) { // Reject upper band - resamp2_cccf_filter_execute(ssbFilt,(*inputData)[i],&x,&y); + resamp2_crcf_filter_execute(ssbFilt,(*inputData)[i],&x,&y); ampmodem_demodulate(demodAM, x, &demodOutputData[i]); } break; case DEMOD_TYPE_USB: for (int i = 0; i < bufSize; i++) { // Reject lower band - resamp2_cccf_filter_execute(ssbFilt,(*inputData)[i],&x,&y); + resamp2_crcf_filter_execute(ssbFilt,(*inputData)[i],&x,&y); ampmodem_demodulate(demodAM, y, &demodOutputData[i]); } break; @@ -487,7 +487,7 @@ void DemodulatorThread::run() { firhilbf_destroy(firStereoR2C); firhilbf_destroy(firStereoC2R); nco_crcf_destroy(stereoPilot); - resamp2_cccf_destroy(ssbFilt); + resamp2_crcf_destroy(ssbFilt); outputBuffers.purge(); diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 3710ef76..31ec0b7c 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -17,6 +17,9 @@ SDRPostThread::SDRPostThread() : IOThread() { sampleRate = 0; nRunDemods = 0; + visFrequency.store(0); + visBandwidth.store(0); + doRefresh.store(false); dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); } @@ -128,6 +131,24 @@ void SDRPostThread::updateChannels() { chanCenters[numChannels] = frequency + (sampleRate/2); } +int SDRPostThread::getChannelAt(long long frequency) { + int chan = -1; + long long minDelta = sampleRate; + for (int i = 0; i < numChannels+1; i++) { + long long fdelta = abs(frequency - chanCenters[i]); + if (fdelta < minDelta) { + minDelta = fdelta; + chan = i; + } + } + return chan; +} + +void SDRPostThread::setIQVisualRange(long long frequency, int bandwidth) { + visFrequency.store(frequency); + visBandwidth.store(bandwidth); +} + void SDRPostThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread @@ -181,7 +202,13 @@ void SDRPostThread::run() { // } // } - if (iqDataOutQueue != NULL && !iqDataOutQueue->full()) { + int activeVisChannel = -1; + +// if (visBandwidth.load() && visBandwidth.load() < (chanBw/2)) { +// activeVisChannel = getChannelAt(visFrequency); +// } + + if (iqDataOutQueue != NULL && !iqDataOutQueue->full() && activeVisChannel < 0) { DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer(); bool doVis = false; @@ -219,7 +246,7 @@ void SDRPostThread::run() { int activeDemodChannel = -1; // Find active demodulators - if (nRunDemods) { + if (nRunDemods || (activeVisChannel >= 0)) { // for (int i = 0; i < numChannels; i++) { // firpfbch_crcf_set_channel_state(channelizer, i, (demodChannelActive[i]>0)?1:0); @@ -238,17 +265,9 @@ void SDRPostThread::run() { // Find nearest channel for each demodulator for (int i = 0; i < nRunDemods; i++) { DemodulatorInstance *demod = runDemods[i]; - long long minDelta = data_in->sampleRate; - for (int j = 0, jMax = numChannels+1; j < jMax; j++) { - // Distance from channel center to demod center - long long fdelta = abs(demod->getFrequency() - chanCenters[j]); - if (fdelta < minDelta) { - minDelta = fdelta; - demodChannel[i] = j; - if (demod == activeDemod) { - activeDemodChannel = j; - } - } + demodChannel[i] = getChannelAt(demod->getFrequency()); + if (demod == activeDemod) { + activeDemodChannel = demodChannel[i]; } } @@ -261,10 +280,19 @@ void SDRPostThread::run() { // Run channels for (int i = 0; i < numChannels+1; i++) { - bool doVis = (activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full(); + int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full())?1:0; + int doVis = 0; + +// if (activeVisChannel == i) { +// doVis = (((iqDataOutQueue != NULL))?1:0) + ((iqVisualQueue != NULL && !iqVisualQueue->full())?1:0); +// } + + if (!doVis && !doDemodVis && demodChannelActive[i] == 0) { + continue; + } DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); - demodDataOut->setRefCount(demodChannelActive[i] + (doVis?1:0)); + demodDataOut->setRefCount(demodChannelActive[i] + doVis + doDemodVis); demodDataOut->frequency = chanCenters[i]; demodDataOut->sampleRate = chanBw; @@ -303,7 +331,14 @@ void SDRPostThread::run() { } } - if (doVis) { +// if (doVis) { +// iqDataOutQueue->push(demodDataOut); +// if (doVis>1) { +// iqVisualQueue->push(demodDataOut); +// } +// } + + if (doDemodVis) { iqActiveDemodVisualQueue->push(demodDataOut); } diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index b3a2d4c1..7a9b6349 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -21,6 +21,8 @@ class SDRPostThread : public IOThread { void run(); void terminate(); + void setIQVisualRange(long long frequency, int bandwidth); + protected: SDRThreadIQDataQueue *iqDataInQueue; DemodulatorThreadInputQueue *iqDataOutQueue; @@ -35,6 +37,7 @@ class SDRPostThread : public IOThread { void initPFBChannelizer(); void updateActiveDemodulators(); void updateChannels(); + int getChannelAt(long long frequency); ReBuffer buffers; std::vector fpData; @@ -49,6 +52,8 @@ class SDRPostThread : public IOThread { ReBuffer visualDataBuffers; atomic_bool doRefresh; + atomic_llong visFrequency; + atomic_int visBandwidth; int numChannels, sampleRate; long long frequency; firpfbch_crcf channelizer; From c200048bf78c0d944074c3c22ccf8d0091861fcd Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 18 Oct 2015 13:44:31 -0400 Subject: [PATCH 12/13] Reduce glTexSubImage2D calls - far less texture update calls for high speed waterfall rates --- src/panel/WaterfallPanel.cpp | 37 +++++++++++++++++++++++++++++++--- src/panel/WaterfallPanel.h | 6 +++++- src/visual/WaterfallCanvas.cpp | 1 + 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/panel/WaterfallPanel.cpp b/src/panel/WaterfallPanel.cpp index 5de721a7..4720b4f7 100644 --- a/src/panel/WaterfallPanel.cpp +++ b/src/panel/WaterfallPanel.cpp @@ -91,15 +91,46 @@ void WaterfallPanel::step() { waterfall_slice[i] = (unsigned char) floor(wv * 255.0); } + int newBufSize = (half_fft_size*lines_buffered+half_fft_size); + if (lineBuffer[j].size() < newBufSize) { + lineBuffer[j].resize(newBufSize); + rLineBuffer[j].resize(newBufSize); + } + memcpy(&(lineBuffer[j][half_fft_size*lines_buffered]), waterfall_slice, sizeof(unsigned char) * half_fft_size); + } + lines_buffered++; + } +} + +void WaterfallPanel::update() { + int half_fft_size = fft_size / 2; + + for (int i = 0; i < lines_buffered; i++) { + for (int j = 0; j < 2; j++) { + memcpy(&(rLineBuffer[j][i*half_fft_size]), + &(lineBuffer[j][((lines_buffered-1)*half_fft_size)-(i*half_fft_size)]), sizeof(unsigned char) * half_fft_size); + } + } + + int run_ofs = 0; + while (lines_buffered) { + int run_lines = lines_buffered; + if (run_lines > waterfall_ofs[0]) { + run_lines = waterfall_ofs[0]; + } + for (int j = 0; j < 2; j++) { glBindTexture(GL_TEXTURE_2D, waterfall[j]); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, waterfall_ofs[j], half_fft_size, 1, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_slice); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, waterfall_ofs[j]-run_lines, half_fft_size, run_lines, + GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) &(rLineBuffer[j][run_ofs])); + + waterfall_ofs[j]-=run_lines; if (waterfall_ofs[j] == 0) { waterfall_ofs[j] = waterfall_lines; } - - waterfall_ofs[j]--; } + run_ofs += run_lines*half_fft_size; + lines_buffered-=run_lines; } } diff --git a/src/panel/WaterfallPanel.h b/src/panel/WaterfallPanel.h index 65c664a0..faa566aa 100644 --- a/src/panel/WaterfallPanel.h +++ b/src/panel/WaterfallPanel.h @@ -9,7 +9,8 @@ class WaterfallPanel : public GLPanel { void refreshTheme(); void setPoints(std::vector &points); void step(); - + void update(); + protected: void drawPanelContents(); @@ -21,6 +22,9 @@ class WaterfallPanel : public GLPanel { int fft_size; int waterfall_lines; unsigned char *waterfall_slice; + std::vector lineBuffer[2]; + std::vector rLineBuffer[2]; + int lines_buffered; ColorTheme *activeTheme; }; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index f64845ae..5b5cbe87 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -100,6 +100,7 @@ void WaterfallCanvas::processInputQueue() { break; } } + waterfallPanel.update(); tex_update.unlock(); } }} From 73733eabfe10756b5c8b5539d2a79bdd7c723a11 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 18 Oct 2015 14:18:26 -0400 Subject: [PATCH 13/13] Update default sample rate - Should fix startup for AirSpy --- src/AppFrame.cpp | 13 +++++-------- src/AppFrame.h | 4 ++-- src/CubicSDRDefs.h | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 2816b240..357f1d69 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -280,14 +280,14 @@ AppFrame::AppFrame() : sampleRateMenuItems[wxID_BANDWIDTH_2000M] = menu->AppendRadioItem(wxID_BANDWIDTH_2000M, "2.0M"); sampleRateMenuItems[wxID_BANDWIDTH_2048M] = menu->AppendRadioItem(wxID_BANDWIDTH_2048M, "2.048M"); sampleRateMenuItems[wxID_BANDWIDTH_2160M] = menu->AppendRadioItem(wxID_BANDWIDTH_2160M, "2.16M"); - sampleRateMenuItems[wxID_BANDWIDTH_2400M] = menu->AppendRadioItem(wxID_BANDWIDTH_2400M, "2.4M"); - sampleRateMenuItems[wxID_BANDWIDTH_2560M] = menu->AppendRadioItem(wxID_BANDWIDTH_2560M, "2.56M"); +// sampleRateMenuItems[wxID_BANDWIDTH_2400M] = menu->AppendRadioItem(wxID_BANDWIDTH_2400M, "2.4M"); + sampleRateMenuItems[wxID_BANDWIDTH_2500M] = menu->AppendRadioItem(wxID_BANDWIDTH_2500M, "2.5M"); sampleRateMenuItems[wxID_BANDWIDTH_2880M] = menu->AppendRadioItem(wxID_BANDWIDTH_2880M, "2.88M"); // sampleRateMenuItems[wxID_BANDWIDTH_3000M] = menu->AppendRadioItem(wxID_BANDWIDTH_3000M, "3.0M"); sampleRateMenuItems[wxID_BANDWIDTH_3200M] = menu->AppendRadioItem(wxID_BANDWIDTH_3200M, "3.2M"); sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = menu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, "Manual Entry"); - sampleRateMenuItems[wxID_BANDWIDTH_2400M]->Check(true); + sampleRateMenuItems[wxID_BANDWIDTH_2500M]->Check(true); menuBar->Append(menu, wxT("&Input Bandwidth")); @@ -574,11 +574,8 @@ void AppFrame::OnMenu(wxCommandEvent& event) { case wxID_BANDWIDTH_2160M: wxGetApp().setSampleRate(2160000); break; - case wxID_BANDWIDTH_2400M: - wxGetApp().setSampleRate(2400000); - break; - case wxID_BANDWIDTH_2560M: - wxGetApp().setSampleRate(2560000); + case wxID_BANDWIDTH_2500M: + wxGetApp().setSampleRate(2500000); break; case wxID_BANDWIDTH_2880M: wxGetApp().setSampleRate(2880000); diff --git a/src/AppFrame.h b/src/AppFrame.h index 26c0c61e..3359d97b 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -41,8 +41,8 @@ #define wxID_BANDWIDTH_2000M 2156 #define wxID_BANDWIDTH_2048M 2157 #define wxID_BANDWIDTH_2160M 2158 -#define wxID_BANDWIDTH_2400M 2159 -#define wxID_BANDWIDTH_2560M 2160 +//#define wxID_BANDWIDTH_2400M 2159 +#define wxID_BANDWIDTH_2500M 2160 #define wxID_BANDWIDTH_2880M 2161 //#define wxID_BANDWIDTH_3000M 2162 #define wxID_BANDWIDTH_3200M 2163 diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 60c43132..11a98386 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -27,7 +27,7 @@ const char filePathSeparator = #define BUF_SIZE (16384*6) -#define DEFAULT_SAMPLE_RATE 2400000 +#define DEFAULT_SAMPLE_RATE 2500000 #define DEFAULT_FFT_SIZE 2048 #define DEFAULT_DEMOD_TYPE 1