diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index df5e13c..2c80502 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -113,12 +113,12 @@ void AppFrame::OnIdle(wxIdleEvent& event) { wxGetApp().getAudioVisualQueue()->pop(demodAudioData); if (demodAudioData.data.size()) { - if (scopeCanvas->waveform_points.size() != demodAudioData.data.size()) { - scopeCanvas->waveform_points.resize(demodAudioData.data.size()); + if (scopeCanvas->waveform_points.size() != demodAudioData.data.size()*2) { + scopeCanvas->waveform_points.resize(demodAudioData.data.size()*2); } - for (int i = 0, iMax = demodAudioData.data.size() / 2; i < iMax; i++) { - scopeCanvas->waveform_points[i * 2 + 1] = demodAudioData.data[i * 2] * 0.5f; + for (int i = 0, iMax = demodAudioData.data.size(); i < iMax; i++) { + scopeCanvas->waveform_points[i * 2 + 1] = demodAudioData.data[i] * 0.5f; scopeCanvas->waveform_points[i * 2] = ((double) i / (double) iMax); } diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index 53a186f..b429ec3 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -11,7 +11,7 @@ std::map AudioThread::deviceThread; AudioThread::AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify) : inputQueue(inputQueue), terminated(false), audio_queue_ptr(0), underflow_count(0), threadQueueNotify(threadQueueNotify), gain(1.0), active( - false) { + false) { #ifdef __APPLE__ boundThreads = new std::vector; #endif @@ -65,14 +65,35 @@ static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBu continue; } - for (int i = 0; i < nBufferFrames * 2; i++) { - if (srcmix->audio_queue_ptr >= srcmix->currentInput.data.size()) { + if (srcmix->currentInput.channels == 0) { + if (!srcmix->inputQueue->empty()) { srcmix->inputQueue->pop(srcmix->currentInput); - srcmix->audio_queue_ptr = 0; } - out[i] = out[i] + srcmix->currentInput.data[srcmix->audio_queue_ptr] * src->gain; - srcmix->audio_queue_ptr++; + continue; } + + if (srcmix->currentInput.channels == 1) { + for (int i = 0; i < nBufferFrames; i++) { + if (srcmix->audio_queue_ptr >= srcmix->currentInput.data.size()) { + srcmix->inputQueue->pop(srcmix->currentInput); + srcmix->audio_queue_ptr = 0; + } + float v = srcmix->currentInput.data[srcmix->audio_queue_ptr] * src->gain; + out[i * 2] += v; + out[i * 2 + 1] += v; + srcmix->audio_queue_ptr++; + } + } else { + for (int i = 0, iMax = src->currentInput.channels * nBufferFrames; i < iMax; i++) { + if (srcmix->audio_queue_ptr >= srcmix->currentInput.data.size()) { + srcmix->inputQueue->pop(srcmix->currentInput); + srcmix->audio_queue_ptr = 0; + } + out[i] = out[i] + srcmix->currentInput.data[srcmix->audio_queue_ptr] * src->gain; + srcmix->audio_queue_ptr++; + } + } + } return 0; @@ -89,23 +110,42 @@ static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBu std::cout << "Audio buffer underflow.." << (src->underflow_count++) << std::endl; } - for (int i = 0; i < nBufferFrames * 2; i++) { - if (src->audio_queue_ptr >= src->currentInput.data.size()) { - if (src->terminated) { - break; - } + if (src->currentInput.channels == 0) { + if (!src->inputQueue->empty()) { src->inputQueue->pop(src->currentInput); - src->audio_queue_ptr = 0; } - out[i] = src->currentInput.data[src->audio_queue_ptr] * src->gain; - src->audio_queue_ptr++; + return 0; } + if (src->currentInput.channels == 1) { + for (int i = 0; i < nBufferFrames; i++) { + if (src->audio_queue_ptr >= src->currentInput.data.size()) { + if (src->terminated) { + break; + } + src->inputQueue->pop(src->currentInput); + src->audio_queue_ptr = 0; + } + out[i * 2] = out[i * 2 + 1] = src->currentInput.data[src->audio_queue_ptr] * src->gain; + src->audio_queue_ptr++; + } + } else { + for (int i = 0, iMax = src->currentInput.channels * nBufferFrames; i < iMax; i++) { + if (src->audio_queue_ptr >= src->currentInput.data.size()) { + if (src->terminated) { + break; + } + src->inputQueue->pop(src->currentInput); + src->audio_queue_ptr = 0; + } + out[i] = src->currentInput.data[src->audio_queue_ptr] * src->gain; + src->audio_queue_ptr++; + } + } return 0; } #endif - void AudioThread::enumerateDevices() { int numDevices = dac.getDeviceCount(); @@ -158,7 +198,7 @@ void AudioThread::threadMain() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread int priority = sched_get_priority_max( SCHED_RR) - 1; - sched_param prio = { priority }; // scheduling priority of thread + sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_RR, &prio); #endif diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h index e295555..9ea135a 100644 --- a/src/audio/AudioThread.h +++ b/src/audio/AudioThread.h @@ -22,6 +22,15 @@ class AudioThreadInput { public: int frequency; int sampleRate; + int channels; + + AudioThreadInput(): frequency(0), sampleRate(0), channels(0) { + + } + + ~AudioThreadInput() { + + } std::vector data; }; diff --git a/src/demod/DemodDefs.h b/src/demod/DemodDefs.h index 9e6ea0c..e5822c1 100644 --- a/src/demod/DemodDefs.h +++ b/src/demod/DemodDefs.h @@ -38,6 +38,21 @@ public: int int_value; }; +class DemodulatorThreadControlCommand { +public: + enum DemodulatorThreadControlCommandEnum { + DEMOD_THREAD_CMD_CTL_NULL, + DEMOD_THREAD_CMD_CTL_SQUELCH_AUTO, + DEMOD_THREAD_CMD_CTL_SQUELCH_OFF + }; + + DemodulatorThreadControlCommand() : + cmd(DEMOD_THREAD_CMD_CTL_NULL) { + } + + DemodulatorThreadControlCommandEnum cmd; +}; + class DemodulatorThreadIQData { public: unsigned int frequency; @@ -64,7 +79,7 @@ class DemodulatorThreadPostIQData { public: std::vector data; float audio_resample_ratio; - msresamp_crcf audio_resampler; + msresamp_rrrf audio_resampler; float resample_ratio; msresamp_crcf resampler; @@ -106,6 +121,7 @@ public: typedef ThreadQueue DemodulatorThreadInputQueue; typedef ThreadQueue DemodulatorThreadPostInputQueue; typedef ThreadQueue DemodulatorThreadCommandQueue; +typedef ThreadQueue DemodulatorThreadControlCommandQueue; class DemodulatorThreadParameters { diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 48297de..9d46565 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -2,17 +2,18 @@ DemodulatorInstance::DemodulatorInstance() : t_Demod(NULL), t_PreDemod(NULL), t_Audio(NULL), threadQueueDemod(NULL), demodulatorThread(NULL), terminated(false), audioTerminated(false), demodTerminated( - false), preDemodTerminated(false), active(false) { + false), preDemodTerminated(false), active(false), squelch(false) { label = new std::string("Unnamed"); threadQueueDemod = new DemodulatorThreadInputQueue; threadQueuePostDemod = new DemodulatorThreadPostInputQueue; threadQueueCommand = new DemodulatorThreadCommandQueue; threadQueueNotify = new DemodulatorThreadCommandQueue; + threadQueueControl = new DemodulatorThreadControlCommandQueue; - demodulatorPreThread = new DemodulatorPreThread(threadQueueDemod, threadQueuePostDemod, threadQueueNotify); + demodulatorPreThread = new DemodulatorPreThread(threadQueueDemod, threadQueuePostDemod, threadQueueControl, threadQueueNotify); demodulatorPreThread->setCommandQueue(threadQueueCommand); - demodulatorThread = new DemodulatorThread(threadQueuePostDemod, threadQueueNotify); + demodulatorThread = new DemodulatorThread(threadQueuePostDemod, threadQueueControl, threadQueueNotify); audioInputQueue = new AudioThreadInputQueue; audioThread = new AudioThread(audioInputQueue, threadQueueNotify); @@ -144,3 +145,30 @@ void DemodulatorInstance::setActive(bool state) { active = state; audioThread->setActive(state); } + + +void DemodulatorInstance::squelchAuto() { + DemodulatorThreadControlCommand command; + command.cmd = DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_AUTO; + threadQueueControl->push(command); + squelch = true; +} + +bool DemodulatorInstance::isSquelchEnabled() { + return squelch; +} + +void DemodulatorInstance::setSquelchEnabled(bool state) { + if (!state && squelch) { + DemodulatorThreadControlCommand command; + command.cmd = DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_OFF; + threadQueueControl->push(command); + } else if (state && !squelch) { + DemodulatorThreadControlCommand command; + command.cmd = DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_AUTO; + threadQueueControl->push(command); + } + + squelch = state; +} + diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index 9b759ce..ccd14be 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -16,6 +16,8 @@ public: DemodulatorThreadCommandQueue* threadQueueNotify; DemodulatorPreThread *demodulatorPreThread; DemodulatorThread *demodulatorThread; + DemodulatorThreadControlCommandQueue *threadQueueControl; + #ifdef __APPLE__ pthread_t t_PreDemod; pthread_t t_Demod; @@ -47,6 +49,10 @@ public: bool isActive(); void setActive(bool state); + void squelchAuto(); + bool isSquelchEnabled(); + void setSquelchEnabled(bool state); + private: std::atomic label; bool terminated; @@ -54,5 +60,6 @@ private: bool audioTerminated; bool preDemodTerminated; std::atomic active; + std::atomic squelch; }; diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index b872dc5..19775ec 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -8,9 +8,9 @@ #include "DemodulatorPreThread.h" DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn, DemodulatorThreadPostInputQueue* pQueueOut, - DemodulatorThreadCommandQueue* threadQueueNotify) : + DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) : inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), resample_ratio(1), audio_resample_ratio( - 1), resampler(NULL), commandQueue(NULL), fir_filter(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify) { + 1), resampler(NULL), commandQueue(NULL), fir_filter(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl(threadQueueControl) { float kf = 0.5; // modulation factor fdem = freqdem_create(kf); @@ -65,9 +65,9 @@ void DemodulatorPreThread::initialize() { // msresamp_crcf_print(resampler); if (audio_resampler) { - msresamp_crcf_destroy(audio_resampler); + msresamp_rrrf_destroy(audio_resampler); } - audio_resampler = msresamp_crcf_create(audio_resample_ratio, As); + audio_resampler = msresamp_rrrf_create(audio_resample_ratio, As); // msresamp_crcf_print(audio_resampler); initialized = true; diff --git a/src/demod/DemodulatorPreThread.h b/src/demod/DemodulatorPreThread.h index f11ca03..0295984 100644 --- a/src/demod/DemodulatorPreThread.h +++ b/src/demod/DemodulatorPreThread.h @@ -11,7 +11,7 @@ class DemodulatorPreThread { public: DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn, DemodulatorThreadPostInputQueue* pQueueOut, - DemodulatorThreadCommandQueue* threadQueueNotify); + DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify); ~DemodulatorPreThread(); #ifdef __APPLE__ @@ -28,6 +28,11 @@ public: audioInputQueue = tQueue; } + void setDemodulatorControlQueue(DemodulatorThreadControlCommandQueue *tQueue) { + threadQueueControl = tQueue; + } + + DemodulatorThreadParameters &getParams() { return params; } @@ -52,7 +57,7 @@ protected: msresamp_crcf resampler; float resample_ratio; - msresamp_crcf audio_resampler; + msresamp_rrrf audio_resampler; float audio_resample_ratio; DemodulatorThreadParameters params; @@ -71,4 +76,5 @@ protected: DemodulatorThreadWorkerCommandQueue *workerQueue; DemodulatorThreadWorkerResultQueue *workerResults; DemodulatorThreadCommandQueue* threadQueueNotify; + DemodulatorThreadControlCommandQueue *threadQueueControl; }; diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 2c1e9b4..f9befc5 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -6,8 +6,8 @@ #include #endif -DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* pQueue, DemodulatorThreadCommandQueue* threadQueueNotify) : - postInputQueue(pQueue), visOutQueue(NULL), terminated(false), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify) { +DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* pQueue, DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) : + postInputQueue(pQueue), visOutQueue(NULL), terminated(false), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl(threadQueueControl), agc(NULL), squelch_enabled(false), squelch_level(0), squelch_tolerance(0) { float kf = 0.5; // modulation factor fdem = freqdem_create(kf); @@ -28,9 +28,12 @@ void DemodulatorThread::threadMain() { pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif - msresamp_crcf audio_resampler = NULL; + msresamp_rrrf audio_resampler = NULL; msresamp_crcf resampler = NULL; + agc = agc_crcf_create(); + agc_crcf_set_bandwidth(agc, 1e-3f); + std::cout << "Demodulator thread started.." << std::endl; while (!terminated) { DemodulatorThreadPostIQData inp; @@ -47,70 +50,95 @@ void DemodulatorThread::threadMain() { audio_resampler = inp.audio_resampler; } else if (resampler != inp.resampler) { msresamp_crcf_destroy(resampler); - msresamp_crcf_destroy(audio_resampler); + msresamp_rrrf_destroy(audio_resampler); resampler = inp.resampler; audio_resampler = inp.audio_resampler; } int out_size = ceil((float) (bufSize) * inp.resample_ratio); liquid_float_complex resampled_data[out_size]; + liquid_float_complex agc_data[out_size]; unsigned int num_written; msresamp_crcf_execute(resampler, &inp.data[0], bufSize, resampled_data, &num_written); - float audio_resample_ratio = inp.audio_resample_ratio; + agc_crcf_execute_block(agc, resampled_data, num_written, agc_data); + float audio_resample_ratio = inp.audio_resample_ratio; float demod_output[num_written]; - freqdem_demodulate_block(fdem, resampled_data, num_written, demod_output); - - liquid_float_complex demod_audio_data[num_written]; - - for (int i = 0; i < num_written; i++) { - demod_audio_data[i].real = demod_output[i]; - demod_audio_data[i].imag = 0; - } + freqdem_demodulate_block(fdem, agc_data, num_written, demod_output); int audio_out_size = ceil((float) (num_written) * audio_resample_ratio); - liquid_float_complex resampled_audio_output[audio_out_size]; + float resampled_audio_output[audio_out_size]; unsigned int num_audio_written; - msresamp_crcf_execute(audio_resampler, demod_audio_data, num_written, resampled_audio_output, &num_audio_written); - - std::vector newBuffer; - newBuffer.resize(num_audio_written * 2); - for (int i = 0; i < num_audio_written; i++) { - liquid_float_complex y = resampled_audio_output[i]; - - newBuffer[i * 2] = y.real; - newBuffer[i * 2 + 1] = y.real; - } + msresamp_rrrf_execute(audio_resampler, demod_output, num_written, resampled_audio_output, &num_audio_written); AudioThreadInput ati; - ati.data = newBuffer; + ati.channels = 1; + ati.data.assign(resampled_audio_output,resampled_audio_output+num_audio_written); if (audioInputQueue != NULL) { - audioInputQueue->push(ati); + if (!squelch_enabled || ((agc_crcf_get_signal_level(agc)) >= 0.1)) { + audioInputQueue->push(ati); + } } if (visOutQueue != NULL && visOutQueue->empty()) { + AudioThreadInput ati_vis; + ati_vis.channels = ati.channels; + + int num_vis = DEMOD_VIS_SIZE; if (num_audio_written > num_written) { - visOutQueue->push(ati); + if (num_vis > num_audio_written) { + num_vis = num_audio_written; + } + ati_vis.data.assign(ati.data.begin(), ati.data.begin()+num_vis); } else { - AudioThreadInput ati_vis; - ati_vis.data.assign(demod_output, demod_output + num_written); - visOutQueue->push(ati_vis); + if (num_vis > num_written) { + num_vis = num_written; + } + ati_vis.data.assign(demod_output, demod_output + num_vis); + } + + visOutQueue->push(ati_vis); +// std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl; + } + + if (!threadQueueControl->empty()) { + while (!threadQueueControl->empty()) { + DemodulatorThreadControlCommand command; + threadQueueControl->pop(command); + + switch (command.cmd) { + case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_AUTO: + squelch_level = agc_crcf_get_signal_level(agc); + squelch_tolerance = agc_crcf_get_signal_level(agc)/2.0; + squelch_enabled = true; + break; + case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_OFF: + squelch_level = 0; + squelch_tolerance = 1; + squelch_enabled = false; + break; + default: + break; + } } } + } if (resampler != NULL) { msresamp_crcf_destroy(resampler); } if (audio_resampler != NULL) { - msresamp_crcf_destroy(audio_resampler); + msresamp_rrrf_destroy(audio_resampler); } + agc_crcf_destroy(agc); + std::cout << "Demodulator thread done." << std::endl; DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_TERMINATED); tCmd.context = this; diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 2a126a3..5327620 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -8,10 +8,12 @@ typedef ThreadQueue DemodulatorThreadOutputQueue; +#define DEMOD_VIS_SIZE 2048 + class DemodulatorThread { public: - DemodulatorThread(DemodulatorThreadPostInputQueue* pQueueIn, DemodulatorThreadCommandQueue* threadQueueNotify); + DemodulatorThread(DemodulatorThreadPostInputQueue* pQueueIn, DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify); ~DemodulatorThread(); #ifdef __APPLE__ @@ -44,8 +46,13 @@ protected: AudioThreadInputQueue *audioInputQueue; freqdem fdem; + agc_crcf agc; std::atomic terminated; DemodulatorThreadCommandQueue* threadQueueNotify; + DemodulatorThreadControlCommandQueue *threadQueueControl; + float squelch_level; + float squelch_tolerance; + bool squelch_enabled; }; diff --git a/src/demod/DemodulatorWorkerThread.cpp b/src/demod/DemodulatorWorkerThread.cpp index f002737..8cadf90 100644 --- a/src/demod/DemodulatorWorkerThread.cpp +++ b/src/demod/DemodulatorWorkerThread.cpp @@ -58,7 +58,7 @@ void DemodulatorWorkerThread::threadMain() { result.fir_filter = firfilt_crcf_create(h, h_len); result.resampler = msresamp_crcf_create(result.resample_ratio, As); - result.audio_resampler = msresamp_crcf_create(result.audio_resample_ratio, As); + result.audio_resampler = msresamp_rrrf_create(result.audio_resample_ratio, As); result.audioSampleRate = filterCommand.audioSampleRate; result.bandwidth = filterCommand.bandwidth; diff --git a/src/demod/DemodulatorWorkerThread.h b/src/demod/DemodulatorWorkerThread.h index 9911972..950b5f1 100644 --- a/src/demod/DemodulatorWorkerThread.h +++ b/src/demod/DemodulatorWorkerThread.h @@ -36,7 +36,7 @@ public: firfilt_crcf fir_filter; msresamp_crcf resampler; float resample_ratio; - msresamp_crcf audio_resampler; + msresamp_rrrf audio_resampler; float audio_resample_ratio; unsigned int inputRate; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index d493029..6318996 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -181,7 +181,16 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { wxGetApp().removeDemodulator(activeDemod); wxGetApp().getDemodMgr().deleteThread(activeDemod); break; - + case 'S': + if (!activeDemod) { + break; + } + if (activeDemod->isSquelchEnabled()) { + activeDemod->setSquelchEnabled(false); + } else { + activeDemod->squelchAuto(); + } + break; default: event.Skip(); return;