CubicSDR/src/demod/DemodulatorMgr.cpp

589 lines
18 KiB
C++

// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#include <DemodulatorMgr.h>
#include <algorithm>
#include <string>
#include "CubicSDR.h"
#if USE_HAMLIB
#include "RigThread.h"
#endif
#include "DataTree.h"
#include <wx/string.h>
bool demodFreqCompare (const DemodulatorInstancePtr& i, const DemodulatorInstancePtr& j) { return (i->getFrequency() < j->getFrequency()); }
bool inactiveCompare (const DemodulatorInstancePtr& i, const DemodulatorInstancePtr& j) { return (i->isActive() < j->isActive()); }
DemodulatorMgr::DemodulatorMgr() {
lastBandwidth = DEFAULT_DEMOD_BW;
lastDemodType = DEFAULT_DEMOD_TYPE;
lastSquelchEnabled = false;
lastSquelch = -100;
lastGain = 1.0;
lastMuted = false;
lastDeltaLock = false;
}
DemodulatorMgr::~DemodulatorMgr() {
terminateAll();
}
DemodulatorInstancePtr DemodulatorMgr::newThread() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
//create a new instance of DemodulatorInstance here.
DemodulatorInstancePtr newDemod = std::make_shared<DemodulatorInstance>();
std::stringstream label;
label << demods.size();
newDemod->setLabel(label.str());
demods.push_back(newDemod);
return newDemod;
}
void DemodulatorMgr::terminateAll() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
while (!demods.empty()) {
DemodulatorInstancePtr d = demods.back();
demods.pop_back();
deleteThread(d);
}
}
std::vector<DemodulatorInstancePtr> DemodulatorMgr::getDemodulators() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
return demods;
}
std::vector<DemodulatorInstancePtr> DemodulatorMgr::getOrderedDemodulators(bool actives) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
auto demods_ordered = demods;
if (actives) {
std::sort(demods_ordered.begin(), demods_ordered.end(), inactiveCompare);
std::vector<DemodulatorInstancePtr>::iterator i;
for (i = demods_ordered.begin(); i != demods_ordered.end(); i++) {
if ((*i)->isActive()) {
break;
}
}
if (i == demods_ordered.end()) {
demods_ordered.erase(demods_ordered.begin(), demods_ordered.end());
} else if ((*i) != demods_ordered.front()) {
demods_ordered.erase(demods_ordered.begin(), i);
}
}
//if by chance they have the same frequency, keep their relative order
std::stable_sort(demods_ordered.begin(), demods_ordered.end(), demodFreqCompare);
return demods_ordered;
}
DemodulatorInstancePtr DemodulatorMgr::getPreviousDemodulator(const DemodulatorInstancePtr& demod, bool actives) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
if (!getCurrentModem()) {
return nullptr;
}
auto demods_ordered = getOrderedDemodulators(actives);
auto p = std::find(demods_ordered.begin(), demods_ordered.end(), demod);
if (p == demods_ordered.end()) {
return nullptr;
}
if (*p == demods_ordered.front()) {
return demods_ordered.back();
}
return *(--p);
}
DemodulatorInstancePtr DemodulatorMgr::getNextDemodulator(const DemodulatorInstancePtr& demod, bool actives) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
if (!getCurrentModem()) {
return nullptr;
}
auto demods_ordered = getOrderedDemodulators(actives);
auto p = std::find(demods_ordered.begin(), demods_ordered.end(), demod);
if (actives) {
}
if (p == demods_ordered.end()) {
return nullptr;
}
if (*p == demods_ordered.back()) {
return demods_ordered.front();
}
return *(++p);
}
DemodulatorInstancePtr DemodulatorMgr::getLastDemodulator() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
return getOrderedDemodulators().back();
}
DemodulatorInstancePtr DemodulatorMgr::getFirstDemodulator() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
return getOrderedDemodulators().front();
}
void DemodulatorMgr::deleteThread(const DemodulatorInstancePtr& demod) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
wxGetApp().getBookmarkMgr().addRecent(demod);
auto i = std::find(demods.begin(), demods.end(), demod);
if (activeContextModem == demod) {
activeContextModem = nullptr;
}
if (currentModem == demod) {
currentModem = nullptr;
}
if (activeVisualDemodulator == demod) {
activeVisualDemodulator = nullptr;
}
if (i != demods.end()) {
demods.erase(i);
}
//Ask for termination
demod->setActive(false);
demod->terminate();
}
std::vector<DemodulatorInstancePtr> DemodulatorMgr::getDemodulatorsAt(long long freq, int bandwidth) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
std::vector<DemodulatorInstancePtr> foundDemods;
for (auto testDemod : demods) {
long long freqTest = testDemod->getFrequency();
long long bandwidthTest = testDemod->getBandwidth();
long long halfBandwidthTest = bandwidthTest / 2;
long long halfBuffer = bandwidth / 2;
if ((freq <= (freqTest + ((testDemod->getDemodulatorType() != "LSB")?halfBandwidthTest:0) + halfBuffer)) && (freq >= (freqTest - ((testDemod->getDemodulatorType() != "USB")?halfBandwidthTest:0) - halfBuffer))) {
foundDemods.push_back(testDemod);
}
}
return foundDemods;
}
bool DemodulatorMgr::anyDemodulatorsAt(long long freq, int bandwidth) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
for (auto testDemod : demods) {
long long freqTest = testDemod->getFrequency();
long long bandwidthTest = testDemod->getBandwidth();
long long halfBandwidthTest = bandwidthTest / 2;
long long halfBuffer = bandwidth / 2;
if ((freq <= (freqTest + ((testDemod->getDemodulatorType() != "LSB")?halfBandwidthTest:0) + halfBuffer)) && (freq >= (freqTest - ((testDemod->getDemodulatorType() != "USB")?halfBandwidthTest:0) - halfBuffer))) {
return true;
}
}
return false;
}
void DemodulatorMgr::setActiveDemodulator(const DemodulatorInstancePtr& demod, bool temporary) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
// Should this be made the current modem (i.e. clicked, toggled)
if (!temporary) {
if (activeContextModem != nullptr) {
currentModem = activeContextModem;
updateLastState();
} else {
currentModem = demod;
}
updateLastState();
wxGetApp().getBookmarkMgr().updateActiveList();
#if USE_HAMLIB
if (wxGetApp().rigIsActive() && wxGetApp().getRigThread()->getFollowModem() && currentModem) {
wxGetApp().getRigThread()->setFrequency(currentModem->getFrequency(),true);
}
#endif
}
// TODO: This is probably unnecessary and confusing
if (activeVisualDemodulator) {
activeVisualDemodulator->setVisualOutputQueue(nullptr);
}
if (demod) {
demod->setVisualOutputQueue(wxGetApp().getAudioVisualQueue());
activeVisualDemodulator = demod;
} else {
DemodulatorInstancePtr last = getCurrentModem();
if (last) {
last->setVisualOutputQueue(wxGetApp().getAudioVisualQueue());
}
activeVisualDemodulator = last;
}
// :ODOT
activeContextModem = demod;
}
//Dangerous: this is only intended by some internal classes
void DemodulatorMgr::setActiveDemodulatorByRawPointer(DemodulatorInstance* demod, bool temporary) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
for (const auto& existing_demod : demods) {
if (existing_demod.get() == demod) {
setActiveDemodulator(existing_demod, temporary);
break;
}
}
}
/**
* Get the currently focused modem, i.e. the one hovered by interaction
* If no active context modem is available the current modem is returned
* @return Active Context Modem
*/
DemodulatorInstancePtr DemodulatorMgr::getActiveContextModem() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
if (activeContextModem && !activeContextModem->isActive()) {
activeContextModem = getCurrentModem();
}
return activeContextModem;
}
/**
* Get the last selected / focused modem
* This is the currently active modem
* @return Current Modem
*/
DemodulatorInstancePtr DemodulatorMgr::getCurrentModem() {
return currentModem;
}
DemodulatorInstancePtr DemodulatorMgr::getLastDemodulatorWith(const std::string& type,
const std::wstring& userLabel,
long long frequency,
int bandwidth) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
//backwards search:
for (auto it = demods.rbegin(); it != demods.rend(); it++) {
if ((*it)->getDemodulatorType() == type &&
(*it)->getDemodulatorUserLabel() == userLabel &&
(*it)->getFrequency() == frequency &&
(*it)->getBandwidth() == bandwidth) {
return (*it);
}
}
return nullptr;
}
void DemodulatorMgr::updateLastState() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
if (std::find(demods.begin(), demods.end(), currentModem) == demods.end()) {
if (activeContextModem && activeContextModem->isActive()) {
currentModem = activeContextModem;
} else if (activeContextModem && !activeContextModem->isActive()){
activeContextModem = nullptr;
currentModem = nullptr;
}
}
if (currentModem && !currentModem->isActive()) {
currentModem = nullptr;
}
if (currentModem) {
lastBandwidth = currentModem->getBandwidth();
lastDemodType = currentModem->getDemodulatorType();
lastDemodLock = currentModem->getDemodulatorLock() != 0;
lastSquelchEnabled = currentModem->isSquelchEnabled();
lastSquelch = currentModem->getSquelchLevel();
lastGain = currentModem->getGain();
lastModemSettings[lastDemodType] = currentModem->readModemSettings();
}
}
int DemodulatorMgr::getLastBandwidth() const {
return lastBandwidth;
}
void DemodulatorMgr::setLastBandwidth(int lastBandwidth_in) {
if (lastBandwidth_in < MIN_BANDWIDTH) {
lastBandwidth_in = MIN_BANDWIDTH;
} else if (lastBandwidth_in > wxGetApp().getSampleRate()) {
lastBandwidth_in = wxGetApp().getSampleRate();
}
lastBandwidth = lastBandwidth_in;
}
std::string DemodulatorMgr::getLastDemodulatorType() const {
return lastDemodType;
}
void DemodulatorMgr::setLastDemodulatorType(std::string lastDemodType_in) {
lastDemodType = lastDemodType_in;
}
float DemodulatorMgr::getLastGain() const {
return lastGain;
}
void DemodulatorMgr::setLastGain(float lastGain_in) {
lastGain = lastGain_in;
}
bool DemodulatorMgr::getLastDeltaLock() const {
return lastDeltaLock;
}
void DemodulatorMgr::setLastDeltaLock(bool lock) {
lastDeltaLock = lock;
}
float DemodulatorMgr::getLastSquelchLevel() const {
return lastSquelch;
}
void DemodulatorMgr::setLastSquelchLevel(float lastSquelch_in) {
lastSquelch = lastSquelch_in;
}
bool DemodulatorMgr::isLastSquelchEnabled() const {
return lastSquelchEnabled;
}
void DemodulatorMgr::setLastSquelchEnabled(bool lastSquelchEnabled_in) {
lastSquelchEnabled = lastSquelchEnabled_in;
}
bool DemodulatorMgr::isLastMuted() const {
return lastMuted;
}
void DemodulatorMgr::setLastMuted(bool lastMuted_in) {
lastMuted = lastMuted_in;
}
ModemSettings DemodulatorMgr::getLastModemSettings(const std::string& modemType) {
return lastModemSettings[modemType];
}
void DemodulatorMgr::setLastModemSettings(const std::string& modemType, ModemSettings settings) {
lastModemSettings[modemType] = settings;
}
void DemodulatorMgr::setOutputDevices(std::map<int,RtAudio::DeviceInfo> devs) {
outputDevices = devs;
}
std::map<int, RtAudio::DeviceInfo> DemodulatorMgr::getOutputDevices() {
return outputDevices;
}
void DemodulatorMgr::saveInstance(DataNode *node, const DemodulatorInstancePtr& inst) {
*node->newChild("bandwidth") = inst->getBandwidth();
*node->newChild("frequency") = inst->getFrequency();
*node->newChild("type") = inst->getDemodulatorType();
node->newChild("user_label")->element()->set(inst->getDemodulatorUserLabel());
*node->newChild("squelch_level") = inst->getSquelchLevel();
*node->newChild("squelch_enabled") = inst->isSquelchEnabled() ? 1 : 0;
*node->newChild("output_device") = outputDevices[inst->getOutputDevice()].name;
*node->newChild("gain") = inst->getGain();
*node->newChild("muted") = inst->isMuted() ? 1 : 0;
if (inst->isDeltaLock()) {
*node->newChild("delta_lock") = inst->isDeltaLock() ? 1 : 0;
*node->newChild("delta_ofs") = inst->getDeltaLockOfs();
}
if (inst == getCurrentModem()) {
*node->newChild("active") = 1;
}
ModemSettings saveSettings = inst->readModemSettings();
if (!saveSettings.empty()) {
DataNode *settingsNode = node->newChild("settings");
for (ModemSettings::const_iterator msi = saveSettings.begin(); msi != saveSettings.end(); msi++) {
*settingsNode->newChild(msi->first.c_str()) = msi->second;
}
}
}
std::wstring DemodulatorMgr::getSafeWstringValue(DataNode* node) {
std::wstring decodedWString;
if (node != nullptr) {
//1) decode as encoded wstring:
try {
node->element()->get(decodedWString);
} catch (const DataTypeMismatchException &) {
//2) wstring decode fail, try simple std::string
std::string decodedStdString;
try {
node->element()->get(decodedStdString);
//use wxString for a clean conversion to a wstring:
decodedWString = wxString(decodedStdString).ToStdWstring();
} catch (const DataTypeMismatchException &) {
//nothing works, return an empty string.
decodedWString = L"";
}
}
}
return decodedWString;
}
DemodulatorInstancePtr DemodulatorMgr::loadInstance(DataNode *node) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
DemodulatorInstancePtr newDemod = nullptr;
node->rewindAll();
long bandwidth = (long)*node->getNext("bandwidth");
long long freq = (long long)*node->getNext("frequency");
float squelch_level = node->hasAnother("squelch_level") ? (float) *node->getNext("squelch_level") : 0;
int squelch_enabled = node->hasAnother("squelch_enabled") ? (int) *node->getNext("squelch_enabled") : 0;
int muted = node->hasAnother("muted") ? (int) *node->getNext("muted") : 0;
int delta_locked = node->hasAnother("delta_lock") ? (int) *node->getNext("delta_lock") : 0;
int delta_ofs = node->hasAnother("delta_ofs") ? (int) *node->getNext("delta_ofs") : 0;
std::string output_device = node->hasAnother("output_device") ? ((string)*(node->getNext("output_device"))) : "";
float gain = node->hasAnother("gain") ? (float) *node->getNext("gain") : 1.0f;
std::string type = "FM";
DataNode *demodTypeNode = node->hasAnother("type")?node->getNext("type"):nullptr;
if (demodTypeNode && demodTypeNode->element()->getDataType() == DataElement::Type::DATA_INT) {
int legacyType = (int)*demodTypeNode;
int legacyStereo = node->hasAnother("stereo") ? (int) *node->getNext("stereo") : 0;
switch (legacyType) { // legacy demod ID
case 1: type = legacyStereo?"FMS":"FM"; break;
case 2: type = "AM"; break;
case 3: type = "LSB"; break;
case 4: type = "USB"; break;
case 5: type = "DSB"; break;
case 6: type = "ASK"; break;
case 7: type = "APSK"; break;
case 8: type = "BPSK"; break;
case 9: type = "DPSK"; break;
case 10: type = "PSK"; break;
case 11: type = "OOK"; break;
case 12: type = "ST"; break;
case 13: type = "SQAM"; break;
case 14: type = "QAM"; break;
case 15: type = "QPSK"; break;
case 16: type = "I/Q"; break;
default: type = "FM"; break;
}
} else if (demodTypeNode && demodTypeNode->element()->getDataType() == DataElement::Type::DATA_STRING) {
demodTypeNode->element()->get(type);
}
//read the user label associated with the demodulator
std::wstring user_label;
DataNode *demodUserLabel = node->hasAnother("user_label") ? node->getNext("user_label") : nullptr;
if (demodUserLabel) {
user_label = DemodulatorMgr::getSafeWstringValue(demodUserLabel);
}
ModemSettings mSettings;
if (node->hasAnother("settings")) {
DataNode *modemSettings = node->getNext("settings");
for (int msi = 0, numSettings = modemSettings->numChildren(); msi < numSettings; msi++) {
DataNode *settingNode = modemSettings->child(msi);
std::string keyName = settingNode->getName();
std::string strSettingValue = settingNode->element()->toString();
if (!keyName.empty() && !strSettingValue.empty()) {
mSettings[keyName] = strSettingValue;
}
}
}
newDemod = newThread();
newDemod->setDemodulatorType(type);
newDemod->setDemodulatorUserLabel(user_label);
newDemod->writeModemSettings(mSettings);
newDemod->setBandwidth(bandwidth);
newDemod->setFrequency(freq);
newDemod->setGain(gain);
newDemod->updateLabel(freq);
newDemod->setMuted(muted != 0);
if (delta_locked) {
newDemod->setDeltaLock(true);
newDemod->setDeltaLockOfs(delta_ofs);
}
if (squelch_enabled) {
newDemod->setSquelchEnabled(true);
newDemod->setSquelchLevel(squelch_level);
}
//Attach to sound output:
std::map<int, RtAudio::DeviceInfo>::iterator i;
bool matching_device_found = false;
for (i = outputDevices.begin(); i != outputDevices.end(); i++) {
if (i->second.name == output_device) {
newDemod->setOutputDevice(i->first);
matching_device_found = true;
break;
}
}
//if no device is found, choose the first of the list anyway.
if (!matching_device_found) {
newDemod->setOutputDevice(outputDevices.begin()->first);
}
return newDemod;
}