mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-26 21:58:37 -05:00
Clean-up/fix squelch
This commit is contained in:
parent
c503bb93da
commit
724808d9ff
@ -129,7 +129,10 @@ AppFrame::AppFrame() :
|
||||
demodTray->AddSpacer(1);
|
||||
|
||||
demodSignalMeter = new MeterCanvas(demodPanel, attribList);
|
||||
demodSignalMeter->setMax(0.5);
|
||||
demodSignalMeter->setMax(DEMOD_SIGNAL_MAX);
|
||||
demodSignalMeter->setMin(DEMOD_SIGNAL_MIN);
|
||||
demodSignalMeter->setLevel(DEMOD_SIGNAL_MIN);
|
||||
demodSignalMeter->setInputValue(DEMOD_SIGNAL_MIN);
|
||||
demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level.");
|
||||
demodSignalMeter->SetMinSize(wxSize(12,24));
|
||||
demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0);
|
||||
@ -644,7 +647,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
||||
wxGetApp().getDemodMgr().setLastMuted(false);
|
||||
wxGetApp().getDemodMgr().setLastBandwidth(DEFAULT_DEMOD_BW);
|
||||
wxGetApp().getDemodMgr().setLastGain(1.0);
|
||||
wxGetApp().getDemodMgr().setLastSquelchLevel(0);
|
||||
wxGetApp().getDemodMgr().setLastSquelchLevel(-100);
|
||||
waterfallCanvas->setBandwidth(wxGetApp().getSampleRate());
|
||||
waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
||||
spectrumCanvas->setBandwidth(wxGetApp().getSampleRate());
|
||||
|
@ -269,10 +269,6 @@ void DemodulatorInstance::setDemodulatorType(std::string demod_type_in) {
|
||||
} else {
|
||||
setBandwidth(AudioThread::deviceSampleRate[getOutputDevice()]);
|
||||
}
|
||||
} else if (currentDemodType == "USB" || currentDemodType == "LSB" || currentDemodType == "DSB" || currentDemodType == "AM") {
|
||||
demodulatorThread->setAGC(false);
|
||||
} else {
|
||||
demodulatorThread->setAGC(true);
|
||||
}
|
||||
setGain(getGain());
|
||||
|
||||
@ -400,18 +396,7 @@ int DemodulatorInstance::getAudioSampleRate() {
|
||||
|
||||
void DemodulatorInstance::setGain(float gain_in) {
|
||||
currentAudioGain = gain_in;
|
||||
|
||||
if (currentDemodType == "I/Q") {
|
||||
if (gain_in < 0.25) {
|
||||
audioThread->setGain(1.0);
|
||||
demodulatorThread->setAGC(false);
|
||||
} else {
|
||||
audioThread->setGain(gain_in);
|
||||
demodulatorThread->setAGC(true);
|
||||
}
|
||||
} else {
|
||||
audioThread->setGain(gain_in);
|
||||
}
|
||||
audioThread->setGain(gain_in);
|
||||
}
|
||||
|
||||
float DemodulatorInstance::getGain() {
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
DemodulatorMgr::DemodulatorMgr() :
|
||||
activeDemodulator(NULL), lastActiveDemodulator(NULL), activeVisualDemodulator(NULL), lastBandwidth(DEFAULT_DEMOD_BW), lastDemodType(
|
||||
DEFAULT_DEMOD_TYPE), lastSquelchEnabled(false), lastSquelch(0), lastGain(1.0), lastMuted(false) {
|
||||
DEFAULT_DEMOD_TYPE), lastSquelchEnabled(false), lastSquelch(-100), lastGain(1.0), lastMuted(false) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -12,12 +12,10 @@
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
DemodulatorThread::DemodulatorThread(DemodulatorInstance *parent) : IOThread(), iqAutoGain(NULL), audioSampleRate(0), squelchLevel(0), signalLevel(0), squelchEnabled(false), cModem(nullptr), cModemKit(nullptr), iqInputQueue(NULL), audioOutputQueue(NULL), audioVisOutputQueue(NULL), threadQueueControl(NULL), threadQueueNotify(NULL) {
|
||||
DemodulatorThread::DemodulatorThread(DemodulatorInstance *parent) : IOThread(), audioSampleRate(0), squelchLevel(-100), signalLevel(-100), squelchEnabled(false), cModem(nullptr), cModemKit(nullptr), iqInputQueue(NULL), audioOutputQueue(NULL), audioVisOutputQueue(NULL), threadQueueControl(NULL), threadQueueNotify(NULL) {
|
||||
|
||||
demodInstance = parent;
|
||||
muted.store(false);
|
||||
agcEnabled.store(false);
|
||||
|
||||
}
|
||||
|
||||
DemodulatorThread::~DemodulatorThread() {
|
||||
@ -30,6 +28,27 @@ void DemodulatorThread::onBindOutput(std::string name, ThreadQueueBase *threadQu
|
||||
}
|
||||
}
|
||||
|
||||
float DemodulatorThread::abMagnitude(double alpha, double beta, float inphase, float quadrature) {
|
||||
// http://dspguru.com/dsp/tricks/magnitude-estimator
|
||||
/* magnitude ~= alpha * max(|I|, |Q|) + beta * min(|I|, |Q|) */
|
||||
double abs_inphase = fabs(inphase);
|
||||
double abs_quadrature = fabs(quadrature);
|
||||
if (abs_inphase > abs_quadrature) {
|
||||
return alpha * abs_inphase + beta * abs_quadrature;
|
||||
} else {
|
||||
return alpha * abs_quadrature + beta * abs_inphase;
|
||||
}
|
||||
}
|
||||
|
||||
float DemodulatorThread::linearToDb(float linear) {
|
||||
// http://dspguru.com/dsp/tricks/magnitude-estimator
|
||||
#define SMALL 1e-20
|
||||
if (linear <= SMALL) {
|
||||
linear = SMALL;
|
||||
}
|
||||
return 20.0 * log10(linear);
|
||||
}
|
||||
|
||||
void DemodulatorThread::run() {
|
||||
#ifdef __APPLE__
|
||||
pthread_t tID = pthread_self(); // ID of this thread
|
||||
@ -38,10 +57,6 @@ void DemodulatorThread::run() {
|
||||
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
||||
#endif
|
||||
|
||||
// Automatic IQ gain
|
||||
iqAutoGain = agc_crcf_create();
|
||||
agc_crcf_set_bandwidth(iqAutoGain, 0.1);
|
||||
|
||||
ReBuffer<AudioThreadInput> audioVisBuffers;
|
||||
|
||||
std::cout << "Demodulator thread started.." << std::endl;
|
||||
@ -84,30 +99,21 @@ void DemodulatorThread::run() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (agcData.size() != bufSize) {
|
||||
if (agcData.capacity() < bufSize) {
|
||||
agcData.reserve(bufSize);
|
||||
}
|
||||
agcData.resize(bufSize);
|
||||
float currentSignalLevel = 0;
|
||||
float accum = 0;
|
||||
|
||||
for (std::vector<liquid_float_complex>::iterator i = inp->data.begin(); i != inp->data.end(); i++) {
|
||||
accum += abMagnitude(0.948059448969, 0.392699081699, i->real, i->imag);
|
||||
}
|
||||
|
||||
agc_crcf_execute_block(iqAutoGain, &(inp->data[0]), bufSize, &agcData[0]);
|
||||
|
||||
float currentSignalLevel = 0;
|
||||
|
||||
currentSignalLevel = ((60.0 / fabs(agc_crcf_get_rssi(iqAutoGain))) / 15.0 - signalLevel);
|
||||
|
||||
if (agc_crcf_get_signal_level(iqAutoGain) > currentSignalLevel) {
|
||||
currentSignalLevel = agc_crcf_get_signal_level(iqAutoGain);
|
||||
currentSignalLevel = linearToDb(accum / float(inp->data.size()));
|
||||
if (currentSignalLevel < DEMOD_SIGNAL_MIN+1) {
|
||||
currentSignalLevel = DEMOD_SIGNAL_MIN+1;
|
||||
}
|
||||
|
||||
std::vector<liquid_float_complex> *inputData;
|
||||
|
||||
if (agcEnabled) {
|
||||
inputData = &agcData;
|
||||
} else {
|
||||
inputData = &inp->data;
|
||||
}
|
||||
inputData = &inp->data;
|
||||
|
||||
modemData.sampleRate = inp->sampleRate;
|
||||
modemData.data.assign(inputData->begin(), inputData->end());
|
||||
@ -133,17 +139,20 @@ void DemodulatorThread::run() {
|
||||
signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.05;
|
||||
}
|
||||
|
||||
if (audioOutputQueue != NULL) {
|
||||
if (ati && (!squelchEnabled || (signalLevel >= squelchLevel))) {
|
||||
std::vector<float>::iterator data_i;
|
||||
ati->peak = 0;
|
||||
for (data_i = ati->data.begin(); data_i != ati->data.end(); data_i++) {
|
||||
float p = fabs(*data_i);
|
||||
if (p > ati->peak) {
|
||||
ati->peak = p;
|
||||
}
|
||||
bool squelched = (squelchEnabled && (signalLevel < squelchLevel));
|
||||
|
||||
if (audioOutputQueue != NULL && ati && !squelched) {
|
||||
std::vector<float>::iterator data_i;
|
||||
ati->peak = 0;
|
||||
for (data_i = ati->data.begin(); data_i != ati->data.end(); data_i++) {
|
||||
float p = fabs(*data_i);
|
||||
if (p > ati->peak) {
|
||||
ati->peak = p;
|
||||
}
|
||||
}
|
||||
} else if (ati) {
|
||||
ati->decRefCount();
|
||||
ati = nullptr;
|
||||
}
|
||||
|
||||
if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) {
|
||||
@ -164,8 +173,8 @@ void DemodulatorThread::run() {
|
||||
|
||||
if (inp->modemType == "I/Q") {
|
||||
for (int i = 0; i < stereoSize / 2; i++) {
|
||||
ati_vis->data[i] = agcData[i].real * 0.75;
|
||||
ati_vis->data[i + stereoSize / 2] = agcData[i].imag * 0.75;
|
||||
ati_vis->data[i] = (*inputData)[i].real * 0.75;
|
||||
ati_vis->data[i + stereoSize / 2] = (*inputData)[i].imag * 0.75;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < stereoSize / 2; i++) {
|
||||
@ -192,7 +201,6 @@ void DemodulatorThread::run() {
|
||||
ati_vis->data.assign(demodOutData->begin(), demodOutData->begin() + num_vis);
|
||||
}
|
||||
|
||||
// std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl;
|
||||
}
|
||||
|
||||
audioVisOutputQueue->push(ati_vis);
|
||||
@ -258,14 +266,6 @@ void DemodulatorThread::setMuted(bool muted) {
|
||||
this->muted.store(muted);
|
||||
}
|
||||
|
||||
void DemodulatorThread::setAGC(bool state) {
|
||||
agcEnabled.store(state);
|
||||
}
|
||||
|
||||
bool DemodulatorThread::getAGC() {
|
||||
return agcEnabled.load();
|
||||
}
|
||||
|
||||
float DemodulatorThread::getSignalLevel() {
|
||||
return signalLevel.load();
|
||||
}
|
||||
|
@ -10,6 +10,9 @@
|
||||
typedef ThreadQueue<AudioThreadInput *> DemodulatorThreadOutputQueue;
|
||||
|
||||
#define DEMOD_VIS_SIZE 2048
|
||||
#define DEMOD_SIGNAL_MIN -30
|
||||
#define DEMOD_SIGNAL_MAX 30
|
||||
|
||||
class DemodulatorInstance;
|
||||
|
||||
class DemodulatorThread : public IOThread {
|
||||
@ -23,9 +26,6 @@ public:
|
||||
void run();
|
||||
void terminate();
|
||||
|
||||
void setAGC(bool state);
|
||||
bool getAGC();
|
||||
|
||||
void setMuted(bool state);
|
||||
bool isMuted();
|
||||
|
||||
@ -33,16 +33,16 @@ public:
|
||||
void setSquelchLevel(float signal_level_in);
|
||||
float getSquelchLevel();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
float abMagnitude(double alpha, double beta, float inphase, float quadrature);
|
||||
float linearToDb(float linear);
|
||||
|
||||
DemodulatorInstance *demodInstance;
|
||||
ReBuffer<AudioThreadInput> outputBuffers;
|
||||
|
||||
std::vector<liquid_float_complex> agcData;
|
||||
|
||||
agc_crcf iqAutoGain;
|
||||
|
||||
std::atomic_bool muted;
|
||||
std::atomic_bool agcEnabled;
|
||||
int audioSampleRate;
|
||||
|
||||
std::atomic<float> squelchLevel;
|
||||
|
@ -25,7 +25,7 @@ EVT_ENTER_WINDOW(MeterCanvas::OnMouseEnterWindow)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
MeterCanvas::MeterCanvas(wxWindow *parent, int *attribList) :
|
||||
InteractiveCanvas(parent, attribList), level(0), level_max(1), inputValue(0), userInputValue(0), showUserInput(true) {
|
||||
InteractiveCanvas(parent, attribList), level(0), level_min(0), level_max(1), inputValue(0), userInputValue(0), showUserInput(true) {
|
||||
|
||||
glContext = new MeterContext(this, &wxGetApp().GetContext(this));
|
||||
}
|
||||
@ -47,6 +47,11 @@ void MeterCanvas::setMax(float max_in) {
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void MeterCanvas::setMin(float min_in) {
|
||||
level_min = min_in;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void MeterCanvas::setInputValue(float slider_in) {
|
||||
userInputValue = inputValue = slider_in;
|
||||
Refresh();
|
||||
@ -80,10 +85,10 @@ void MeterCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
if (mouseTracker.mouseInView()) {
|
||||
glContext->Draw(0.4, 0.4, 0.4, 0.5, mouseTracker.getMouseY());
|
||||
}
|
||||
glContext->Draw(ThemeMgr::mgr.currentTheme->meterLevel.r, ThemeMgr::mgr.currentTheme->meterLevel.g, ThemeMgr::mgr.currentTheme->meterLevel.b, 0.5, level / level_max);
|
||||
glContext->Draw(ThemeMgr::mgr.currentTheme->meterLevel.r, ThemeMgr::mgr.currentTheme->meterLevel.g, ThemeMgr::mgr.currentTheme->meterLevel.b, 0.5, (level-level_min) / (level_max-level_min));
|
||||
if (showUserInput) {
|
||||
glContext->Draw(ThemeMgr::mgr.currentTheme->meterValue.r, ThemeMgr::mgr.currentTheme->meterValue.g, ThemeMgr::mgr.currentTheme->meterValue.b, 0.5, userInputValue / level_max);
|
||||
}
|
||||
glContext->Draw(ThemeMgr::mgr.currentTheme->meterValue.r, ThemeMgr::mgr.currentTheme->meterValue.g, ThemeMgr::mgr.currentTheme->meterValue.b, 0.5, (userInputValue-level_min) / (level_max-level_min));
|
||||
}
|
||||
glContext->DrawEnd();
|
||||
|
||||
SwapBuffers();
|
||||
@ -101,7 +106,7 @@ void MeterCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseMoved(event);
|
||||
|
||||
if (mouseTracker.mouseDown()) {
|
||||
userInputValue = mouseTracker.getMouseY() * level_max;
|
||||
userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min;
|
||||
} else {
|
||||
if (!helpTip.empty()) {
|
||||
setStatusText(helpTip);
|
||||
@ -111,7 +116,7 @@ void MeterCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
|
||||
void MeterCanvas::OnMouseDown(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseDown(event);
|
||||
userInputValue = mouseTracker.getMouseY() * level_max;
|
||||
userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min;
|
||||
mouseTracker.setHorizDragLock(true);
|
||||
Refresh();
|
||||
}
|
||||
@ -123,7 +128,7 @@ void MeterCanvas::OnMouseWheelMoved(wxMouseEvent& event) {
|
||||
|
||||
void MeterCanvas::OnMouseReleased(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseReleased(event);
|
||||
userInputValue = mouseTracker.getMouseY() * level_max;
|
||||
userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ public:
|
||||
float getLevel();
|
||||
|
||||
void setMax(float max_in);
|
||||
void setMin(float max_in);
|
||||
|
||||
void setInputValue(float slider_in);
|
||||
bool inputChanged();
|
||||
@ -44,7 +45,7 @@ private:
|
||||
MeterContext *glContext;
|
||||
|
||||
float level;
|
||||
float level_max;
|
||||
float level_min, level_max;
|
||||
|
||||
float inputValue;
|
||||
float userInputValue;
|
||||
|
Loading…
Reference in New Issue
Block a user