CubicSDR/src/demod/DemodulatorThread.cpp

522 lines
17 KiB
C++
Raw Normal View History

2014-12-16 18:27:32 -05:00
#include "CubicSDRDefs.h"
2015-02-22 01:01:28 -05:00
#include "DemodulatorThread.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
2014-12-16 18:27:32 -05:00
#ifdef __APPLE__
#include <pthread.h>
#endif
DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* iqInputQueue, DemodulatorThreadControlCommandQueue *threadQueueControl,
2014-12-24 01:28:33 -05:00
DemodulatorThreadCommandQueue* threadQueueNotify) :
iqInputQueue(iqInputQueue), audioVisOutputQueue(NULL), audioOutputQueue(NULL), iqAutoGain(NULL), amOutputCeil(1), amOutputCeilMA(1), amOutputCeilMAA(
1), stereo(false), terminated(
false), demodulatorType(DEMOD_TYPE_FM), threadQueueNotify(threadQueueNotify), threadQueueControl(threadQueueControl), squelchLevel(0), signalLevel(
0), squelchEnabled(false), audioSampleRate(0) {
2014-12-16 18:27:32 -05:00
demodFM = freqdem_create(0.5);
2015-02-12 01:38:53 -05:00
demodAM_USB = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_USB, 1);
demodAM_LSB = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_LSB, 1);
demodAM_DSB = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_DSB, 1);
demodAM_DSB_CSP = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_DSB, 0);
demodAM = demodAM_DSB_CSP;
2015-01-01 03:48:32 -05:00
2014-12-16 18:27:32 -05:00
}
DemodulatorThread::~DemodulatorThread() {
}
#ifdef __APPLE__
void *DemodulatorThread::threadMain() {
#else
2014-12-16 21:30:03 -05:00
void DemodulatorThread::threadMain() {
2014-12-16 18:27:32 -05:00
#endif
#ifdef __APPLE__
pthread_t tID = pthread_self(); // ID of this thread
int priority = sched_get_priority_max( SCHED_FIFO )-1;
sched_param prio = {priority}; // scheduling priority of thread
pthread_setschedparam(tID, SCHED_FIFO, &prio);
#endif
msresamp_rrrf audioResampler = NULL;
msresamp_rrrf stereoResampler = NULL;
firfilt_rrrf firStereoLeft = NULL;
firfilt_rrrf firStereoRight = NULL;
iirfilt_crcf iirStereoPilot = NULL;
2014-12-16 18:27:32 -05:00
2015-06-05 20:58:10 -04:00
liquid_float_complex u, v, w, x, y;
firhilbf firStereoR2C = firhilbf_create(5, 60.0f);
firhilbf firStereoC2R = firhilbf_create(5, 60.0f);
2015-01-01 18:08:54 -05:00
2015-06-05 20:58:10 -04:00
nco_crcf stereoPilot = nco_crcf_create(LIQUID_VCO);
nco_crcf_reset(stereoPilot);
2015-06-05 20:58:10 -04:00
nco_crcf_pll_set_bandwidth(stereoPilot, 0.25f);
// half band filter used for side-band elimination
resamp2_cccf ssbFilt = resamp2_cccf_create(12,-0.25f,60.0f);
2015-01-25 01:27:37 -05:00
// Automatic IQ gain
iqAutoGain = agc_crcf_create();
agc_crcf_set_bandwidth(iqAutoGain, 0.9);
AudioThreadInput *ati_vis = new AudioThreadInput;
ati_vis->data.reserve(DEMOD_VIS_SIZE);
2014-12-16 21:30:03 -05:00
std::cout << "Demodulator thread started.." << std::endl;
2014-12-24 01:28:33 -05:00
2015-02-03 12:25:02 -05:00
switch (demodulatorType.load()) {
case DEMOD_TYPE_FM:
break;
case DEMOD_TYPE_LSB:
demodAM = demodAM_LSB;
break;
case DEMOD_TYPE_USB:
demodAM = demodAM_USB;
break;
case DEMOD_TYPE_DSB:
demodAM = demodAM_DSB;
break;
case DEMOD_TYPE_AM:
demodAM = demodAM_DSB_CSP;
break;
}
2015-01-08 21:12:49 -05:00
terminated = false;
2014-12-16 21:30:03 -05:00
while (!terminated) {
2014-12-23 01:12:14 -05:00
DemodulatorThreadPostIQData *inp;
iqInputQueue->pop(inp);
2014-12-24 01:28:33 -05:00
std::lock_guard < std::mutex > lock(inp->m_mutex);
2014-12-16 18:27:32 -05:00
2014-12-23 01:12:14 -05:00
int bufSize = inp->data.size();
2014-12-16 18:27:32 -05:00
2014-12-16 21:30:03 -05:00
if (!bufSize) {
2014-12-24 01:28:33 -05:00
inp->decRefCount();
2014-12-16 21:30:03 -05:00
continue;
}
2014-12-16 18:27:32 -05:00
if (audioResampler == NULL) {
audioResampler = inp->audioResampler;
stereoResampler = inp->stereoResampler;
firStereoLeft = inp->firStereoLeft;
firStereoRight = inp->firStereoRight;
iirStereoPilot = inp->iirStereoPilot;
audioSampleRate = inp->audioSampleRate;
} else if (audioResampler != inp->audioResampler) {
msresamp_rrrf_destroy(audioResampler);
msresamp_rrrf_destroy(stereoResampler);
audioResampler = inp->audioResampler;
stereoResampler = inp->stereoResampler;
audioSampleRate = inp->audioSampleRate;
2015-01-01 03:48:32 -05:00
if (demodAM) {
ampmodem_reset(demodAM);
}
freqdem_reset(demodFM);
2014-12-16 21:30:03 -05:00
}
if (firStereoLeft != inp->firStereoLeft) {
if (firStereoLeft != NULL) {
firfilt_rrrf_destroy(firStereoLeft);
}
firStereoLeft = inp->firStereoLeft;
}
if (firStereoRight != inp->firStereoRight) {
if (firStereoRight != NULL) {
firfilt_rrrf_destroy(firStereoRight);
}
firStereoRight = inp->firStereoRight;
}
if (iirStereoPilot != inp->iirStereoPilot) {
if (iirStereoPilot != NULL) {
iirfilt_crcf_destroy(iirStereoPilot);
}
iirStereoPilot = inp->iirStereoPilot;
}
if (agcData.size() != bufSize) {
if (agcData.capacity() < bufSize) {
agcData.reserve(bufSize);
agcAMData.reserve(bufSize);
}
agcData.resize(bufSize);
agcAMData.resize(bufSize);
}
2014-12-16 21:30:03 -05:00
double audio_resample_ratio = inp->audioResampleRatio;
2014-12-16 18:27:32 -05:00
if (demodOutputData.size() != bufSize) {
if (demodOutputData.capacity() < bufSize) {
demodOutputData.reserve(bufSize);
}
demodOutputData.resize(bufSize);
}
int audio_out_size = ceil((double) (bufSize) * audio_resample_ratio) + 512;
agc_crcf_execute_block(iqAutoGain, &(inp->data[0]), bufSize, &agcData[0]);
2015-01-01 03:48:32 -05:00
float currentSignalLevel = 0;
2015-01-01 03:48:32 -05:00
currentSignalLevel = ((60.0 / fabs(agc_crcf_get_rssi(iqAutoGain))) / 15.0 - signalLevel);
2015-01-01 03:48:32 -05:00
if (agc_crcf_get_signal_level(iqAutoGain) > currentSignalLevel) {
currentSignalLevel = agc_crcf_get_signal_level(iqAutoGain);
2015-01-01 03:48:32 -05:00
}
2015-01-01 18:08:54 -05:00
if (demodulatorType == DEMOD_TYPE_FM) {
freqdem_demodulate_block(demodFM, &agcData[0], bufSize, &demodOutputData[0]);
2015-01-01 18:08:54 -05:00
} else {
float p;
2015-01-03 19:03:16 -05:00
switch (demodulatorType.load()) {
2015-01-01 18:08:54 -05:00
case DEMOD_TYPE_LSB:
for (int i = 0; i < bufSize; i++) { // Reject upper band
resamp2_cccf_filter_execute(ssbFilt,inp->data[i],&x,&y);
ampmodem_demodulate(demodAM, x, &demodOutputData[i]);
}
2015-01-01 18:08:54 -05:00
break;
case DEMOD_TYPE_USB:
for (int i = 0; i < bufSize; i++) { // Reject lower band
resamp2_cccf_filter_execute(ssbFilt,inp->data[i],&x,&y);
ampmodem_demodulate(demodAM, y, &demodOutputData[i]);
}
2015-01-01 18:08:54 -05:00
break;
case DEMOD_TYPE_AM:
case DEMOD_TYPE_DSB:
2015-02-12 02:14:22 -05:00
for (int i = 0; i < bufSize; i++) {
ampmodem_demodulate(demodAM, inp->data[i], &demodOutputData[i]);
}
2015-01-01 18:08:54 -05:00
break;
2015-01-01 03:48:32 -05:00
}
amOutputCeilMA = amOutputCeilMA + (amOutputCeil - amOutputCeilMA) * 0.025;
amOutputCeilMAA = amOutputCeilMAA + (amOutputCeilMA - amOutputCeilMAA) * 0.025;
2015-01-01 03:48:32 -05:00
2015-02-12 02:14:22 -05:00
amOutputCeil = 0;
for (int i = 0; i < bufSize; i++) {
if (demodOutputData[i] > amOutputCeil) {
amOutputCeil = demodOutputData[i];
}
}
float gain = 0.5 / amOutputCeilMAA;
2015-01-01 03:48:32 -05:00
for (int i = 0; i < bufSize; i++) {
demodOutputData[i] *= gain;
2015-01-01 03:48:32 -05:00
}
}
2014-12-16 18:27:32 -05:00
if (audio_out_size != resampledOutputData.size()) {
if (resampledOutputData.capacity() < audio_out_size) {
resampledOutputData.reserve(audio_out_size);
}
resampledOutputData.resize(audio_out_size);
}
unsigned int numAudioWritten;
msresamp_rrrf_execute(audioResampler, &demodOutputData[0], bufSize, &resampledOutputData[0], &numAudioWritten);
2014-12-26 20:58:42 -05:00
if (stereo) {
if (demodStereoData.size() != bufSize) {
if (demodStereoData.capacity() < bufSize) {
demodStereoData.reserve(bufSize);
}
demodStereoData.resize(bufSize);
}
2014-12-26 20:58:42 -05:00
2015-06-05 20:58:10 -04:00
float phase_error = 0;
2015-06-05 20:58:10 -04:00
for (int i = 0; i < bufSize; i++) {
2015-06-05 20:58:10 -04:00
// real -> complex
firhilbf_r2c_execute(firStereoR2C, demodOutputData[i], &x);
2015-06-05 20:58:10 -04:00
// 19khz pilot band-pass
iirfilt_crcf_execute(iirStereoPilot, x, &v);
nco_crcf_cexpf(stereoPilot, &w);
2015-06-05 20:58:10 -04:00
w.imag = -w.imag; // conjf(w)
2015-06-05 20:58:10 -04:00
// multiply u = v * conjf(w)
u.real = v.real * w.real - v.imag * w.imag;
u.imag = v.real * w.imag + v.imag * w.real;
// cargf(u)
phase_error = atan2f(u.imag,u.real);
2015-06-05 20:58:10 -04:00
// step pll
nco_crcf_pll_step(stereoPilot, phase_error);
2015-06-05 20:58:10 -04:00
nco_crcf_step(stereoPilot);
// 38khz down-mix
nco_crcf_mix_down(stereoPilot, x, &y);
nco_crcf_mix_down(stereoPilot, y, &x);
// complex -> real
firhilbf_c2r_execute(firStereoC2R, x, &demodStereoData[i]);
2014-12-26 20:58:42 -05:00
}
2015-06-05 20:58:10 -04:00
2015-06-05 00:35:18 -04:00
// std::cout << "[PLL] phase error: " << phase_error;
// std::cout << " freq:" << (((nco_crcf_get_frequency(stereoPilot) / (2.0 * M_PI)) * inp->sampleRate)) << std::endl;
if (audio_out_size != resampledStereoData.size()) {
if (resampledStereoData.capacity() < audio_out_size) {
resampledStereoData.reserve(audio_out_size);
}
resampledStereoData.resize(audio_out_size);
}
2014-12-16 18:27:32 -05:00
msresamp_rrrf_execute(stereoResampler, &demodStereoData[0], bufSize, &resampledStereoData[0], &numAudioWritten);
2014-12-26 20:58:42 -05:00
}
if (currentSignalLevel > signalLevel) {
signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.5;
2014-12-31 19:45:01 -05:00
} else {
signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.05;
2014-12-31 19:45:01 -05:00
}
AudioThreadInput *ati = NULL;
if (audioOutputQueue != NULL) {
if (!squelchEnabled || (signalLevel >= squelchLevel)) {
2014-12-24 01:28:33 -05:00
for (outputBuffersI = outputBuffers.begin(); outputBuffersI != outputBuffers.end(); outputBuffersI++) {
if ((*outputBuffersI)->getRefCount() <= 0) {
ati = (*outputBuffersI);
2014-12-24 01:28:33 -05:00
break;
}
}
if (ati == NULL) {
ati = new AudioThreadInput;
outputBuffers.push_back(ati);
2014-12-24 01:28:33 -05:00
}
ati->sampleRate = audioSampleRate;
2014-12-24 01:28:33 -05:00
ati->setRefCount(1);
2014-12-26 20:58:42 -05:00
if (stereo) {
ati->channels = 2;
if (ati->data.capacity() < (numAudioWritten * 2)) {
ati->data.reserve(numAudioWritten * 2);
}
ati->data.resize(numAudioWritten * 2);
for (int i = 0; i < numAudioWritten; i++) {
float l, r;
2015-06-05 20:58:10 -04:00
firfilt_rrrf_push(firStereoLeft, 0.568 * (resampledOutputData[i] - (resampledStereoData[i])));
firfilt_rrrf_execute(firStereoLeft, &l);
2015-06-05 20:58:10 -04:00
firfilt_rrrf_push(firStereoRight, 0.568 * (resampledOutputData[i] + (resampledStereoData[i])));
firfilt_rrrf_execute(firStereoRight, &r);
ati->data[i * 2] = l;
ati->data[i * 2 + 1] = r;
2014-12-26 20:58:42 -05:00
}
} else {
ati->channels = 1;
ati->data.assign(resampledOutputData.begin(), resampledOutputData.begin() + numAudioWritten);
2014-12-26 20:58:42 -05:00
}
2014-12-24 01:28:33 -05:00
std::vector<float>::iterator data_i;
ati->peak = 0;
for (data_i = ati->data.begin(); data_i != ati->data.end(); data_i++) {
2015-02-12 02:14:22 -05:00
float p = fabs(*data_i);
if (p > ati->peak) {
ati->peak = p;
}
}
audioOutputQueue->push(ati);
}
2014-12-16 21:30:03 -05:00
}
2014-12-16 18:27:32 -05:00
if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) {
2015-05-27 23:22:19 -04:00
ati_vis->busy_update.lock();
int num_vis = DEMOD_VIS_SIZE;
2014-12-26 22:20:50 -05:00
if (stereo) {
ati_vis->channels = 2;
int stereoSize = ati->data.size();
2014-12-26 22:20:50 -05:00
if (stereoSize > DEMOD_VIS_SIZE) {
stereoSize = DEMOD_VIS_SIZE;
}
2014-12-26 22:20:50 -05:00
ati_vis->data.resize(stereoSize);
for (int i = 0; i < stereoSize / 2; i++) {
2015-01-01 03:48:32 -05:00
ati_vis->data[i] = ati->data[i * 2];
ati_vis->data[i + stereoSize / 2] = ati->data[i * 2 + 1];
}
} else {
ati_vis->channels = 1;
if (numAudioWritten > bufSize) {
2014-12-26 22:20:50 -05:00
if (num_vis > numAudioWritten) {
num_vis = numAudioWritten;
2014-12-26 22:20:50 -05:00
}
ati_vis->data.assign(resampledOutputData.begin(), resampledOutputData.begin() + num_vis);
2014-12-26 22:20:50 -05:00
} else {
if (num_vis > bufSize) {
num_vis = bufSize;
2014-12-26 22:20:50 -05:00
}
ati_vis->data.assign(demodOutputData.begin(), demodOutputData.begin() + num_vis);
}
2014-12-26 22:20:50 -05:00
// std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl;
}
2015-05-27 23:22:19 -04:00
ati_vis->busy_update.unlock();
audioVisOutputQueue->push(ati_vis);
}
if (!threadQueueControl->empty()) {
2015-01-01 18:08:54 -05:00
int newDemodType = DEMOD_TYPE_NULL;
while (!threadQueueControl->empty()) {
DemodulatorThreadControlCommand command;
threadQueueControl->pop(command);
switch (command.cmd) {
case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_ON:
squelchEnabled = true;
break;
case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_OFF:
squelchEnabled = false;
break;
2015-01-01 18:08:54 -05:00
case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_TYPE:
newDemodType = command.demodType;
break;
default:
break;
}
}
2015-01-01 18:08:54 -05:00
if (newDemodType != DEMOD_TYPE_NULL) {
switch (newDemodType) {
case DEMOD_TYPE_FM:
freqdem_reset(demodFM);
2015-01-01 18:08:54 -05:00
break;
case DEMOD_TYPE_LSB:
demodAM = demodAM_LSB;
ampmodem_reset(demodAM);
2015-01-01 18:08:54 -05:00
break;
case DEMOD_TYPE_USB:
demodAM = demodAM_USB;
ampmodem_reset(demodAM);
2015-01-01 18:08:54 -05:00
break;
case DEMOD_TYPE_DSB:
demodAM = demodAM_DSB;
ampmodem_reset(demodAM);
break;
2015-01-01 18:08:54 -05:00
case DEMOD_TYPE_AM:
demodAM = demodAM_DSB_CSP;
ampmodem_reset(demodAM);
2015-01-01 18:08:54 -05:00
break;
}
demodulatorType = newDemodType;
}
2014-12-16 21:30:03 -05:00
}
2014-12-24 01:28:33 -05:00
inp->decRefCount();
2014-12-16 21:30:03 -05:00
}
2014-12-16 18:27:32 -05:00
if (audioResampler != NULL) {
msresamp_rrrf_destroy(audioResampler);
2014-12-16 21:30:03 -05:00
}
if (stereoResampler != NULL) {
msresamp_rrrf_destroy(stereoResampler);
2014-12-26 21:55:13 -05:00
}
if (firStereoLeft != NULL) {
firfilt_rrrf_destroy(firStereoLeft);
}
if (firStereoRight != NULL) {
firfilt_rrrf_destroy(firStereoRight);
}
if (iirStereoPilot != NULL) {
iirfilt_crcf_destroy(iirStereoPilot);
}
agc_crcf_destroy(iqAutoGain);
firhilbf_destroy(firStereoR2C);
firhilbf_destroy(firStereoC2R);
nco_crcf_destroy(stereoPilot);
resamp2_cccf_destroy(ssbFilt);
while (!outputBuffers.empty()) {
AudioThreadInput *audioDataDel = outputBuffers.front();
outputBuffers.pop_front();
2014-12-24 01:28:33 -05:00
delete audioDataDel;
}
2015-01-22 23:41:33 -05:00
if (audioVisOutputQueue && !audioVisOutputQueue->empty()) {
AudioThreadInput *dummy_vis;
audioVisOutputQueue->pop(dummy_vis);
}
delete ati_vis;
2014-12-16 21:30:03 -05:00
DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_TERMINATED);
tCmd.context = this;
threadQueueNotify->push(tCmd);
std::cout << "Demodulator thread done." << std::endl;
#ifdef __APPLE__
return this;
#endif
2014-12-16 18:27:32 -05:00
}
void DemodulatorThread::setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue) {
audioVisOutputQueue = tQueue;
}
void DemodulatorThread::setAudioOutputQueue(AudioThreadInputQueue *tQueue) {
audioOutputQueue = tQueue;
}
2014-12-16 18:27:32 -05:00
void DemodulatorThread::terminate() {
2014-12-16 21:30:03 -05:00
terminated = true;
2014-12-23 01:12:14 -05:00
DemodulatorThreadPostIQData *inp = new DemodulatorThreadPostIQData; // push dummy to nudge queue
iqInputQueue->push(inp);
2014-12-16 18:27:32 -05:00
}
2014-12-26 20:58:42 -05:00
void DemodulatorThread::setStereo(bool state) {
stereo = state;
2014-12-26 22:20:50 -05:00
std::cout << "Stereo " << (state ? "Enabled" : "Disabled") << std::endl;
2014-12-26 20:58:42 -05:00
}
bool DemodulatorThread::isStereo() {
return stereo;
}
2014-12-31 19:45:01 -05:00
float DemodulatorThread::getSignalLevel() {
return signalLevel;
2014-12-31 19:45:01 -05:00
}
void DemodulatorThread::setSquelchLevel(float signal_level_in) {
if (!squelchEnabled) {
squelchEnabled = true;
2014-12-31 19:45:01 -05:00
}
squelchLevel = signal_level_in;
2014-12-31 19:45:01 -05:00
}
float DemodulatorThread::getSquelchLevel() {
return squelchLevel;
2014-12-31 19:45:01 -05:00
}
2015-01-01 03:48:32 -05:00
2015-01-01 18:08:54 -05:00
void DemodulatorThread::setDemodulatorType(int demod_type_in) {
2015-01-01 03:48:32 -05:00
demodulatorType = demod_type_in;
}
2015-01-01 18:08:54 -05:00
int DemodulatorThread::getDemodulatorType() {
2015-01-01 03:48:32 -05:00
return demodulatorType;
}
2015-01-01 18:08:54 -05:00