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>
|
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
SDRPostThread::SDRPostThread() : IOThread() {
|
|
|
|
iqDataInQueue = NULL;
|
|
|
|
iqDataOutQueue = NULL;
|
|
|
|
iqVisualQueue = NULL;
|
|
|
|
|
|
|
|
swapIQ.store(false);
|
2015-10-14 00:54:48 -04:00
|
|
|
numChannels = 0;
|
|
|
|
channelizer = NULL;
|
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
|
|
|
nRunDemods = 0;
|
|
|
|
|
|
|
|
doRefresh.store(false);
|
2015-10-17 16:17:12 -04:00
|
|
|
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
|
2014-11-29 13:58:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
SDRPostThread::~SDRPostThread() {
|
2014-12-11 19:07:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) {
|
2015-05-31 12:05:45 -04:00
|
|
|
busy_demod.lock();
|
|
|
|
demodulators.push_back(demod);
|
2015-10-14 18:09:29 -04:00
|
|
|
doRefresh.store(true);
|
2015-05-31 12:05:45 -04:00
|
|
|
busy_demod.unlock();
|
2014-12-11 19:07:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void SDRPostThread::removeDemodulator(DemodulatorInstance *demod) {
|
|
|
|
if (!demod) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-31 12:05:45 -04:00
|
|
|
busy_demod.lock();
|
|
|
|
std::vector<DemodulatorInstance *>::iterator i = std::find(demodulators.begin(), demodulators.end(), demod);
|
|
|
|
|
|
|
|
if (i != demodulators.end()) {
|
|
|
|
demodulators.erase(i);
|
2015-10-14 18:09:29 -04:00
|
|
|
doRefresh.store(true);
|
2015-05-31 12:05:45 -04:00
|
|
|
}
|
|
|
|
busy_demod.unlock();
|
2014-12-11 19:07:21 -05:00
|
|
|
}
|
|
|
|
|
2015-05-31 22:13:14 -04:00
|
|
|
void SDRPostThread::setSwapIQ(bool swapIQ) {
|
|
|
|
this->swapIQ.store(swapIQ);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SDRPostThread::getSwapIQ() {
|
|
|
|
return this->swapIQ.load();
|
|
|
|
}
|
|
|
|
|
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?
|
|
|
|
std::vector<DemodulatorInstance *>::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++) {
|
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-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
|
|
|
|
2014-11-30 17:11:29 -05:00
|
|
|
std::cout << "SDR post-processing thread started.." << std::endl;
|
|
|
|
|
2015-07-30 19:30:46 -04:00
|
|
|
iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput");
|
|
|
|
iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput");
|
2015-08-11 00:50:43 -04:00
|
|
|
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput");
|
2015-10-17 16:17:12 -04:00
|
|
|
iqActiveDemodVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQActiveDemodVisualDataOutput");
|
2015-10-14 00:54:48 -04:00
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
iqDataInQueue->set_max_num_items(0);
|
2015-10-17 16:17:12 -04:00
|
|
|
|
|
|
|
std::vector<liquid_float_complex> dcBuf;
|
2015-08-12 21:45:02 -04:00
|
|
|
|
2014-11-29 13:58:20 -05:00
|
|
|
while (!terminated) {
|
2014-12-23 01:59:03 -05:00
|
|
|
SDRThreadIQData *data_in;
|
2015-08-11 00:50:43 -04:00
|
|
|
|
2015-07-30 19:30:46 -04:00
|
|
|
iqDataInQueue->pop(data_in);
|
2015-08-11 00:50:43 -04:00
|
|
|
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
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;
|
2015-10-14 18:09:29 -04:00
|
|
|
initPFBChannelizer();
|
|
|
|
doRefresh.store(true);
|
2015-10-14 00:54:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int dataSize = data_in->data.size();
|
2015-10-16 18:40:40 -04:00
|
|
|
int outSize = data_in->data.size();
|
2015-09-30 23:45:06 -04:00
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
if (outSize > dataOut.capacity()) {
|
|
|
|
dataOut.reserve(outSize);
|
2014-12-26 16:15:35 -05:00
|
|
|
}
|
2015-10-14 00:54:48 -04:00
|
|
|
if (outSize != dataOut.size()) {
|
|
|
|
dataOut.resize(outSize);
|
2014-12-24 03:03:34 -05:00
|
|
|
}
|
2015-09-13 22:18:29 -04:00
|
|
|
|
2015-09-30 23:45:06 -04:00
|
|
|
// if (swapIQ) {
|
|
|
|
// for (int i = 0; i < dataSize; i++) {
|
|
|
|
// fpData[i] = _lut_swap[*((uint16_t*)&data_in->data[2*i])];
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// for (int i = 0; i < dataSize; i++) {
|
|
|
|
// fpData[i] = _lut[*((uint16_t*)&data_in->data[2*i])];
|
|
|
|
// }
|
|
|
|
// }
|
2015-08-11 00:50:43 -04:00
|
|
|
|
2015-10-17 16:17:12 -04:00
|
|
|
if (iqDataOutQueue != NULL && !iqDataOutQueue->full()) {
|
2015-10-14 00:54:48 -04:00
|
|
|
DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer();
|
2015-10-17 16:17:12 -04:00
|
|
|
|
|
|
|
bool doVis = false;
|
|
|
|
|
|
|
|
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
|
|
|
|
doVis = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
iqDataOut->setRefCount(1 + (doVis?1:0));
|
2015-08-11 00:50:43 -04:00
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
iqDataOut->frequency = data_in->frequency;
|
|
|
|
iqDataOut->sampleRate = data_in->sampleRate;
|
2015-10-15 01:35:03 -04:00
|
|
|
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
|
2015-10-14 00:54:48 -04:00
|
|
|
|
2015-10-17 16:17:12 -04:00
|
|
|
iqDataOutQueue->push(iqDataOut);
|
|
|
|
if (doVis) {
|
2015-10-14 00:54:48 -04:00
|
|
|
iqVisualQueue->push(iqDataOut);
|
|
|
|
}
|
2014-11-29 13:58:20 -05:00
|
|
|
}
|
2015-10-14 00:54:48 -04:00
|
|
|
|
2015-05-31 12:05:45 -04:00
|
|
|
busy_demod.lock();
|
2015-08-11 00:50:43 -04:00
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
if (frequency != data_in->frequency) {
|
|
|
|
frequency = data_in->frequency;
|
|
|
|
doRefresh.store(true);
|
|
|
|
}
|
2015-10-14 00:54:48 -04:00
|
|
|
|
2015-10-14 18:09:29 -04:00
|
|
|
if (doRefresh.load()) {
|
|
|
|
updateActiveDemodulators();
|
|
|
|
updateChannels();
|
|
|
|
doRefresh.store(false);
|
|
|
|
}
|
|
|
|
|
2015-10-17 16:17:12 -04:00
|
|
|
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
|
|
|
int activeDemodChannel = -1;
|
2015-10-14 18:09:29 -04:00
|
|
|
|
|
|
|
// Find active demodulators
|
|
|
|
if (nRunDemods) {
|
2015-10-15 21:01:07 -04:00
|
|
|
|
2015-10-16 18:40:40 -04:00
|
|
|
// for (int i = 0; i < numChannels; i++) {
|
|
|
|
// firpfbch_crcf_set_channel_state(channelizer, i, (demodChannelActive[i]>0)?1:0);
|
|
|
|
// }
|
2015-10-15 21:01:07 -04:00
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
// channelize data
|
2015-10-17 16:17:12 -04:00
|
|
|
// firpfbch output rate is (input rate / channels)
|
2015-10-16 18:40:40 -04:00
|
|
|
for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) {
|
|
|
|
firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]);
|
2015-10-14 00:54:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2015-10-17 18:25:14 -04:00
|
|
|
for (int j = 0, jMax = numChannels+1; j < jMax; j++) {
|
2015-10-14 00:54:48 -04:00
|
|
|
// Distance from channel center to demod center
|
|
|
|
long long fdelta = abs(demod->getFrequency() - chanCenters[j]);
|
|
|
|
if (fdelta < minDelta) {
|
|
|
|
minDelta = fdelta;
|
|
|
|
demodChannel[i] = j;
|
2015-10-17 16:17:12 -04:00
|
|
|
if (demod == activeDemod) {
|
|
|
|
activeDemodChannel = j;
|
|
|
|
}
|
2015-10-14 00:54:48 -04:00
|
|
|
}
|
2015-08-12 21:45:02 -04:00
|
|
|
}
|
2015-08-11 00:50:43 -04:00
|
|
|
}
|
2015-10-17 16:17:12 -04:00
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
for (int i = 0; i < nRunDemods; i++) {
|
|
|
|
// cache channel usage refcounts
|
|
|
|
if (demodChannel[i] >= 0) {
|
|
|
|
demodChannelActive[demodChannel[i]]++;
|
|
|
|
}
|
|
|
|
}
|
2015-08-11 00:50:43 -04:00
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
// Run channels
|
2015-10-17 18:25:14 -04:00
|
|
|
for (int i = 0; i < numChannels+1; i++) {
|
2015-10-17 16:17:12 -04:00
|
|
|
bool doVis = (activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full();
|
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
2015-10-17 16:17:12 -04:00
|
|
|
demodDataOut->setRefCount(demodChannelActive[i] + (doVis?1:0));
|
2015-10-14 00:54:48 -04:00
|
|
|
demodDataOut->frequency = chanCenters[i];
|
|
|
|
demodDataOut->sampleRate = chanBw;
|
2015-10-17 16:17:12 -04:00
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2015-10-17 18:25:14 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-10-14 00:54:48 -04:00
|
|
|
// prepare channel data buffer
|
2015-10-17 16:17:12 -04:00
|
|
|
if (i == 0) { // Channel 0 requires DC correction
|
|
|
|
if (dcBuf.size() != chanDataSize) {
|
|
|
|
dcBuf.resize(chanDataSize);
|
|
|
|
}
|
2015-10-17 18:25:14 -04:00
|
|
|
for (int j = 0; j < chanDataSize; j++) {
|
2015-10-17 16:17:12 -04:00
|
|
|
idx += numChannels;
|
|
|
|
dcBuf[j] = dataOut[idx];
|
|
|
|
}
|
|
|
|
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
|
|
|
|
} else {
|
2015-10-17 18:25:14 -04:00
|
|
|
for (int j = 0; j < chanDataSize; j++) {
|
2015-10-17 16:17:12 -04:00
|
|
|
idx += numChannels;
|
|
|
|
demodDataOut->data[j] = dataOut[idx];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doVis) {
|
|
|
|
iqActiveDemodVisualQueue->push(demodDataOut);
|
2015-10-14 00:54:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = 0; j < nRunDemods; j++) {
|
|
|
|
if (demodChannel[j] == i) {
|
|
|
|
DemodulatorInstance *demod = runDemods[j];
|
|
|
|
demod->getIQInputDataPipe()->push(demodDataOut);
|
2015-10-14 18:09:29 -04:00
|
|
|
// std::cout << "Demodulator " << j << " in channel #" << i << " ctr: " << chanCenters[i] << " dataSize: " << chanDataSize << std::endl;
|
2015-10-14 00:54:48 -04:00
|
|
|
}
|
|
|
|
}
|
2014-12-22 23:27:52 -05:00
|
|
|
}
|
2014-12-22 19:43:56 -05:00
|
|
|
}
|
2015-08-11 00:50:43 -04:00
|
|
|
|
2015-05-31 12:05:45 -04:00
|
|
|
busy_demod.unlock();
|
2014-12-23 01:59:03 -05:00
|
|
|
}
|
2014-12-24 00:11:41 -05:00
|
|
|
data_in->decRefCount();
|
2014-11-29 13:58:20 -05:00
|
|
|
}
|
2014-12-23 23:37:18 -05:00
|
|
|
|
2015-08-16 19:47:49 -04:00
|
|
|
// buffers.purge();
|
2015-07-28 21:56:39 -04:00
|
|
|
|
2015-07-30 19:30:46 -04:00
|
|
|
if (iqVisualQueue && !iqVisualQueue->empty()) {
|
2015-01-22 23:41:33 -05:00
|
|
|
DemodulatorThreadIQData *visualDataDummy;
|
2015-07-30 19:30:46 -04:00
|
|
|
iqVisualQueue->pop(visualDataDummy);
|
2015-01-22 23:41:33 -05:00
|
|
|
}
|
2014-12-23 23:37:18 -05:00
|
|
|
|
2015-08-16 19:47:49 -04:00
|
|
|
// visualDataBuffers.purge();
|
2015-01-22 21:32:32 -05:00
|
|
|
|
2014-11-30 17:11:29 -05:00
|
|
|
std::cout << "SDR post-processing thread done." << std::endl;
|
2014-11-29 13:58:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void SDRPostThread::terminate() {
|
|
|
|
terminated = true;
|
2014-12-23 01:59:03 -05:00
|
|
|
SDRThreadIQData *dummy = new SDRThreadIQData;
|
2015-07-30 19:30:46 -04:00
|
|
|
iqDataInQueue->push(dummy);
|
2014-11-29 13:58:20 -05:00
|
|
|
}
|