2014-12-16 18:27:32 -05:00
|
|
|
#include "CubicSDRDefs.h"
|
2015-02-22 01:01:28 -05:00
|
|
|
#include "DemodulatorThread.h"
|
2015-11-17 23:23:23 -05:00
|
|
|
#include "DemodulatorInstance.h"
|
2014-12-16 18:27:32 -05:00
|
|
|
#include <vector>
|
|
|
|
|
2015-02-22 01:01:28 -05:00
|
|
|
#include <cmath>
|
|
|
|
#ifndef M_PI
|
|
|
|
#define M_PI 3.14159265358979323846
|
|
|
|
#endif
|
|
|
|
|
2014-12-16 18:27:32 -05:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <pthread.h>
|
|
|
|
#endif
|
|
|
|
|
2015-11-18 21:09:51 -05:00
|
|
|
DemodulatorThread::DemodulatorThread(DemodulatorInstance *parent) : IOThread(), iqAutoGain(NULL), audioSampleRate(0), squelchLevel(0), signalLevel(0), squelchEnabled(false), iqInputQueue(NULL), audioOutputQueue(NULL), audioVisOutputQueue(NULL), threadQueueControl(NULL), threadQueueNotify(NULL), cModem(nullptr), cModemKit(nullptr) {
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-11-17 23:23:23 -05:00
|
|
|
demodInstance = parent;
|
2015-08-17 00:59:38 -04:00
|
|
|
muted.store(false);
|
2015-11-18 21:13:04 -05:00
|
|
|
agcEnabled.store(false);
|
|
|
|
|
2014-12-16 18:27:32 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-16 18:27:32 -05:00
|
|
|
DemodulatorThread::~DemodulatorThread() {
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-16 18:27:32 -05:00
|
|
|
}
|
|
|
|
|
2015-07-30 00:52:00 -04:00
|
|
|
void DemodulatorThread::onBindOutput(std::string name, ThreadQueueBase *threadQueue) {
|
|
|
|
if (name == "AudioVisualOutput") {
|
|
|
|
audioVisOutputQueue = (DemodulatorThreadOutputQueue *)threadQueue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-29 20:57:02 -04:00
|
|
|
void DemodulatorThread::run() {
|
2014-12-16 20:33:44 -05:00
|
|
|
#ifdef __APPLE__
|
2014-12-18 20:11:25 -05:00
|
|
|
pthread_t tID = pthread_self(); // ID of this thread
|
|
|
|
int priority = sched_get_priority_max( SCHED_FIFO )-1;
|
2014-12-18 21:39:32 -05:00
|
|
|
sched_param prio = {priority}; // scheduling priority of thread
|
2014-12-18 20:11:25 -05:00
|
|
|
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
2014-12-16 20:33:44 -05:00
|
|
|
#endif
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-03 17:07:39 -05:00
|
|
|
// Automatic IQ gain
|
|
|
|
iqAutoGain = agc_crcf_create();
|
2015-07-17 19:14:39 -04:00
|
|
|
agc_crcf_set_bandwidth(iqAutoGain, 0.1);
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-10-27 16:53:06 -04:00
|
|
|
ReBuffer<AudioThreadInput> audioVisBuffers;
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-16 21:30:03 -05:00
|
|
|
std::cout << "Demodulator thread started.." << std::endl;
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-07-30 00:28:53 -04:00
|
|
|
iqInputQueue = (DemodulatorThreadPostInputQueue*)getInputQueue("IQDataInput");
|
2015-07-30 19:30:46 -04:00
|
|
|
audioOutputQueue = (AudioThreadInputQueue*)getOutputQueue("AudioDataOutput");
|
2015-07-30 00:28:53 -04:00
|
|
|
threadQueueControl = (DemodulatorThreadControlCommandQueue *)getInputQueue("ControlQueue");
|
|
|
|
threadQueueNotify = (DemodulatorThreadCommandQueue*)getOutputQueue("NotifyQueue");
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-11-17 21:22:51 -05:00
|
|
|
ModemIQData modemData;
|
2015-07-30 00:28:53 -04:00
|
|
|
|
2014-12-16 21:30:03 -05:00
|
|
|
while (!terminated) {
|
2014-12-23 01:12:14 -05:00
|
|
|
DemodulatorThreadPostIQData *inp;
|
2015-01-03 17:07:39 -05:00
|
|
|
iqInputQueue->pop(inp);
|
2015-11-18 21:13:04 -05:00
|
|
|
// std::lock_guard < std::mutex > lock(inp->m_mutex);
|
|
|
|
|
2015-11-17 23:23:23 -05:00
|
|
|
audioSampleRate = demodInstance->getAudioSampleRate();
|
|
|
|
|
2014-12-23 01:12:14 -05:00
|
|
|
int bufSize = inp->data.size();
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-16 21:30:03 -05:00
|
|
|
if (!bufSize) {
|
2014-12-24 01:28:33 -05:00
|
|
|
inp->decRefCount();
|
2014-12-16 21:30:03 -05:00
|
|
|
continue;
|
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-11-17 21:22:51 -05:00
|
|
|
if (inp->modemKit && inp->modemKit != cModemKit) {
|
2015-11-17 23:23:23 -05:00
|
|
|
if (cModemKit != nullptr) {
|
|
|
|
cModem->disposeKit(cModemKit);
|
|
|
|
}
|
2015-11-17 21:22:51 -05:00
|
|
|
cModemKit = inp->modemKit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inp->modem && inp->modem != cModem) {
|
|
|
|
delete cModem;
|
|
|
|
cModem = inp->modem;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cModem || !cModemKit) {
|
|
|
|
inp->decRefCount();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-01-04 13:20:31 -05:00
|
|
|
if (agcData.size() != bufSize) {
|
|
|
|
if (agcData.capacity() < bufSize) {
|
|
|
|
agcData.reserve(bufSize);
|
2014-12-24 03:03:34 -05:00
|
|
|
}
|
2015-01-04 13:20:31 -05:00
|
|
|
agcData.resize(bufSize);
|
2014-12-24 03:03:34 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-04 13:20:31 -05:00
|
|
|
agc_crcf_execute_block(iqAutoGain, &(inp->data[0]), bufSize, &agcData[0]);
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-03 17:07:39 -05:00
|
|
|
float currentSignalLevel = 0;
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-03 17:07:39 -05:00
|
|
|
currentSignalLevel = ((60.0 / fabs(agc_crcf_get_rssi(iqAutoGain))) / 15.0 - signalLevel);
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-03 17:07:39 -05:00
|
|
|
if (agc_crcf_get_signal_level(iqAutoGain) > currentSignalLevel) {
|
|
|
|
currentSignalLevel = agc_crcf_get_signal_level(iqAutoGain);
|
2015-01-01 03:48:32 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-07-19 15:34:06 -04:00
|
|
|
std::vector<liquid_float_complex> *inputData;
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-07-19 15:34:06 -04:00
|
|
|
if (agcEnabled) {
|
2015-11-18 21:13:04 -05:00
|
|
|
inputData = &agcData;
|
2015-07-19 15:34:06 -04:00
|
|
|
} else {
|
2015-11-18 21:13:04 -05:00
|
|
|
inputData = &inp->data;
|
2015-07-19 15:34:06 -04:00
|
|
|
}
|
2015-11-17 21:22:51 -05:00
|
|
|
|
|
|
|
modemData.sampleRate = inp->sampleRate;
|
|
|
|
modemData.data.assign(inputData->begin(), inputData->end());
|
2015-11-17 23:23:23 -05:00
|
|
|
modemData.setRefCount(1);
|
|
|
|
|
2015-11-17 21:22:51 -05:00
|
|
|
AudioThreadInput *ati = NULL;
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-11-20 20:46:25 -05:00
|
|
|
ModemAnalog *modemAnalog = (cModem->getType() == "analog")?((ModemAnalog *)cModem):nullptr;
|
2015-11-20 22:13:09 -05:00
|
|
|
// ModemDigital *modemDigital = (cModem->getType() == "digital")?((ModemDigital *)cModem):nullptr;
|
2015-11-20 20:46:25 -05:00
|
|
|
|
|
|
|
if (modemAnalog != nullptr) {
|
2015-11-19 00:11:34 -05:00
|
|
|
ati = outputBuffers.getBuffer();
|
|
|
|
|
|
|
|
ati->sampleRate = audioSampleRate;
|
|
|
|
ati->inputRate = inp->sampleRate;
|
|
|
|
ati->setRefCount(1);
|
|
|
|
}
|
2015-11-17 21:22:51 -05:00
|
|
|
cModem->demodulate(cModemKit, &modemData, ati);
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-03 17:07:39 -05:00
|
|
|
if (currentSignalLevel > signalLevel) {
|
|
|
|
signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.5;
|
2014-12-31 19:45:01 -05:00
|
|
|
} else {
|
2015-01-03 17:07:39 -05:00
|
|
|
signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.05;
|
2014-12-31 19:45:01 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-03 17:07:39 -05:00
|
|
|
if (audioOutputQueue != NULL) {
|
2015-11-17 21:22:51 -05:00
|
|
|
if (ati && (!squelchEnabled || (signalLevel >= squelchLevel))) {
|
2015-02-08 18:59:08 -05:00
|
|
|
std::vector<float>::iterator data_i;
|
|
|
|
ati->peak = 0;
|
|
|
|
for (data_i = ati->data.begin(); data_i != ati->data.end(); data_i++) {
|
2015-02-12 02:14:22 -05:00
|
|
|
float p = fabs(*data_i);
|
|
|
|
if (p > ati->peak) {
|
2015-02-08 18:59:08 -05:00
|
|
|
ati->peak = p;
|
|
|
|
}
|
|
|
|
}
|
2014-12-21 16:08:32 -05:00
|
|
|
}
|
2014-12-16 21:30:03 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-03 17:07:39 -05:00
|
|
|
if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) {
|
2015-11-18 21:13:04 -05:00
|
|
|
AudioThreadInput *ati_vis = audioVisBuffers.getBuffer();
|
|
|
|
ati_vis->setRefCount(1);
|
2015-08-24 01:31:37 -04:00
|
|
|
ati_vis->sampleRate = inp->sampleRate;
|
|
|
|
ati_vis->inputRate = inp->sampleRate;
|
|
|
|
|
2014-12-21 16:08:32 -05:00
|
|
|
int num_vis = DEMOD_VIS_SIZE;
|
2015-11-17 21:22:51 -05:00
|
|
|
if (ati->channels==2) {
|
2014-12-27 15:04:43 -05:00
|
|
|
ati_vis->channels = 2;
|
|
|
|
int stereoSize = ati->data.size();
|
2015-06-27 23:23:43 -04:00
|
|
|
if (stereoSize > DEMOD_VIS_SIZE * 2) {
|
|
|
|
stereoSize = DEMOD_VIS_SIZE * 2;
|
2014-12-26 22:20:50 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-26 22:20:50 -05:00
|
|
|
ati_vis->data.resize(stereoSize);
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-11-17 21:49:02 -05:00
|
|
|
if (inp->modemType == "I/Q") {
|
2015-06-27 23:23:43 -04:00
|
|
|
for (int i = 0; i < stereoSize / 2; i++) {
|
2015-07-19 15:34:06 -04:00
|
|
|
ati_vis->data[i] = agcData[i].real * 0.75;
|
|
|
|
ati_vis->data[i + stereoSize / 2] = agcData[i].imag * 0.75;
|
2015-06-27 23:23:43 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < stereoSize / 2; i++) {
|
2015-08-24 01:31:37 -04:00
|
|
|
ati_vis->inputRate = audioSampleRate;
|
|
|
|
ati_vis->sampleRate = 36000;
|
2015-06-27 23:23:43 -04:00
|
|
|
ati_vis->data[i] = ati->data[i * 2];
|
|
|
|
ati_vis->data[i + stereoSize / 2] = ati->data[i * 2 + 1];
|
|
|
|
}
|
2014-12-21 16:08:32 -05:00
|
|
|
}
|
2014-12-18 21:39:32 -05:00
|
|
|
} else {
|
2015-11-16 23:49:54 -05:00
|
|
|
int numAudioWritten = ati->data.size();
|
2014-12-27 15:04:43 -05:00
|
|
|
ati_vis->channels = 1;
|
2015-11-20 20:46:25 -05:00
|
|
|
std::vector<float> *demodOutData = (modemAnalog != nullptr)?modemAnalog->getDemodOutputData():nullptr;
|
|
|
|
if ((numAudioWritten > bufSize) || (demodOutData == nullptr)) {
|
|
|
|
ati_vis->inputRate = audioSampleRate;
|
|
|
|
if (num_vis > numAudioWritten) {
|
|
|
|
num_vis = numAudioWritten;
|
|
|
|
}
|
|
|
|
ati_vis->data.assign(ati->data.begin(), ati->data.begin() + num_vis);
|
|
|
|
} else {
|
|
|
|
if (num_vis > demodOutData->size()) {
|
|
|
|
num_vis = demodOutData->size();
|
|
|
|
}
|
|
|
|
ati_vis->data.assign(demodOutData->begin(), demodOutData->begin() + num_vis);
|
2015-11-18 21:13:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl;
|
2014-12-21 16:08:32 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-08-24 01:31:37 -04:00
|
|
|
audioVisOutputQueue->push(ati_vis);
|
2014-12-21 16:08:32 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-11-16 23:49:54 -05:00
|
|
|
|
2015-07-19 15:34:06 -04:00
|
|
|
if (ati != NULL) {
|
2015-08-17 00:59:38 -04:00
|
|
|
if (!muted.load()) {
|
|
|
|
audioOutputQueue->push(ati);
|
|
|
|
} else {
|
|
|
|
ati->setRefCount(0);
|
|
|
|
}
|
2015-07-19 15:34:06 -04:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-21 16:08:32 -05:00
|
|
|
if (!threadQueueControl->empty()) {
|
|
|
|
while (!threadQueueControl->empty()) {
|
|
|
|
DemodulatorThreadControlCommand command;
|
|
|
|
threadQueueControl->pop(command);
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-21 16:08:32 -05:00
|
|
|
switch (command.cmd) {
|
2015-11-18 21:13:04 -05:00
|
|
|
case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_ON:
|
|
|
|
squelchEnabled = true;
|
|
|
|
break;
|
|
|
|
case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_OFF:
|
|
|
|
squelchEnabled = false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2014-12-21 16:08:32 -05:00
|
|
|
}
|
2014-12-18 21:39:32 -05:00
|
|
|
}
|
2014-12-16 21:30:03 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-24 01:28:33 -05:00
|
|
|
inp->decRefCount();
|
2014-12-16 21:30:03 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
// end while !terminated
|
2015-11-17 21:22:51 -05:00
|
|
|
|
2015-07-28 21:56:39 -04:00
|
|
|
outputBuffers.purge();
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-22 23:41:33 -05:00
|
|
|
if (audioVisOutputQueue && !audioVisOutputQueue->empty()) {
|
|
|
|
AudioThreadInput *dummy_vis;
|
|
|
|
audioVisOutputQueue->pop(dummy_vis);
|
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
audioVisBuffers.purge();
|
|
|
|
|
2014-12-16 21:30:03 -05:00
|
|
|
DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_TERMINATED);
|
|
|
|
tCmd.context = this;
|
|
|
|
threadQueueNotify->push(tCmd);
|
2015-07-30 00:28:53 -04:00
|
|
|
|
2015-01-11 20:26:51 -05:00
|
|
|
std::cout << "Demodulator thread done." << std::endl;
|
2015-01-03 17:07:39 -05:00
|
|
|
}
|
|
|
|
|
2014-12-16 18:27:32 -05:00
|
|
|
void DemodulatorThread::terminate() {
|
2014-12-16 21:30:03 -05:00
|
|
|
terminated = true;
|
2014-12-23 01:12:14 -05:00
|
|
|
DemodulatorThreadPostIQData *inp = new DemodulatorThreadPostIQData; // push dummy to nudge queue
|
2015-01-03 17:07:39 -05:00
|
|
|
iqInputQueue->push(inp);
|
2014-12-16 18:27:32 -05:00
|
|
|
}
|
2014-12-26 20:58:42 -05:00
|
|
|
|
2015-08-17 00:59:38 -04:00
|
|
|
bool DemodulatorThread::isMuted() {
|
|
|
|
return muted.load();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DemodulatorThread::setMuted(bool muted) {
|
|
|
|
this->muted.store(muted);
|
|
|
|
}
|
|
|
|
|
2015-07-19 15:34:06 -04:00
|
|
|
void DemodulatorThread::setAGC(bool state) {
|
2015-11-18 21:13:04 -05:00
|
|
|
agcEnabled.store(state);
|
2014-12-26 20:58:42 -05:00
|
|
|
}
|
2014-12-31 19:45:01 -05:00
|
|
|
|
2015-07-19 15:34:06 -04:00
|
|
|
bool DemodulatorThread::getAGC() {
|
2015-11-18 21:13:04 -05:00
|
|
|
return agcEnabled.load();
|
2015-07-19 15:34:06 -04:00
|
|
|
}
|
|
|
|
|
2014-12-31 19:45:01 -05:00
|
|
|
float DemodulatorThread::getSignalLevel() {
|
2015-07-19 15:34:06 -04:00
|
|
|
return signalLevel.load();
|
2014-12-31 19:45:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void DemodulatorThread::setSquelchLevel(float signal_level_in) {
|
2015-01-03 17:07:39 -05:00
|
|
|
if (!squelchEnabled) {
|
|
|
|
squelchEnabled = true;
|
2014-12-31 19:45:01 -05:00
|
|
|
}
|
2015-01-03 17:07:39 -05:00
|
|
|
squelchLevel = signal_level_in;
|
2014-12-31 19:45:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
float DemodulatorThread::getSquelchLevel() {
|
2015-01-03 17:07:39 -05:00
|
|
|
return squelchLevel;
|
2014-12-31 19:45:01 -05:00
|
|
|
}
|