2017-01-02 21:07:43 -05:00
|
|
|
// Copyright (c) Charles J. Cliffe
|
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
|
2014-11-29 13:58:20 -05:00
|
|
|
#include "SDRPostThread.h"
|
|
|
|
#include "CubicSDRDefs.h"
|
|
|
|
#include "CubicSDR.h"
|
|
|
|
|
2014-12-23 23:37:18 -05:00
|
|
|
#include <vector>
|
|
|
|
#include <deque>
|
2017-08-13 18:49:47 +02:00
|
|
|
#include <memory>
|
2014-12-23 23:37:18 -05:00
|
|
|
|
2017-05-25 09:32:27 +02:00
|
|
|
//50 ms
|
|
|
|
#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
|
|
|
|
|
2016-01-29 20:08:33 +11:00
|
|
|
SDRPostThread::SDRPostThread() : IOThread(), buffers("SDRPostThreadBuffers"), visualDataBuffers("SDRPostThreadVisualDataBuffers"), frequency(0) {
|
2017-08-27 13:00:32 +02:00
|
|
|
iqDataInQueue = nullptr;
|
|
|
|
iqDataOutQueue = nullptr;
|
|
|
|
iqVisualQueue = nullptr;
|
2015-10-14 18:09:29 -04:00
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
numChannels = 0;
|
2017-08-27 13:00:32 +02:00
|
|
|
channelizer = nullptr;
|
2018-02-14 21:24:22 -05:00
|
|
|
channelizer2 = nullptr;
|
|
|
|
chanMode = 2;
|
|
|
|
lastChanMode = 0;
|
2015-10-14 18:09:29 -04:00
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
sampleRate = 0;
|
2015-10-14 18:09:29 -04:00
|
|
|
|
2015-10-18 12:26:07 -04:00
|
|
|
visFrequency.store(0);
|
|
|
|
visBandwidth.store(0);
|
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
doRefresh.store(false);
|
2016-06-01 19:42:34 -04:00
|
|
|
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005f);
|
2014-11-29 13:58:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
SDRPostThread::~SDRPostThread() {
|
2014-12-11 19:07:21 -05:00
|
|
|
}
|
|
|
|
|
2017-08-27 12:39:28 +02:00
|
|
|
void SDRPostThread::notifyDemodulatorsChanged() {
|
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
doRefresh.store(true);
|
2016-06-02 23:56:31 +02:00
|
|
|
|
2014-12-11 19:07:21 -05:00
|
|
|
}
|
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
void SDRPostThread::initPFBChannelizer() {
|
|
|
|
// std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl;
|
|
|
|
if (channelizer) {
|
2015-10-16 18:40:40 -04:00
|
|
|
firpfbch_crcf_destroy(channelizer);
|
2015-10-14 18:09:29 -04:00
|
|
|
}
|
2015-10-16 18:40:40 -04:00
|
|
|
channelizer = firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 4, 60);
|
2015-10-14 18:09:29 -04:00
|
|
|
|
2015-10-16 18:40:40 -04:00
|
|
|
chanBw = (sampleRate / numChannels);
|
2015-10-14 18:09:29 -04:00
|
|
|
|
2015-10-17 18:25:14 -04:00
|
|
|
chanCenters.resize(numChannels+1);
|
|
|
|
demodChannelActive.resize(numChannels+1);
|
2015-10-14 18:09:29 -04:00
|
|
|
|
2015-10-17 16:17:12 -04:00
|
|
|
// std::cout << "Channel bandwidth spacing: " << (chanBw) << std::endl;
|
2015-10-14 18:09:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void SDRPostThread::updateActiveDemodulators() {
|
|
|
|
// In range?
|
2017-08-28 21:22:07 +02:00
|
|
|
|
|
|
|
runDemods.clear();
|
|
|
|
demodChannel.clear();
|
|
|
|
|
2016-02-15 17:43:10 -05:00
|
|
|
long long centerFreq = wxGetApp().getFrequency();
|
2016-07-06 21:23:59 +02:00
|
|
|
|
2017-08-27 12:39:28 +02:00
|
|
|
//retreive the current list of demodulators:
|
|
|
|
auto demodulators = wxGetApp().getDemodMgr().getDemodulators();
|
2017-08-29 21:59:30 +02:00
|
|
|
|
2017-08-27 11:11:30 +02:00
|
|
|
for (auto demod : demodulators) {
|
2017-08-29 21:59:30 +02:00
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
// not in range?
|
2016-02-15 17:43:10 -05:00
|
|
|
if (demod->isDeltaLock()) {
|
|
|
|
if (demod->getFrequency() != centerFreq + demod->getDeltaLockOfs()) {
|
|
|
|
demod->setFrequency(centerFreq + demod->getDeltaLockOfs());
|
|
|
|
demod->updateLabel(demod->getFrequency());
|
|
|
|
demod->setFollow(false);
|
|
|
|
demod->setTracking(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) {
|
|
|
|
// deactivate if active
|
2017-08-29 21:59:30 +02:00
|
|
|
|
|
|
|
if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == demod) {
|
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
demod->setActive(false);
|
|
|
|
}
|
2017-08-29 21:59:30 +02:00
|
|
|
else if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) {
|
|
|
|
demod->setActive(false);
|
|
|
|
}
|
2015-10-14 18:09:29 -04:00
|
|
|
|
|
|
|
// follow if follow mode
|
2016-02-15 17:43:10 -05:00
|
|
|
if (demod->isFollow() && centerFreq != demod->getFrequency()) {
|
2015-10-14 18:09:29 -04:00
|
|
|
wxGetApp().setFrequency(demod->getFrequency());
|
|
|
|
demod->setFollow(false);
|
|
|
|
}
|
|
|
|
} else if (!demod->isActive()) { // in range, activate if not activated
|
|
|
|
demod->setActive(true);
|
2017-08-27 13:00:32 +02:00
|
|
|
if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == nullptr) {
|
2016-07-06 21:23:59 +02:00
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
wxGetApp().getDemodMgr().setActiveDemodulator(demod);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!demod->isActive()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-08-29 21:59:30 +02:00
|
|
|
// Add active demods to the current run:
|
|
|
|
|
2017-08-28 21:22:07 +02:00
|
|
|
runDemods.push_back(demod);
|
|
|
|
demodChannel.push_back(-1);
|
2015-10-14 18:09:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-29 21:59:30 +02:00
|
|
|
void SDRPostThread::resetAllDemodulators() {
|
|
|
|
|
|
|
|
//retreive the current list of demodulators:
|
|
|
|
auto demodulators = wxGetApp().getDemodMgr().getDemodulators();
|
|
|
|
|
|
|
|
for (auto demod : demodulators) {
|
|
|
|
|
|
|
|
demod->setActive(false);
|
|
|
|
demod->getIQInputDataPipe()->flush();
|
|
|
|
}
|
|
|
|
|
2018-02-14 21:24:22 -05:00
|
|
|
doRefresh.store(true);
|
2017-08-29 21:59:30 +02:00
|
|
|
}
|
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
void SDRPostThread::updateChannels() {
|
|
|
|
// calculate channel center frequencies, todo: cache
|
|
|
|
for (int i = 0; i < numChannels/2; i++) {
|
2015-10-16 18:40:40 -04:00
|
|
|
int ofs = ((chanBw) * i);
|
2015-10-14 18:09:29 -04:00
|
|
|
chanCenters[i] = frequency + ofs;
|
|
|
|
chanCenters[i+(numChannels/2)] = frequency - (sampleRate/2) + ofs;
|
|
|
|
}
|
2015-10-17 18:25:14 -04:00
|
|
|
chanCenters[numChannels] = frequency + (sampleRate/2);
|
2015-10-14 18:09:29 -04:00
|
|
|
}
|
|
|
|
|
2015-10-18 12:26:07 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-02-14 21:24:22 -05:00
|
|
|
void SDRPostThread::setChannelizerType(SDRPostThreadChannelizerType chType) {
|
|
|
|
chanMode.store((int)chType);
|
|
|
|
}
|
|
|
|
|
|
|
|
SDRPostThreadChannelizerType SDRPostThread::getChannelizerType() {
|
|
|
|
return (SDRPostThreadChannelizerType) chanMode.load();
|
|
|
|
}
|
|
|
|
|
2015-07-29 20:57:02 -04:00
|
|
|
void SDRPostThread::run() {
|
2014-12-18 20:11:25 -05:00
|
|
|
#ifdef __APPLE__
|
|
|
|
pthread_t tID = pthread_self(); // ID of this thread
|
2015-10-14 00:54:48 -04:00
|
|
|
int priority = sched_get_priority_max( SCHED_FIFO);
|
2014-12-24 01:28:33 -05:00
|
|
|
sched_param prio = {priority}; // scheduling priority of thread
|
2014-12-18 20:11:25 -05:00
|
|
|
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
|
|
|
#endif
|
2014-12-16 20:33:44 -05:00
|
|
|
|
2016-06-02 19:45:34 -04:00
|
|
|
// std::cout << "SDR post-processing thread started.." << std::endl;
|
2014-11-30 17:11:29 -05:00
|
|
|
|
2017-08-13 18:49:47 +02:00
|
|
|
iqDataInQueue = std::static_pointer_cast<SDRThreadIQDataQueue>(getInputQueue("IQDataInput"));
|
|
|
|
iqDataOutQueue = std::static_pointer_cast<DemodulatorThreadInputQueue>(getOutputQueue("IQDataOutput"));
|
|
|
|
iqVisualQueue = std::static_pointer_cast<DemodulatorThreadInputQueue>(getOutputQueue("IQVisualDataOutput"));
|
|
|
|
iqActiveDemodVisualQueue = std::static_pointer_cast<DemodulatorThreadInputQueue>(getOutputQueue("IQActiveDemodVisualDataOutput"));
|
2015-08-12 21:45:02 -04:00
|
|
|
|
2016-06-28 21:04:52 +02:00
|
|
|
while (!stopping) {
|
2017-05-24 12:24:36 +02:00
|
|
|
SDRThreadIQDataPtr data_in;
|
2015-08-11 00:50:43 -04:00
|
|
|
|
2017-05-25 09:32:27 +02:00
|
|
|
if (!iqDataInQueue->pop(data_in, HEARTBEAT_CHECK_PERIOD_MICROS)) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-08-27 12:39:28 +02:00
|
|
|
|
|
|
|
bool doUpdate = false;
|
2015-10-14 00:54:48 -04:00
|
|
|
|
2015-12-29 20:52:49 -05:00
|
|
|
if (data_in && data_in->data.size()) {
|
|
|
|
if(data_in->numChannels > 1) {
|
2018-02-14 21:24:22 -05:00
|
|
|
if (chanMode == 1) {
|
|
|
|
runPFBCH(data_in.get());
|
|
|
|
} else if (chanMode == 2) {
|
|
|
|
runPFBCH2(data_in.get());
|
|
|
|
}
|
2015-12-29 20:52:49 -05:00
|
|
|
} else {
|
2017-05-24 12:24:36 +02:00
|
|
|
runSingleCH(data_in.get());
|
2015-10-14 18:09:29 -04:00
|
|
|
}
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
2017-08-27 12:39:28 +02:00
|
|
|
|
2017-08-28 21:22:07 +02:00
|
|
|
for (size_t j = 0; j < runDemods.size(); j++) {
|
2017-08-27 11:11:30 +02:00
|
|
|
DemodulatorInstancePtr demod = runDemods[j];
|
2015-12-29 20:52:49 -05:00
|
|
|
if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) {
|
|
|
|
doUpdate = true;
|
2015-10-27 01:56:49 -04:00
|
|
|
}
|
2014-12-23 01:59:03 -05:00
|
|
|
}
|
2015-12-29 20:52:49 -05:00
|
|
|
|
2016-07-06 21:23:59 +02:00
|
|
|
//Only update the list of demodulators here
|
2018-02-14 21:24:22 -05:00
|
|
|
if (doUpdate || doRefresh.load()) {
|
2015-12-29 20:52:49 -05:00
|
|
|
updateActiveDemodulators();
|
|
|
|
}
|
2016-07-05 21:43:45 +02:00
|
|
|
} //end while
|
2015-07-28 21:56:39 -04:00
|
|
|
|
2016-07-06 21:23:59 +02:00
|
|
|
//Be safe, remove as many elements as possible
|
2017-08-28 20:31:07 +02:00
|
|
|
iqVisualQueue->flush();
|
2017-06-01 19:56:33 +02:00
|
|
|
iqDataInQueue->flush();
|
|
|
|
iqDataOutQueue->flush();
|
|
|
|
iqActiveDemodVisualQueue->flush();
|
2015-01-22 21:32:32 -05:00
|
|
|
|
2016-06-02 19:45:34 -04:00
|
|
|
// std::cout << "SDR post-processing thread done." << std::endl;
|
2014-11-29 13:58:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void SDRPostThread::terminate() {
|
2016-06-28 21:04:52 +02:00
|
|
|
IOThread::terminate();
|
2017-08-28 20:31:07 +02:00
|
|
|
//unblock push()
|
|
|
|
iqVisualQueue->flush();
|
|
|
|
iqDataInQueue->flush();
|
|
|
|
iqDataOutQueue->flush();
|
|
|
|
iqActiveDemodVisualQueue->flush();
|
2014-11-29 13:58:20 -05:00
|
|
|
}
|
2015-12-29 20:52:49 -05:00
|
|
|
|
|
|
|
void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) {
|
|
|
|
if (sampleRate != data_in->sampleRate) {
|
|
|
|
sampleRate = data_in->sampleRate;
|
2015-12-29 23:25:22 -05:00
|
|
|
numChannels = 1;
|
2015-12-29 20:52:49 -05:00
|
|
|
doRefresh.store(true);
|
|
|
|
}
|
|
|
|
|
2016-01-29 07:49:31 +11:00
|
|
|
size_t dataSize = data_in->data.size();
|
|
|
|
size_t outSize = data_in->data.size();
|
2015-12-29 20:52:49 -05:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2015-12-29 23:25:22 -05:00
|
|
|
|
2017-08-28 21:22:07 +02:00
|
|
|
size_t refCount = runDemods.size();
|
2017-08-27 12:39:28 +02:00
|
|
|
bool doIQDataOut = (iqDataOutQueue != nullptr && !iqDataOutQueue->full());
|
2017-08-28 21:22:07 +02:00
|
|
|
bool doDemodVisOut = (runDemods.size() > 0 && iqActiveDemodVisualQueue != nullptr && !iqActiveDemodVisualQueue->full());
|
2017-08-27 12:39:28 +02:00
|
|
|
bool doVisOut = (iqVisualQueue != nullptr && !iqVisualQueue->full());
|
2015-12-29 20:52:49 -05:00
|
|
|
|
|
|
|
if (doIQDataOut) {
|
|
|
|
refCount++;
|
|
|
|
}
|
|
|
|
if (doDemodVisOut) {
|
|
|
|
refCount++;
|
|
|
|
}
|
|
|
|
if (doVisOut) {
|
|
|
|
refCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (refCount) {
|
2017-05-24 12:24:36 +02:00
|
|
|
DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
|
|
|
|
|
2015-12-29 20:52:49 -05:00
|
|
|
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) {
|
2017-02-09 19:12:12 +01:00
|
|
|
//VSO: blocking push
|
2017-08-27 12:39:28 +02:00
|
|
|
iqActiveDemodVisualQueue->push(demodDataOut);
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (doIQDataOut) {
|
2017-02-09 19:12:12 +01:00
|
|
|
//VSO: blocking push
|
2017-08-27 12:39:28 +02:00
|
|
|
iqDataOutQueue->push(demodDataOut);
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (doVisOut) {
|
2017-02-09 19:12:12 +01:00
|
|
|
//VSO: blocking push
|
2017-08-27 12:39:28 +02:00
|
|
|
iqVisualQueue->push(demodDataOut);
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
|
|
|
|
2017-08-28 21:22:07 +02:00
|
|
|
for (size_t i = 0; i < runDemods.size(); i++) {
|
2017-09-02 16:24:25 +02:00
|
|
|
// try-push() : we do our best to only stimulate active demods, but some could happen to be dead, full, or indeed non-active.
|
|
|
|
//so in short never block here no matter what.
|
|
|
|
if (!runDemods[i]->getIQInputDataPipe()->try_push(demodDataOut)) {
|
|
|
|
|
2017-09-02 17:35:43 +02:00
|
|
|
// std::cout << "SDRPostThread::runSingleCH() attempt to push into demod '" << runDemods[i]->getLabel()
|
|
|
|
// << "' (" << runDemods[i]->getFrequency() << " Hz) failed, demod is either too busy, not-active, or dead..." << std::endl << std::flush;
|
2017-09-02 16:24:25 +02:00
|
|
|
std::this_thread::yield();
|
2017-08-27 12:39:28 +02:00
|
|
|
}
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
|
2018-02-14 21:24:22 -05:00
|
|
|
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate || chanMode != lastChanMode) {
|
2015-12-29 20:52:49 -05:00
|
|
|
numChannels = data_in->numChannels;
|
|
|
|
sampleRate = data_in->sampleRate;
|
|
|
|
initPFBChannelizer();
|
2018-02-14 21:24:22 -05:00
|
|
|
lastChanMode = 1;
|
2015-12-29 20:52:49 -05:00
|
|
|
doRefresh.store(true);
|
|
|
|
}
|
|
|
|
|
2016-01-29 07:49:31 +11:00
|
|
|
size_t dataSize = data_in->data.size();
|
|
|
|
size_t outSize = data_in->data.size();
|
2015-12-29 20:52:49 -05:00
|
|
|
|
|
|
|
if (outSize > dataOut.capacity()) {
|
|
|
|
dataOut.reserve(outSize);
|
|
|
|
}
|
|
|
|
if (outSize != dataOut.size()) {
|
|
|
|
dataOut.resize(outSize);
|
|
|
|
}
|
|
|
|
|
2017-08-26 23:34:48 +02:00
|
|
|
if (iqDataOutQueue != nullptr && !iqDataOutQueue->full()) {
|
2017-05-24 12:24:36 +02:00
|
|
|
DemodulatorThreadIQDataPtr iqDataOut = visualDataBuffers.getBuffer();
|
2015-12-29 20:52:49 -05:00
|
|
|
|
|
|
|
bool doVis = false;
|
|
|
|
|
2017-08-27 13:00:32 +02:00
|
|
|
if (iqVisualQueue != nullptr && !iqVisualQueue->full()) {
|
2015-12-29 20:52:49 -05:00
|
|
|
doVis = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
iqDataOut->frequency = data_in->frequency;
|
|
|
|
iqDataOut->sampleRate = data_in->sampleRate;
|
|
|
|
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
|
|
|
|
|
2017-02-09 19:12:12 +01:00
|
|
|
//VSO: blocking push
|
2017-08-27 12:39:28 +02:00
|
|
|
iqDataOutQueue->push(iqDataOut);
|
2017-02-09 19:12:12 +01:00
|
|
|
|
|
|
|
if (doVis) {
|
|
|
|
//VSO: blocking push
|
2017-08-27 12:39:28 +02:00
|
|
|
iqVisualQueue->push(iqDataOut);
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frequency != data_in->frequency) {
|
|
|
|
frequency = data_in->frequency;
|
|
|
|
doRefresh.store(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doRefresh.load()) {
|
|
|
|
updateActiveDemodulators();
|
|
|
|
updateChannels();
|
|
|
|
doRefresh.store(false);
|
|
|
|
}
|
|
|
|
|
2017-08-27 11:11:30 +02:00
|
|
|
DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
2015-12-29 20:52:49 -05:00
|
|
|
int activeDemodChannel = -1;
|
|
|
|
|
|
|
|
// Find active demodulators
|
2017-08-28 21:22:07 +02:00
|
|
|
if (runDemods.size() > 0) {
|
2015-12-29 20:52:49 -05:00
|
|
|
|
|
|
|
// 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
|
2017-08-28 21:22:07 +02:00
|
|
|
for (size_t i = 0; i < runDemods.size(); i++) {
|
2017-08-27 11:11:30 +02:00
|
|
|
DemodulatorInstancePtr demod = runDemods[i];
|
2015-12-29 20:52:49 -05:00
|
|
|
demodChannel[i] = getChannelAt(demod->getFrequency());
|
|
|
|
if (demod == activeDemod) {
|
|
|
|
activeDemodChannel = demodChannel[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-28 21:22:07 +02:00
|
|
|
for (size_t i = 0; i < runDemods.size(); i++) {
|
2015-12-29 20:52:49 -05:00
|
|
|
// cache channel usage refcounts
|
|
|
|
if (demodChannel[i] >= 0) {
|
|
|
|
demodChannelActive[demodChannel[i]]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run channels
|
|
|
|
for (int i = 0; i < numChannels+1; i++) {
|
2017-08-27 13:00:32 +02:00
|
|
|
int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != nullptr) && !iqActiveDemodVisualQueue->full())?1:0;
|
2015-12-29 20:52:49 -05:00
|
|
|
|
|
|
|
if (!doDemodVis && demodChannelActive[i] == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-05-24 12:24:36 +02:00
|
|
|
DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
|
2015-12-29 20:52:49 -05:00
|
|
|
demodDataOut->frequency = chanCenters[i];
|
|
|
|
demodDataOut->sampleRate = chanBw;
|
|
|
|
|
|
|
|
// Calculate channel buffer size
|
2016-01-29 07:49:31 +11:00
|
|
|
size_t chanDataSize = (outSize/numChannels);
|
2015-12-29 20:52:49 -05:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2016-01-29 07:49:31 +11:00
|
|
|
for (size_t j = 0; j < chanDataSize; j++) {
|
2015-12-29 20:52:49 -05:00
|
|
|
dcBuf[j] = dataOut[idx];
|
|
|
|
idx += numChannels;
|
|
|
|
}
|
|
|
|
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
|
|
|
|
} else {
|
2016-01-29 07:49:31 +11:00
|
|
|
for (size_t j = 0; j < chanDataSize; j++) {
|
2015-12-29 20:52:49 -05:00
|
|
|
demodDataOut->data[j] = dataOut[idx];
|
|
|
|
idx += numChannels;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doDemodVis) {
|
2017-02-09 19:12:12 +01:00
|
|
|
//VSO: blocking push
|
2017-08-27 12:39:28 +02:00
|
|
|
iqActiveDemodVisualQueue->push(demodDataOut);
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
|
|
|
|
2017-08-28 21:22:07 +02:00
|
|
|
for (size_t j = 0; j < runDemods.size(); j++) {
|
2015-12-29 20:52:49 -05:00
|
|
|
if (demodChannel[j] == i) {
|
2017-08-27 11:11:30 +02:00
|
|
|
|
2017-09-02 16:24:25 +02:00
|
|
|
// try-push() : we do our best to only stimulate active demods, but some could happen to be dead, full, or indeed non-active.
|
|
|
|
//so in short never block here no matter what.
|
|
|
|
if (!runDemods[j]->getIQInputDataPipe()->try_push(demodDataOut)) {
|
2017-09-02 17:35:43 +02:00
|
|
|
// std::cout << "SDRPostThread::runPFBCH() attempt to push into demod '" << runDemods[i]->getLabel()
|
|
|
|
// << "' (" << runDemods[i]->getFrequency() << " Hz) failed, demod is either too busy, not-active, or dead..." << std::endl << std::flush;
|
2017-09-02 16:24:25 +02:00
|
|
|
std::this_thread::yield();
|
2017-08-27 12:39:28 +02:00
|
|
|
}
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
2017-08-27 12:39:28 +02:00
|
|
|
} //end for
|
2015-12-29 20:52:49 -05:00
|
|
|
}
|
|
|
|
}
|
2016-01-29 07:49:31 +11:00
|
|
|
}
|
2018-02-14 21:24:22 -05:00
|
|
|
|
|
|
|
|
|
|
|
void SDRPostThread::initPFBChannelizer2() {
|
|
|
|
// std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl;
|
|
|
|
if (channelizer2) {
|
|
|
|
firpfbch2_crcf_destroy(channelizer2);
|
|
|
|
}
|
|
|
|
channelizer2 = firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 4, 60);
|
|
|
|
|
|
|
|
chanBw = (sampleRate / numChannels);
|
|
|
|
|
|
|
|
chanCenters.resize(numChannels+1);
|
|
|
|
demodChannelActive.resize(numChannels+1);
|
|
|
|
|
|
|
|
// std::cout << "Channel bandwidth spacing: " << (chanBw) << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SDRPostThread::runPFBCH2(SDRThreadIQData *data_in) {
|
|
|
|
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate || chanMode != lastChanMode) {
|
|
|
|
numChannels = data_in->numChannels;
|
|
|
|
sampleRate = data_in->sampleRate;
|
|
|
|
initPFBChannelizer2();
|
|
|
|
lastChanMode = 2;
|
|
|
|
doRefresh.store(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t dataSize = data_in->data.size();
|
|
|
|
size_t outSize = data_in->data.size();
|
|
|
|
|
|
|
|
if (outSize > dataOut.capacity()) {
|
|
|
|
dataOut.reserve(outSize);
|
|
|
|
}
|
|
|
|
if (outSize != dataOut.size() * 2) {
|
|
|
|
dataOut.resize(outSize * 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iqDataOutQueue != nullptr && !iqDataOutQueue->full()) {
|
|
|
|
DemodulatorThreadIQDataPtr iqDataOut = visualDataBuffers.getBuffer();
|
|
|
|
|
|
|
|
bool doVis = false;
|
|
|
|
|
|
|
|
if (iqVisualQueue != nullptr && !iqVisualQueue->full()) {
|
|
|
|
doVis = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
iqDataOut->frequency = data_in->frequency;
|
|
|
|
iqDataOut->sampleRate = data_in->sampleRate;
|
|
|
|
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
|
|
|
|
|
|
|
|
//VSO: blocking push
|
|
|
|
iqDataOutQueue->push(iqDataOut);
|
|
|
|
|
|
|
|
if (doVis) {
|
|
|
|
//VSO: blocking push
|
|
|
|
iqVisualQueue->push(iqDataOut);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frequency != data_in->frequency) {
|
|
|
|
frequency = data_in->frequency;
|
|
|
|
doRefresh.store(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doRefresh.load()) {
|
|
|
|
updateActiveDemodulators();
|
|
|
|
updateChannels();
|
|
|
|
doRefresh.store(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
|
|
|
int activeDemodChannel = -1;
|
|
|
|
|
|
|
|
// Find active demodulators
|
|
|
|
if (runDemods.size() > 0) {
|
|
|
|
|
|
|
|
// channelize data
|
|
|
|
// firpfbch2 produces M output for every M/2 input
|
|
|
|
for (int i = 0, iMax = dataSize; i < iMax; i += numChannels/2) {
|
|
|
|
firpfbch2_crcf_execute(channelizer2, &data_in->data[i], &dataOut[i*2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0, iMax = numChannels+1; i < iMax; i++) {
|
|
|
|
demodChannelActive[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find nearest channel for each demodulator
|
|
|
|
for (size_t i = 0; i < runDemods.size(); i++) {
|
|
|
|
DemodulatorInstancePtr demod = runDemods[i];
|
|
|
|
demodChannel[i] = getChannelAt(demod->getFrequency());
|
|
|
|
if (demod == activeDemod) {
|
|
|
|
activeDemodChannel = demodChannel[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < runDemods.size(); 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 != nullptr) && !iqActiveDemodVisualQueue->full())?1:0;
|
|
|
|
|
|
|
|
if (!doDemodVis && demodChannelActive[i] == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
|
|
|
|
demodDataOut->frequency = chanCenters[i];
|
|
|
|
demodDataOut->sampleRate = chanBw * 2;
|
|
|
|
|
|
|
|
// Calculate channel buffer size
|
|
|
|
size_t chanDataSize = (outSize/numChannels);
|
|
|
|
|
|
|
|
if (demodDataOut->data.size() != chanDataSize * 2) {
|
|
|
|
if (demodDataOut->data.capacity() < chanDataSize * 2) {
|
|
|
|
demodDataOut->data.reserve(chanDataSize * 2);
|
|
|
|
}
|
|
|
|
demodDataOut->data.resize(chanDataSize * 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 * 2) {
|
|
|
|
dcBuf.resize(chanDataSize * 2);
|
|
|
|
}
|
|
|
|
for (size_t j = 0; j < chanDataSize*2; j++) {
|
|
|
|
dcBuf[j] = dataOut[idx];
|
|
|
|
idx += numChannels;
|
|
|
|
}
|
|
|
|
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize * 2, &demodDataOut->data[0]);
|
|
|
|
} else {
|
|
|
|
for (size_t j = 0; j < chanDataSize*2; j++) {
|
|
|
|
demodDataOut->data[j] = dataOut[idx];
|
|
|
|
idx += numChannels;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doDemodVis) {
|
|
|
|
//VSO: blocking push
|
|
|
|
iqActiveDemodVisualQueue->push(demodDataOut);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t j = 0; j < runDemods.size(); j++) {
|
|
|
|
if (demodChannel[j] == i) {
|
|
|
|
|
|
|
|
// try-push() : we do our best to only stimulate active demods, but some could happen to be dead, full, or indeed non-active.
|
|
|
|
//so in short never block here no matter what.
|
|
|
|
if (!runDemods[j]->getIQInputDataPipe()->try_push(demodDataOut)) {
|
|
|
|
// std::cout << "SDRPostThread::runPFBCH() attempt to push into demod '" << runDemods[i]->getLabel()
|
|
|
|
// << "' (" << runDemods[i]->getFrequency() << " Hz) failed, demod is either too busy, not-active, or dead..." << std::endl << std::flush;
|
|
|
|
std::this_thread::yield();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} //end for
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|