mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2025-02-03 09:44:26 -05:00
Merge pull request #651 from cjcliffe/firpbch2_mode
Firpbch2 support and SDRPostThread clean-up
This commit is contained in:
commit
462da6cdf7
@ -2252,7 +2252,6 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wproc->setView(waterfallCanvas->getViewState(), waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth());
|
wproc->setView(waterfallCanvas->getViewState(), waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth());
|
||||||
wxGetApp().getSDRPostThread()->setIQVisualRange(waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth());
|
|
||||||
|
|
||||||
proc->setView(wproc->isView(), wproc->getCenterFrequency(), wproc->getBandwidth());
|
proc->setView(wproc->isView(), wproc->getCenterFrequency(), wproc->getBandwidth());
|
||||||
|
|
||||||
|
@ -19,40 +19,29 @@ SDRPostThread::SDRPostThread() : IOThread(), buffers("SDRPostThreadBuffers"), vi
|
|||||||
|
|
||||||
numChannels = 0;
|
numChannels = 0;
|
||||||
channelizer = nullptr;
|
channelizer = nullptr;
|
||||||
|
channelizer2 = nullptr;
|
||||||
|
|
||||||
|
// Channel mode default temporary for testing
|
||||||
|
chanMode = 1;
|
||||||
|
lastChanMode = 0;
|
||||||
|
|
||||||
sampleRate = 0;
|
sampleRate = 0;
|
||||||
|
|
||||||
visFrequency.store(0);
|
|
||||||
visBandwidth.store(0);
|
|
||||||
|
|
||||||
doRefresh.store(false);
|
doRefresh.store(false);
|
||||||
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005f);
|
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SDRPostThread::~SDRPostThread() {
|
SDRPostThread::~SDRPostThread() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SDRPostThread::notifyDemodulatorsChanged() {
|
void SDRPostThread::notifyDemodulatorsChanged() {
|
||||||
|
|
||||||
doRefresh.store(true);
|
doRefresh.store(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDRPostThread::initPFBChannelizer() {
|
|
||||||
// std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl;
|
|
||||||
if (channelizer) {
|
|
||||||
firpfbch_crcf_destroy(channelizer);
|
|
||||||
}
|
|
||||||
channelizer = firpfbch_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Update the active list of demodulators for handling
|
||||||
void SDRPostThread::updateActiveDemodulators() {
|
void SDRPostThread::updateActiveDemodulators() {
|
||||||
// In range?
|
// In range?
|
||||||
|
|
||||||
@ -111,8 +100,8 @@ void SDRPostThread::updateActiveDemodulators() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SDRPostThread::resetAllDemodulators() {
|
void SDRPostThread::resetAllDemodulators() {
|
||||||
|
|
||||||
//retreive the current list of demodulators:
|
//retreive the current list of demodulators:
|
||||||
auto demodulators = wxGetApp().getDemodMgr().getDemodulators();
|
auto demodulators = wxGetApp().getDemodMgr().getDemodulators();
|
||||||
|
|
||||||
@ -122,9 +111,11 @@ void SDRPostThread::resetAllDemodulators() {
|
|||||||
demod->getIQInputDataPipe()->flush();
|
demod->getIQInputDataPipe()->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
doRefresh = true;
|
doRefresh.store(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Update the channel positions and frequencies
|
||||||
void SDRPostThread::updateChannels() {
|
void SDRPostThread::updateChannels() {
|
||||||
// calculate channel center frequencies, todo: cache
|
// calculate channel center frequencies, todo: cache
|
||||||
for (int i = 0; i < numChannels/2; i++) {
|
for (int i = 0; i < numChannels/2; i++) {
|
||||||
@ -135,6 +126,8 @@ void SDRPostThread::updateChannels() {
|
|||||||
chanCenters[numChannels] = frequency + (sampleRate/2);
|
chanCenters[numChannels] = frequency + (sampleRate/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Find the channelizer channel that corresponds to the given frequency
|
||||||
int SDRPostThread::getChannelAt(long long frequency) {
|
int SDRPostThread::getChannelAt(long long frequency) {
|
||||||
int chan = -1;
|
int chan = -1;
|
||||||
long long minDelta = sampleRate;
|
long long minDelta = sampleRate;
|
||||||
@ -148,11 +141,17 @@ int SDRPostThread::getChannelAt(long long frequency) {
|
|||||||
return chan;
|
return chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDRPostThread::setIQVisualRange(long long frequency, int bandwidth) {
|
|
||||||
visFrequency.store(frequency);
|
void SDRPostThread::setChannelizerType(SDRPostThreadChannelizerType chType) {
|
||||||
visBandwidth.store(bandwidth);
|
chanMode.store((int)chType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SDRPostThreadChannelizerType SDRPostThread::getChannelizerType() {
|
||||||
|
return (SDRPostThreadChannelizerType) chanMode.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SDRPostThread::run() {
|
void SDRPostThread::run() {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
pthread_t tID = pthread_self(); // ID of this thread
|
pthread_t tID = pthread_self(); // ID of this thread
|
||||||
@ -178,8 +177,15 @@ void SDRPostThread::run() {
|
|||||||
bool doUpdate = false;
|
bool doUpdate = false;
|
||||||
|
|
||||||
if (data_in && data_in->data.size()) {
|
if (data_in && data_in->data.size()) {
|
||||||
|
|
||||||
|
pushVisualData(data_in.get());
|
||||||
|
|
||||||
if(data_in->numChannels > 1) {
|
if(data_in->numChannels > 1) {
|
||||||
runPFBCH(data_in.get());
|
if (chanMode == 1) {
|
||||||
|
runPFBCH(data_in.get());
|
||||||
|
} else if (chanMode == 2) {
|
||||||
|
runPFBCH2(data_in.get());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
runSingleCH(data_in.get());
|
runSingleCH(data_in.get());
|
||||||
}
|
}
|
||||||
@ -193,8 +199,9 @@ void SDRPostThread::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Only update the list of demodulators here
|
//Only update the list of demodulators here
|
||||||
if (doUpdate || doRefresh) {
|
if (doUpdate || doRefresh.load()) {
|
||||||
updateActiveDemodulators();
|
updateActiveDemodulators();
|
||||||
|
doRefresh.store(false);
|
||||||
}
|
}
|
||||||
} //end while
|
} //end while
|
||||||
|
|
||||||
@ -216,109 +223,8 @@ void SDRPostThread::terminate() {
|
|||||||
iqActiveDemodVisualQueue->flush();
|
iqActiveDemodVisualQueue->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) {
|
// Push visual data; i.e. Main Waterfall (all frames) and Spectrum (active frame)
|
||||||
if (sampleRate != data_in->sampleRate) {
|
void SDRPostThread::pushVisualData(SDRThreadIQData *data_in) {
|
||||||
sampleRate = data_in->sampleRate;
|
|
||||||
numChannels = 1;
|
|
||||||
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()) {
|
|
||||||
dataOut.resize(outSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frequency != data_in->frequency) {
|
|
||||||
frequency = data_in->frequency;
|
|
||||||
doRefresh.store(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doRefresh.load()) {
|
|
||||||
updateActiveDemodulators();
|
|
||||||
doRefresh.store(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t refCount = runDemods.size();
|
|
||||||
bool doIQDataOut = (iqDataOutQueue != nullptr && !iqDataOutQueue->full());
|
|
||||||
bool doDemodVisOut = (runDemods.size() > 0 && iqActiveDemodVisualQueue != nullptr && !iqActiveDemodVisualQueue->full());
|
|
||||||
bool doVisOut = (iqVisualQueue != nullptr && !iqVisualQueue->full());
|
|
||||||
|
|
||||||
if (doIQDataOut) {
|
|
||||||
refCount++;
|
|
||||||
}
|
|
||||||
if (doDemodVisOut) {
|
|
||||||
refCount++;
|
|
||||||
}
|
|
||||||
if (doVisOut) {
|
|
||||||
refCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (refCount) {
|
|
||||||
DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
|
|
||||||
|
|
||||||
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) {
|
|
||||||
//VSO: blocking push
|
|
||||||
iqActiveDemodVisualQueue->push(demodDataOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doIQDataOut) {
|
|
||||||
//VSO: blocking push
|
|
||||||
iqDataOutQueue->push(demodDataOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doVisOut) {
|
|
||||||
//VSO: blocking push
|
|
||||||
iqVisualQueue->push(demodDataOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < runDemods.size(); 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[i]->getIQInputDataPipe()->try_push(demodDataOut)) {
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
|
|
||||||
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) {
|
|
||||||
numChannels = data_in->numChannels;
|
|
||||||
sampleRate = data_in->sampleRate;
|
|
||||||
initPFBChannelizer();
|
|
||||||
doRefresh.store(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
dataOut.resize(outSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iqDataOutQueue != nullptr && !iqDataOutQueue->full()) {
|
if (iqDataOutQueue != nullptr && !iqDataOutQueue->full()) {
|
||||||
DemodulatorThreadIQDataPtr iqDataOut = visualDataBuffers.getBuffer();
|
DemodulatorThreadIQDataPtr iqDataOut = visualDataBuffers.getBuffer();
|
||||||
|
|
||||||
@ -330,124 +236,287 @@ void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
|
|||||||
|
|
||||||
iqDataOut->frequency = data_in->frequency;
|
iqDataOut->frequency = data_in->frequency;
|
||||||
iqDataOut->sampleRate = data_in->sampleRate;
|
iqDataOut->sampleRate = data_in->sampleRate;
|
||||||
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
|
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + data_in->data.size());
|
||||||
|
|
||||||
//VSO: blocking push
|
//VSO: blocking push
|
||||||
iqDataOutQueue->push(iqDataOut);
|
iqDataOutQueue->push(iqDataOut);
|
||||||
|
|
||||||
if (doVis) {
|
if (doVis) {
|
||||||
//VSO: blocking push
|
//VSO: blocking push
|
||||||
iqVisualQueue->push(iqDataOut);
|
iqVisualQueue->push(iqDataOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run without any processing; each demod gets the full SDR bandwidth to handle on it's own
|
||||||
|
void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) {
|
||||||
|
bool refreshed = false;
|
||||||
|
|
||||||
if (frequency != data_in->frequency) {
|
if (sampleRate != data_in->sampleRate || doRefresh.load()) {
|
||||||
|
sampleRate = data_in->sampleRate;
|
||||||
|
numChannels = 1;
|
||||||
|
refreshed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshed || frequency != data_in->frequency) {
|
||||||
frequency = data_in->frequency;
|
frequency = data_in->frequency;
|
||||||
doRefresh.store(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doRefresh.load()) {
|
|
||||||
updateActiveDemodulators();
|
updateActiveDemodulators();
|
||||||
updateChannels();
|
|
||||||
doRefresh.store(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
size_t outSize = data_in->data.size();
|
||||||
int activeDemodChannel = -1;
|
|
||||||
|
|
||||||
// Find active demodulators
|
if (outSize > dataOut.capacity()) {
|
||||||
if (runDemods.size() > 0) {
|
dataOut.reserve(outSize);
|
||||||
|
}
|
||||||
// channelize data
|
if (outSize != dataOut.size()) {
|
||||||
// firpfbch output rate is (input rate / channels)
|
dataOut.resize(outSize);
|
||||||
for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) {
|
}
|
||||||
firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]);
|
|
||||||
|
DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
|
||||||
|
|
||||||
|
demodDataOut->frequency = frequency;
|
||||||
|
demodDataOut->sampleRate = sampleRate;
|
||||||
|
|
||||||
|
if (demodDataOut->data.size() != outSize) {
|
||||||
|
if (demodDataOut->data.capacity() < outSize) {
|
||||||
|
demodDataOut->data.reserve(outSize);
|
||||||
}
|
}
|
||||||
|
demodDataOut->data.resize(outSize);
|
||||||
for (int i = 0, iMax = numChannels+1; i < iMax; i++) {
|
}
|
||||||
demodChannelActive[i] = 0;
|
|
||||||
}
|
iirfilt_crcf_execute_block(dcFilter, &data_in->data[0], data_in->data.size(), &demodDataOut->data[0]);
|
||||||
|
|
||||||
// Find nearest channel for each demodulator
|
if (runDemods.size() > 0 && iqActiveDemodVisualQueue != nullptr && !iqActiveDemodVisualQueue->full()) {
|
||||||
for (size_t i = 0; i < runDemods.size(); i++) {
|
//VSO: blocking push
|
||||||
DemodulatorInstancePtr demod = runDemods[i];
|
iqActiveDemodVisualQueue->push(demodDataOut);
|
||||||
demodChannel[i] = getChannelAt(demod->getFrequency());
|
}
|
||||||
if (demod == activeDemod) {
|
|
||||||
activeDemodChannel = demodChannel[i];
|
if (iqDataOutQueue != nullptr && !iqDataOutQueue->full()) {
|
||||||
}
|
//VSO: blocking push
|
||||||
}
|
iqDataOutQueue->push(demodDataOut);
|
||||||
|
}
|
||||||
for (size_t i = 0; i < runDemods.size(); i++) {
|
|
||||||
// cache channel usage refcounts
|
if (iqVisualQueue != nullptr && !iqVisualQueue->full()) {
|
||||||
if (demodChannel[i] >= 0) {
|
//VSO: blocking push
|
||||||
demodChannelActive[demodChannel[i]]++;
|
iqVisualQueue->push(demodDataOut);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
for (size_t i = 0; i < runDemods.size(); i++) {
|
||||||
// Run channels
|
// try-push() : we do our best to only stimulate active demods, but some could happen to be dead, full, or indeed non-active.
|
||||||
for (int i = 0; i < numChannels+1; i++) {
|
//so in short never block here no matter what.
|
||||||
int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != nullptr) && !iqActiveDemodVisualQueue->full())?1:0;
|
if (!runDemods[i]->getIQInputDataPipe()->try_push(demodDataOut)) {
|
||||||
|
|
||||||
if (!doDemodVis && demodChannelActive[i] == 0) {
|
// std::cout << "SDRPostThread::runSingleCH() attempt to push into demod '" << runDemods[i]->getLabel()
|
||||||
continue;
|
// << "' (" << runDemods[i]->getFrequency() << " Hz) failed, demod is either too busy, not-active, or dead..." << std::endl << std::flush;
|
||||||
}
|
std::this_thread::yield();
|
||||||
|
|
||||||
DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
|
|
||||||
demodDataOut->frequency = chanCenters[i];
|
|
||||||
demodDataOut->sampleRate = chanBw;
|
|
||||||
|
|
||||||
// Calculate channel buffer size
|
|
||||||
size_t chanDataSize = (outSize/numChannels);
|
|
||||||
|
|
||||||
if (demodDataOut->data.size() != chanDataSize) {
|
|
||||||
if (demodDataOut->data.capacity() < chanDataSize) {
|
|
||||||
demodDataOut->data.reserve(chanDataSize);
|
|
||||||
}
|
|
||||||
demodDataOut->data.resize(chanDataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
int idx = i;
|
|
||||||
|
|
||||||
// Extra channel wraps lower side band of lowest channel
|
|
||||||
// to fix frequency gap on upper side of spectrum
|
|
||||||
if (i == numChannels) {
|
|
||||||
idx = (numChannels/2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare channel data buffer
|
|
||||||
if (i == 0) { // Channel 0 requires DC correction
|
|
||||||
if (dcBuf.size() != chanDataSize) {
|
|
||||||
dcBuf.resize(chanDataSize);
|
|
||||||
}
|
|
||||||
for (size_t j = 0; j < chanDataSize; j++) {
|
|
||||||
dcBuf[j] = dataOut[idx];
|
|
||||||
idx += numChannels;
|
|
||||||
}
|
|
||||||
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
|
|
||||||
} else {
|
|
||||||
for (size_t j = 0; j < chanDataSize; 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Handle active channels, channel 0 offset correction, de-interlacing and push data to demodulators
|
||||||
|
void SDRPostThread::runDemodChannels(int channelBandwidth) {
|
||||||
|
DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||||
|
|
||||||
|
// Calculate channel data size
|
||||||
|
size_t chanDataSize = dataOut.size()/numChannels;
|
||||||
|
|
||||||
|
// Channel for the 'active' demod that's displaying visual data
|
||||||
|
int activeDemodChannel = -1;
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count the demods per-channel
|
||||||
|
for (size_t i = 0; i < runDemods.size(); i++) {
|
||||||
|
if (demodChannel[i] >= 0) {
|
||||||
|
demodChannelActive[demodChannel[i]]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run channels
|
||||||
|
for (int i = 0; i < numChannels+1; i++) {
|
||||||
|
bool doDemodVis = (activeDemodChannel == i) && (iqActiveDemodVisualQueue != nullptr) && !iqActiveDemodVisualQueue->full();
|
||||||
|
|
||||||
|
if (!doDemodVis && demodChannelActive[i] == 0) {
|
||||||
|
// Nothing to do for this channel? continue.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a channel buffer
|
||||||
|
DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
|
||||||
|
demodDataOut->frequency = chanCenters[i];
|
||||||
|
demodDataOut->sampleRate = channelBandwidth;
|
||||||
|
|
||||||
|
// Resize and update capacity of buffer if necessary
|
||||||
|
if (demodDataOut->data.size() != chanDataSize) {
|
||||||
|
if (demodDataOut->data.capacity() < chanDataSize) {
|
||||||
|
demodDataOut->data.reserve(chanDataSize);
|
||||||
|
}
|
||||||
|
demodDataOut->data.resize(chanDataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start copying interleaved data at given channel index
|
||||||
|
int idx = i;
|
||||||
|
|
||||||
|
// Extra channel wraps left side band of lowest channel
|
||||||
|
// to fix frequency gap on right side of spectrum
|
||||||
|
if (i == numChannels) {
|
||||||
|
idx = (numChannels/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare channel data buffer
|
||||||
|
if (i == 0) { // Channel 0 requires DC correction
|
||||||
|
// Update DC Buffer size if needed
|
||||||
|
if (dcBuf.size() != chanDataSize) {
|
||||||
|
dcBuf.resize(chanDataSize);
|
||||||
|
}
|
||||||
|
// Copy interleaved channel data to dc buffer
|
||||||
|
for (size_t j = 0; j < chanDataSize; j++) {
|
||||||
|
dcBuf[j] = dataOut[idx];
|
||||||
|
idx += numChannels;
|
||||||
|
}
|
||||||
|
// Run DC Filter from dcBuf to demod output buffer
|
||||||
|
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
|
||||||
|
} else {
|
||||||
|
// Copy interleaved channel data to demod output buffer
|
||||||
|
for (size_t j = 0; j < chanDataSize; 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SDRPostThread::initPFBCH() {
|
||||||
|
// std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl;
|
||||||
|
if (channelizer) {
|
||||||
|
firpfbch_crcf_destroy(channelizer);
|
||||||
|
}
|
||||||
|
channelizer = firpfbch_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::runPFBCH(SDRThreadIQData *data_in) {
|
||||||
|
bool refreshed = false;
|
||||||
|
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate || chanMode != lastChanMode || doRefresh.load()) {
|
||||||
|
numChannels = data_in->numChannels;
|
||||||
|
sampleRate = data_in->sampleRate;
|
||||||
|
initPFBCH();
|
||||||
|
lastChanMode = 1;
|
||||||
|
refreshed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshed || frequency != data_in->frequency) {
|
||||||
|
frequency = data_in->frequency;
|
||||||
|
updateActiveDemodulators();
|
||||||
|
updateChannels();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t outSize = data_in->data.size();
|
||||||
|
|
||||||
|
if (outSize > dataOut.capacity()) {
|
||||||
|
dataOut.reserve(outSize);
|
||||||
|
}
|
||||||
|
if (outSize != dataOut.size()) {
|
||||||
|
dataOut.resize(outSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find active demodulators
|
||||||
|
if (runDemods.size() > 0) {
|
||||||
|
// Channelize data
|
||||||
|
// firpfbch produces [numChannels] interleaved output samples for every [numChannels] samples
|
||||||
|
for (int i = 0, iMax = data_in->data.size(); i < iMax; i+=numChannels) {
|
||||||
|
firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
runDemodChannels(chanBw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SDRPostThread::initPFBCH2() {
|
||||||
|
// 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) {
|
||||||
|
bool refreshed = false;
|
||||||
|
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate || chanMode != lastChanMode || doRefresh.load()) {
|
||||||
|
numChannels = data_in->numChannels;
|
||||||
|
sampleRate = data_in->sampleRate;
|
||||||
|
initPFBCH2();
|
||||||
|
lastChanMode = 2;
|
||||||
|
refreshed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshed || frequency != data_in->frequency) {
|
||||||
|
frequency = data_in->frequency;
|
||||||
|
updateActiveDemodulators();
|
||||||
|
updateChannels();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t outSize = data_in->data.size() * 2;
|
||||||
|
|
||||||
|
if (outSize > dataOut.capacity()) {
|
||||||
|
dataOut.reserve(outSize);
|
||||||
|
}
|
||||||
|
if (outSize != dataOut.size()) {
|
||||||
|
dataOut.resize(outSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find active demodulators
|
||||||
|
if (runDemods.size() > 0) {
|
||||||
|
// Channelize data
|
||||||
|
// firpfbch2 produces [numChannels] interleaved output samples for every [numChannels/2] input samples
|
||||||
|
for (int i = 0, iMax = data_in->data.size(); i < iMax; i += numChannels/2) {
|
||||||
|
firpfbch2_crcf_execute(channelizer2, &data_in->data[i], &dataOut[i*2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
runDemodChannels(chanBw * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
#include "SoapySDRThread.h"
|
#include "SoapySDRThread.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
enum SDRPostThreadChannelizerType {
|
||||||
|
SDRPostPFBCH = 1,
|
||||||
|
SDRPostPFBCH2 = 2
|
||||||
|
};
|
||||||
|
|
||||||
class SDRPostThread : public IOThread {
|
class SDRPostThread : public IOThread {
|
||||||
public:
|
public:
|
||||||
SDRPostThread();
|
SDRPostThread();
|
||||||
@ -16,10 +21,12 @@ public:
|
|||||||
virtual void run();
|
virtual void run();
|
||||||
virtual void terminate();
|
virtual void terminate();
|
||||||
|
|
||||||
void runSingleCH(SDRThreadIQData *data_in);
|
void resetAllDemodulators();
|
||||||
void runPFBCH(SDRThreadIQData *data_in);
|
|
||||||
void setIQVisualRange(long long frequency, int bandwidth);
|
void setChannelizerType(SDRPostThreadChannelizerType chType);
|
||||||
|
SDRPostThreadChannelizerType getChannelizerType();
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SDRThreadIQDataQueuePtr iqDataInQueue;
|
SDRThreadIQDataQueuePtr iqDataInQueue;
|
||||||
DemodulatorThreadInputQueuePtr iqDataOutQueue;
|
DemodulatorThreadInputQueuePtr iqDataOutQueue;
|
||||||
@ -28,15 +35,22 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void initPFBChannelizer();
|
void pushVisualData(SDRThreadIQData *data_in);
|
||||||
|
void runSingleCH(SDRThreadIQData *data_in);
|
||||||
|
|
||||||
|
void runDemodChannels(int channelBandwidth);
|
||||||
|
|
||||||
|
void initPFBCH();
|
||||||
|
void runPFBCH(SDRThreadIQData *data_in);
|
||||||
|
|
||||||
|
void initPFBCH2();
|
||||||
|
void runPFBCH2(SDRThreadIQData *data_in);
|
||||||
|
|
||||||
void updateActiveDemodulators();
|
void updateActiveDemodulators();
|
||||||
void updateChannels();
|
void updateChannels();
|
||||||
int getChannelAt(long long frequency);
|
int getChannelAt(long long frequency);
|
||||||
|
|
||||||
void resetAllDemodulators();
|
|
||||||
|
|
||||||
ReBuffer<DemodulatorThreadIQData> buffers;
|
ReBuffer<DemodulatorThreadIQData> buffers;
|
||||||
std::vector<liquid_float_complex> fpData;
|
|
||||||
std::vector<liquid_float_complex> dataOut;
|
std::vector<liquid_float_complex> dataOut;
|
||||||
std::vector<long long> chanCenters;
|
std::vector<long long> chanCenters;
|
||||||
long long chanBw = 0;
|
long long chanBw = 0;
|
||||||
@ -47,11 +61,12 @@ private:
|
|||||||
|
|
||||||
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
||||||
atomic_bool doRefresh;
|
atomic_bool doRefresh;
|
||||||
atomic_llong visFrequency;
|
atomic_int chanMode;
|
||||||
atomic_int visBandwidth;
|
|
||||||
int numChannels, sampleRate;
|
int numChannels, sampleRate, lastChanMode;
|
||||||
long long frequency;
|
long long frequency;
|
||||||
firpfbch_crcf channelizer;
|
firpfbch_crcf channelizer;
|
||||||
|
firpfbch2_crcf channelizer2;
|
||||||
iirfilt_crcf dcFilter;
|
iirfilt_crcf dcFilter;
|
||||||
std::vector<liquid_float_complex> dcBuf;
|
std::vector<liquid_float_complex> dcBuf;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user