2017-01-02 21:07:43 -05:00
|
|
|
// Copyright (c) Charles J. Cliffe
|
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
|
2015-02-22 01:01:28 -05:00
|
|
|
#include "DemodulatorThread.h"
|
2015-11-17 23:23:23 -05:00
|
|
|
#include "DemodulatorInstance.h"
|
2016-02-11 01:09:15 -05:00
|
|
|
#include "CubicSDR.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
|
|
|
|
|
2017-05-25 03:32:27 -04:00
|
|
|
//50 ms
|
|
|
|
#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
|
|
|
|
|
2014-12-16 18:27:32 -05:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <pthread.h>
|
|
|
|
#endif
|
|
|
|
|
2017-08-27 05:11:30 -04:00
|
|
|
DemodulatorInstance* DemodulatorThread::squelchLock(nullptr);
|
2016-08-12 22:58:33 -04:00
|
|
|
std::mutex DemodulatorThread::squelchLockMutex;
|
|
|
|
|
2017-08-27 05:11:30 -04:00
|
|
|
DemodulatorThread::DemodulatorThread(DemodulatorInstance* parent)
|
2021-06-03 01:16:47 -04:00
|
|
|
: IOThread(), demodInstance(parent), outputBuffers("DemodulatorThreadBuffers"), muted(false), squelchLevel(-100),
|
|
|
|
signalLevel(-100), signalFloor(-30), signalCeil(30), squelchEnabled(false), squelchBreak(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() {
|
2016-08-13 14:18:41 -04:00
|
|
|
releaseSquelchLock(demodInstance);
|
2014-12-16 18:27:32 -05:00
|
|
|
}
|
|
|
|
|
2017-08-13 12:49:47 -04:00
|
|
|
void DemodulatorThread::onBindOutput(std::string name, ThreadQueueBasePtr threadQueue) {
|
2015-07-30 00:52:00 -04:00
|
|
|
if (name == "AudioVisualOutput") {
|
2016-06-01 13:32:22 -04:00
|
|
|
|
|
|
|
//protects because it may be changed at runtime
|
2019-03-03 03:49:27 -05:00
|
|
|
std::lock_guard < SpinMutex > lock(m_mutexAudioVisOutputQueue);
|
2016-06-01 13:32:22 -04:00
|
|
|
|
2017-08-13 12:49:47 -04:00
|
|
|
audioVisOutputQueue = std::static_pointer_cast<DemodulatorThreadOutputQueue>(threadQueue);
|
2015-07-30 00:52:00 -04:00
|
|
|
}
|
2017-10-09 20:07:40 -04:00
|
|
|
|
2017-10-12 00:07:05 -04:00
|
|
|
if (name == "AudioSink") {
|
2019-03-03 03:49:27 -05:00
|
|
|
std::lock_guard < SpinMutex > lock(m_mutexAudioVisOutputQueue);
|
2017-10-09 20:07:40 -04:00
|
|
|
|
|
|
|
audioSinkOutputQueue = std::static_pointer_cast<AudioThreadInputQueue>(threadQueue);
|
|
|
|
}
|
2015-07-30 00:52:00 -04:00
|
|
|
}
|
|
|
|
|
2016-08-13 15:12:20 -04:00
|
|
|
double DemodulatorThread::abMagnitude(float inphase, float quadrature) {
|
|
|
|
|
|
|
|
// cast to double, so we keep precision despite the **2 op later.
|
|
|
|
double dinphase = (double)inphase;
|
|
|
|
double dquadrature = (double)quadrature;
|
|
|
|
|
|
|
|
//sqrt() has been an insanely fast intrinsic for years, use it !
|
|
|
|
return sqrt(dinphase * dinphase + dquadrature * dquadrature);
|
|
|
|
|
2015-11-21 15:12:20 -05:00
|
|
|
}
|
|
|
|
|
2016-08-13 15:12:20 -04:00
|
|
|
double DemodulatorThread::linearToDb(double linear) {
|
|
|
|
|
2015-11-21 15:12:20 -05:00
|
|
|
#define SMALL 1e-20
|
|
|
|
if (linear <= SMALL) {
|
2016-08-13 15:12:20 -04:00
|
|
|
linear = double(SMALL);
|
2015-11-21 15:12:20 -05:00
|
|
|
}
|
|
|
|
return 20.0 * log10(linear);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2016-06-02 19:45:34 -04:00
|
|
|
// std::cout << "Demodulator thread started.." << std::endl;
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2017-08-13 12:49:47 -04:00
|
|
|
iqInputQueue = std::static_pointer_cast<DemodulatorThreadPostInputQueue>(getInputQueue("IQDataInput"));
|
|
|
|
audioOutputQueue = std::static_pointer_cast<AudioThreadInputQueue>(getOutputQueue("AudioDataOutput"));
|
2016-07-03 03:47:28 -04:00
|
|
|
|
2015-11-17 21:22:51 -05:00
|
|
|
ModemIQData modemData;
|
2015-07-30 00:28:53 -04:00
|
|
|
|
2016-06-28 15:04:52 -04:00
|
|
|
while (!stopping) {
|
2017-05-24 06:24:36 -04:00
|
|
|
DemodulatorThreadPostIQDataPtr inp;
|
2016-07-06 15:23:59 -04:00
|
|
|
|
2017-05-25 03:32:27 -04:00
|
|
|
if (!iqInputQueue->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-05-24 06:24:36 -04:00
|
|
|
|
2016-02-04 18:05:33 -05:00
|
|
|
size_t bufSize = inp->data.size();
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2014-12-16 21:30:03 -05:00
|
|
|
if (!bufSize) {
|
2017-05-24 06:24:36 -04:00
|
|
|
|
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) {
|
2017-05-24 06:24:36 -04:00
|
|
|
|
2015-11-17 21:22:51 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-19 15:34:06 -04:00
|
|
|
std::vector<liquid_float_complex> *inputData;
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-11-21 15:12:20 -05:00
|
|
|
inputData = &inp->data;
|
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
|
|
|
|
2017-05-24 06:24:36 -04:00
|
|
|
AudioThreadInputPtr ati = nullptr;
|
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-29 13:35:12 -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();
|
|
|
|
|
2015-11-22 23:25:45 -05:00
|
|
|
ati->sampleRate = cModemKit->audioSampleRate;
|
2015-11-19 00:11:34 -05:00
|
|
|
ati->inputRate = inp->sampleRate;
|
2015-11-29 13:35:12 -05:00
|
|
|
} else if (modemDigital != nullptr) {
|
|
|
|
ati = outputBuffers.getBuffer();
|
|
|
|
|
|
|
|
ati->sampleRate = cModemKit->sampleRate;
|
|
|
|
ati->inputRate = inp->sampleRate;
|
2016-07-24 15:25:17 -04:00
|
|
|
ati->data.resize(0);
|
2015-11-19 00:11:34 -05:00
|
|
|
}
|
2015-11-29 13:35:12 -05:00
|
|
|
|
2017-05-24 06:24:36 -04:00
|
|
|
cModem->demodulate(cModemKit, &modemData, ati.get());
|
2016-08-12 20:31:20 -04:00
|
|
|
|
2016-08-13 15:12:20 -04:00
|
|
|
double currentSignalLevel = 0;
|
|
|
|
double sampleTime = double(inp->data.size()) / double(inp->sampleRate);
|
2016-08-12 20:31:20 -04:00
|
|
|
|
2021-04-04 22:14:31 -04:00
|
|
|
if (audioOutputQueue != nullptr && ati && !ati->data.empty()) {
|
2016-08-13 15:12:20 -04:00
|
|
|
double accum = 0;
|
|
|
|
|
|
|
|
if (cModem->useSignalOutput()) {
|
2016-08-12 21:34:34 -04:00
|
|
|
|
2016-08-13 14:50:20 -04:00
|
|
|
for (auto i : ati->data) {
|
2016-08-13 15:12:20 -04:00
|
|
|
accum += abMagnitude(i, 0.0);
|
2016-08-13 14:50:20 -04:00
|
|
|
}
|
2016-08-12 21:34:34 -04:00
|
|
|
|
2016-08-13 15:12:20 -04:00
|
|
|
currentSignalLevel = linearToDb(accum / double(ati->data.size()));
|
2016-08-12 21:34:34 -04:00
|
|
|
|
2016-08-13 14:50:20 -04:00
|
|
|
} else {
|
2016-08-13 15:12:20 -04:00
|
|
|
|
2016-08-13 14:50:20 -04:00
|
|
|
for (auto i : inp->data) {
|
2016-08-13 15:12:20 -04:00
|
|
|
accum += abMagnitude(i.real, i.imag);
|
2016-08-13 14:50:20 -04:00
|
|
|
}
|
2016-08-12 21:34:34 -04:00
|
|
|
|
2016-08-13 15:12:20 -04:00
|
|
|
currentSignalLevel = linearToDb(accum / double(inp->data.size()));
|
2016-08-13 14:50:20 -04:00
|
|
|
}
|
2016-08-12 20:31:20 -04:00
|
|
|
|
2021-04-22 00:34:45 -04:00
|
|
|
float sf = signalFloor, sc = signalCeil, sl = squelchLevel;
|
2016-08-12 20:31:20 -04:00
|
|
|
|
2016-08-13 15:12:20 -04:00
|
|
|
|
2016-08-12 20:31:20 -04:00
|
|
|
if (currentSignalLevel > sc) {
|
|
|
|
sc = currentSignalLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentSignalLevel < sf) {
|
|
|
|
sf = currentSignalLevel;
|
|
|
|
}
|
|
|
|
|
2016-08-13 15:12:20 -04:00
|
|
|
|
2016-08-12 20:31:20 -04:00
|
|
|
if (sl+1.0f > sc) {
|
|
|
|
sc = sl+1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sf+2.0f) > sc) {
|
|
|
|
sc = sf+2.0f;
|
|
|
|
}
|
|
|
|
|
2016-08-12 21:34:34 -04:00
|
|
|
sc -= (sc - (currentSignalLevel + 2.0f)) * sampleTime * 0.05f;
|
2016-08-12 20:31:20 -04:00
|
|
|
sf += ((currentSignalLevel - 5.0f) - sf) * sampleTime * 0.15f;
|
|
|
|
|
2021-04-22 00:34:45 -04:00
|
|
|
signalFloor = sf;
|
|
|
|
signalCeil = sc;
|
2016-08-12 20:31:20 -04:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2015-01-03 17:07:39 -05:00
|
|
|
if (currentSignalLevel > signalLevel) {
|
2016-08-12 21:34:34 -04:00
|
|
|
signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.5;
|
2014-12-31 19:45:01 -05:00
|
|
|
} else {
|
2016-08-12 21:34:34 -04:00
|
|
|
signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.05 * sampleTime * 30.0;
|
2014-12-31 19:45:01 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2017-12-29 22:46:39 -05:00
|
|
|
bool squelched = squelchEnabled && (signalLevel < squelchLevel);
|
2015-11-21 15:12:20 -05:00
|
|
|
|
2016-02-11 01:32:39 -05:00
|
|
|
if (squelchEnabled) {
|
|
|
|
if (!squelched && !squelchBreak) {
|
2016-08-16 17:55:40 -04:00
|
|
|
if (wxGetApp().getSoloMode() && !wxGetApp().getAppFrame()->isUserDemodBusy()) {
|
2016-08-12 22:58:33 -04:00
|
|
|
std::lock_guard < std::mutex > lock(squelchLockMutex);
|
2017-08-27 05:11:30 -04:00
|
|
|
if (squelchLock == nullptr) {
|
|
|
|
squelchLock = demodInstance;
|
2016-08-12 22:58:33 -04:00
|
|
|
wxGetApp().getDemodMgr().setActiveDemodulator(nullptr);
|
2017-08-27 05:11:30 -04:00
|
|
|
wxGetApp().getDemodMgr().setActiveDemodulatorByRawPointer(demodInstance, false);
|
2016-08-12 22:58:33 -04:00
|
|
|
squelchBreak = true;
|
|
|
|
demodInstance->getVisualCue()->triggerSquelchBreak(120);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
squelchBreak = true;
|
|
|
|
demodInstance->getVisualCue()->triggerSquelchBreak(120);
|
|
|
|
}
|
|
|
|
|
2016-02-11 01:32:39 -05:00
|
|
|
} else if (squelched && squelchBreak) {
|
2016-08-13 14:18:41 -04:00
|
|
|
releaseSquelchLock(demodInstance);
|
2016-02-11 01:32:39 -05:00
|
|
|
squelchBreak = false;
|
|
|
|
}
|
|
|
|
}
|
2018-01-06 08:19:32 -05:00
|
|
|
|
2018-01-13 05:50:08 -05:00
|
|
|
//compute audio peak:
|
|
|
|
if (audioOutputQueue != nullptr && ati) {
|
2018-01-06 08:19:32 -05:00
|
|
|
|
2018-01-13 05:50:08 -05:00
|
|
|
ati->peak = 0;
|
2018-01-06 08:19:32 -05:00
|
|
|
|
2018-01-13 05:50:08 -05:00
|
|
|
for (auto data_i : ati->data) {
|
|
|
|
float p = fabs(data_i);
|
|
|
|
if (p > ati->peak) {
|
|
|
|
ati->peak = p;
|
2018-01-06 09:09:33 -05:00
|
|
|
}
|
2018-01-06 08:19:32 -05:00
|
|
|
}
|
2018-01-13 05:50:08 -05:00
|
|
|
}
|
2018-01-06 08:19:32 -05:00
|
|
|
|
2018-01-13 05:50:08 -05:00
|
|
|
//attach squelch flag to samples, to be used by audio sink.
|
|
|
|
if (ati) {
|
|
|
|
ati->is_squelch_active = squelched;
|
|
|
|
}
|
|
|
|
|
2016-06-01 13:32:22 -04:00
|
|
|
//At that point, capture the current state of audioVisOutputQueue in a local
|
|
|
|
//variable, and works with it with now on until the next while-turn.
|
2017-08-13 12:49:47 -04:00
|
|
|
DemodulatorThreadOutputQueuePtr localAudioVisOutputQueue = nullptr;
|
2016-06-01 13:32:22 -04:00
|
|
|
{
|
2019-03-03 03:49:27 -05:00
|
|
|
std::lock_guard < SpinMutex > lock(m_mutexAudioVisOutputQueue);
|
2016-06-01 13:32:22 -04:00
|
|
|
localAudioVisOutputQueue = audioVisOutputQueue;
|
|
|
|
}
|
|
|
|
|
2018-01-14 18:56:30 -05:00
|
|
|
if (!squelched && (ati || modemDigital) && localAudioVisOutputQueue != nullptr && localAudioVisOutputQueue->empty()) {
|
2017-08-28 14:31:07 -04:00
|
|
|
|
|
|
|
AudioThreadInputPtr ati_vis = std::make_shared<AudioThreadInput>();
|
2016-06-01 13:32:22 -04:00
|
|
|
|
2015-08-24 01:31:37 -04:00
|
|
|
ati_vis->sampleRate = inp->sampleRate;
|
|
|
|
ati_vis->inputRate = inp->sampleRate;
|
|
|
|
|
2016-01-28 15:49:31 -05:00
|
|
|
size_t num_vis = DEMOD_VIS_SIZE;
|
2015-11-29 13:35:12 -05:00
|
|
|
if (modemDigital) {
|
2016-07-24 17:24:29 -04:00
|
|
|
if (ati) { // TODO: handle digital modems with audio output
|
2017-05-24 06:24:36 -04:00
|
|
|
|
2016-07-24 17:24:29 -04:00
|
|
|
ati = nullptr;
|
|
|
|
}
|
2015-11-29 13:35:12 -05:00
|
|
|
ati_vis->data.resize(inputData->size());
|
|
|
|
ati_vis->channels = 2;
|
|
|
|
for (int i = 0, iMax = inputData->size() / 2; i < iMax; i++) {
|
|
|
|
ati_vis->data[i * 2] = (*inputData)[i].real;
|
|
|
|
ati_vis->data[i * 2 + 1] = (*inputData)[i].imag;
|
|
|
|
}
|
|
|
|
ati_vis->type = 2;
|
|
|
|
} else 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-29 13:35:12 -05:00
|
|
|
if (inp->modemName == "I/Q") {
|
2015-06-27 23:23:43 -04:00
|
|
|
for (int i = 0; i < stereoSize / 2; i++) {
|
2015-11-21 15:12:20 -05:00
|
|
|
ati_vis->data[i] = (*inputData)[i].real * 0.75;
|
|
|
|
ati_vis->data[i + stereoSize / 2] = (*inputData)[i].imag * 0.75;
|
2015-06-27 23:23:43 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < stereoSize / 2; i++) {
|
2015-11-22 23:25:45 -05:00
|
|
|
ati_vis->inputRate = cModemKit->audioSampleRate;
|
2015-08-24 01:31:37 -04:00
|
|
|
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
|
|
|
}
|
2015-11-29 13:35:12 -05:00
|
|
|
ati_vis->type = 1;
|
2014-12-18 21:39:32 -05:00
|
|
|
} else {
|
2016-01-28 15:49:31 -05:00
|
|
|
size_t 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)) {
|
2015-11-22 23:25:45 -05:00
|
|
|
ati_vis->inputRate = cModemKit->audioSampleRate;
|
2015-11-20 20:46:25 -05:00
|
|
|
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
|
|
|
}
|
2015-11-29 13:35:12 -05:00
|
|
|
ati_vis->type = 0;
|
2014-12-21 16:08:32 -05:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2017-02-15 14:27:57 -05:00
|
|
|
if (!localAudioVisOutputQueue->try_push(ati_vis)) {
|
2017-02-18 04:15:47 -05:00
|
|
|
//non-blocking push needed for audio vis out
|
2017-05-24 06:24:36 -04:00
|
|
|
|
2017-02-15 14:27:57 -05:00
|
|
|
std::cout << "DemodulatorThread::run() cannot push ati_vis into localAudioVisOutputQueue, is full !" << std::endl;
|
|
|
|
std::this_thread::yield();
|
|
|
|
}
|
2014-12-21 16:08:32 -05:00
|
|
|
}
|
2017-02-09 13:12:12 -05:00
|
|
|
|
2018-01-14 18:56:30 -05:00
|
|
|
if (!squelched && ati != nullptr) {
|
2021-04-22 00:34:45 -04:00
|
|
|
if (!muted && (!wxGetApp().getSoloMode() || (demodInstance ==
|
2019-02-11 22:59:43 -05:00
|
|
|
wxGetApp().getDemodMgr().getCurrentModem().get()))) {
|
2017-02-18 04:15:47 -05:00
|
|
|
//non-blocking push needed for audio out
|
|
|
|
if (!audioOutputQueue->try_push(ati)) {
|
2017-08-14 03:10:24 -04:00
|
|
|
|
|
|
|
std::cout << "DemodulatorThread::run() cannot push ati into audioOutputQueue, is full !" << std::endl;
|
2017-02-18 04:15:47 -05:00
|
|
|
std::this_thread::yield();
|
|
|
|
}
|
2017-12-04 22:44:47 -05:00
|
|
|
}
|
2015-07-19 15:34:06 -04:00
|
|
|
}
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2018-01-14 18:56:30 -05:00
|
|
|
|
|
|
|
// Capture audioSinkOutputQueue state in a local variable
|
|
|
|
DemodulatorThreadOutputQueuePtr localAudioSinkOutputQueue = nullptr;
|
|
|
|
{
|
2019-03-03 03:49:27 -05:00
|
|
|
std::lock_guard < SpinMutex > lock(m_mutexAudioVisOutputQueue);
|
2018-01-14 18:56:30 -05:00
|
|
|
localAudioSinkOutputQueue = audioSinkOutputQueue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Push to audio sink, if any:
|
|
|
|
if (ati && localAudioSinkOutputQueue != nullptr) {
|
|
|
|
|
|
|
|
if (!localAudioSinkOutputQueue->try_push(ati)) {
|
|
|
|
std::cout << "DemodulatorThread::run() cannot push ati into audioSinkOutputQueue, is full !" << std::endl;
|
|
|
|
std::this_thread::yield();
|
|
|
|
}
|
|
|
|
}
|
2014-12-16 21:30:03 -05:00
|
|
|
}
|
2016-06-28 15:04:52 -04:00
|
|
|
// end while !stopping
|
2015-11-17 21:22:51 -05:00
|
|
|
|
2016-07-05 15:43:45 -04:00
|
|
|
// Purge any unused inputs, with a non-blocking pop
|
2017-05-24 06:24:36 -04:00
|
|
|
iqInputQueue->flush();
|
|
|
|
audioOutputQueue->flush();
|
2015-11-18 21:13:04 -05:00
|
|
|
|
2016-06-02 19:45:34 -04: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() {
|
2016-06-28 15:04:52 -04:00
|
|
|
IOThread::terminate();
|
2017-08-28 14:31:07 -04:00
|
|
|
|
2021-12-29 13:54:03 -05:00
|
|
|
//unblock the currently blocked push()
|
2017-08-28 14:31:07 -04:00
|
|
|
iqInputQueue->flush();
|
|
|
|
audioOutputQueue->flush();
|
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() {
|
2021-04-22 00:34:45 -04:00
|
|
|
return muted;
|
2015-08-17 00:59:38 -04:00
|
|
|
}
|
|
|
|
|
2021-04-04 22:14:31 -04:00
|
|
|
void DemodulatorThread::setMuted(bool muted_in) {
|
2021-04-22 00:34:45 -04:00
|
|
|
muted = muted_in;
|
2015-08-17 00:59:38 -04:00
|
|
|
}
|
|
|
|
|
2014-12-31 19:45:01 -05:00
|
|
|
float DemodulatorThread::getSignalLevel() {
|
2021-04-22 00:34:45 -04:00
|
|
|
return signalLevel;
|
2014-12-31 19:45:01 -05:00
|
|
|
}
|
|
|
|
|
2016-08-10 14:02:25 -04:00
|
|
|
float DemodulatorThread::getSignalFloor() {
|
2021-04-22 00:34:45 -04:00
|
|
|
return signalFloor;
|
2016-08-10 14:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
float DemodulatorThread::getSignalCeil() {
|
2021-04-22 00:34:45 -04:00
|
|
|
return signalCeil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DemodulatorThread::setSquelchEnabled(bool squelchEnabled_in) {
|
|
|
|
squelchEnabled = squelchEnabled_in;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DemodulatorThread::isSquelchEnabled() {
|
|
|
|
return squelchEnabled;
|
2016-08-10 14:02:25 -04:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2016-08-13 14:18:41 -04:00
|
|
|
|
|
|
|
bool DemodulatorThread::getSquelchBreak() {
|
|
|
|
return squelchBreak;
|
|
|
|
}
|
|
|
|
|
2017-08-27 05:11:30 -04:00
|
|
|
void DemodulatorThread::releaseSquelchLock(DemodulatorInstance* inst) {
|
|
|
|
|
2016-08-13 14:18:41 -04:00
|
|
|
std::lock_guard < std::mutex > lock(squelchLockMutex);
|
2017-08-27 05:11:30 -04:00
|
|
|
|
|
|
|
if (inst == nullptr || squelchLock == inst) {
|
|
|
|
squelchLock = nullptr;
|
2016-08-13 14:18:41 -04:00
|
|
|
}
|
2017-12-04 22:44:47 -05:00
|
|
|
}
|