Clean-up/fix squelch

This commit is contained in:
Charles J. Cliffe 2015-11-21 15:12:20 -05:00
parent c503bb93da
commit 724808d9ff
7 changed files with 74 additions and 80 deletions

View File

@ -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());

View File

@ -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() {

View File

@ -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) {
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;