2014-11-16 16:51:45 -05:00
|
|
|
#include "DemodulatorThread.h"
|
|
|
|
#include "CubicSDRDefs.h"
|
|
|
|
#include <vector>
|
|
|
|
|
2014-12-01 18:59:07 -05:00
|
|
|
#ifdef __APPLE__
|
2014-12-11 19:07:21 -05:00
|
|
|
#include <pthread.h>
|
2014-12-01 18:59:07 -05:00
|
|
|
#endif
|
|
|
|
|
2014-12-11 19:07:21 -05:00
|
|
|
DemodulatorThread::DemodulatorThread(DemodulatorThreadInputQueue* pQueue, DemodulatorThreadCommandQueue* threadQueueNotify) :
|
2014-11-27 22:13:21 -05:00
|
|
|
inputQueue(pQueue), visOutQueue(NULL), terminated(false), initialized(false), audio_resampler(NULL), resample_ratio(1), audio_resample_ratio(
|
2014-12-11 19:07:21 -05:00
|
|
|
1), resampler(NULL), commandQueue(NULL), fir_filter(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify) {
|
2014-11-26 22:29:23 -05:00
|
|
|
|
2014-11-30 17:59:24 -05:00
|
|
|
float kf = 0.5; // modulation factor
|
2014-11-26 22:29:23 -05:00
|
|
|
fdem = freqdem_create(kf);
|
|
|
|
// freqdem_print(fdem);
|
2014-11-26 21:05:19 -05:00
|
|
|
|
2014-11-27 22:13:21 -05:00
|
|
|
nco_shift = nco_crcf_create(LIQUID_VCO);
|
|
|
|
shift_freq = 0;
|
|
|
|
|
2014-11-30 23:33:55 -05:00
|
|
|
workerQueue = new DemodulatorThreadWorkerCommandQueue;
|
|
|
|
workerResults = new DemodulatorThreadWorkerResultQueue;
|
2014-12-01 02:10:36 -05:00
|
|
|
workerThread = new DemodulatorWorkerThread(workerQueue, workerResults);
|
2014-11-30 23:33:55 -05:00
|
|
|
|
2014-12-01 02:10:36 -05:00
|
|
|
t_Worker = new std::thread(&DemodulatorWorkerThread::threadMain, workerThread);
|
2014-11-26 21:05:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void DemodulatorThread::initialize() {
|
|
|
|
initialized = false;
|
|
|
|
|
|
|
|
resample_ratio = (float) (params.bandwidth) / (float) params.inputRate;
|
|
|
|
audio_resample_ratio = (float) (params.audioSampleRate) / (float) params.bandwidth;
|
|
|
|
|
|
|
|
float fc = 0.5 * ((double) params.bandwidth / (double) params.inputRate); // filter cutoff frequency
|
|
|
|
|
|
|
|
if (fc <= 0) {
|
|
|
|
fc = 0;
|
2014-11-17 22:58:56 -05:00
|
|
|
}
|
|
|
|
|
2014-11-26 21:05:19 -05:00
|
|
|
if (fc >= 0.5) {
|
|
|
|
fc = 0.5;
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
|
|
|
float ft = 0.05f; // filter transition
|
|
|
|
float As = 60.0f; // stop-band attenuation [dB]
|
|
|
|
float mu = 0.0f; // fractional timing offset
|
|
|
|
|
|
|
|
// estimate required filter length and generate filter
|
|
|
|
unsigned int h_len = estimate_req_filter_len(ft, As);
|
|
|
|
float h[h_len];
|
|
|
|
liquid_firdes_kaiser(h_len, fc, As, mu, h);
|
|
|
|
|
2014-11-26 22:29:23 -05:00
|
|
|
if (fir_filter) {
|
|
|
|
firfilt_crcf_recreate(fir_filter, h, h_len);
|
|
|
|
} else {
|
|
|
|
fir_filter = firfilt_crcf_create(h, h_len);
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
|
|
|
// create multi-stage arbitrary resampler object
|
2014-11-26 22:29:23 -05:00
|
|
|
if (resampler) {
|
|
|
|
msresamp_crcf_destroy(resampler);
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
resampler = msresamp_crcf_create(resample_ratio, As);
|
2014-11-26 21:05:19 -05:00
|
|
|
// msresamp_crcf_print(resampler);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-26 22:29:23 -05:00
|
|
|
if (audio_resampler) {
|
|
|
|
msresamp_crcf_destroy(audio_resampler);
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
audio_resampler = msresamp_crcf_create(audio_resample_ratio, As);
|
2014-11-26 21:05:19 -05:00
|
|
|
// msresamp_crcf_print(audio_resampler);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-26 21:05:19 -05:00
|
|
|
initialized = true;
|
2014-11-26 22:29:23 -05:00
|
|
|
// std::cout << "inputResampleRate " << params.bandwidth << std::endl;
|
2014-11-26 21:05:19 -05:00
|
|
|
|
2014-11-26 22:29:23 -05:00
|
|
|
last_params = params;
|
2014-11-16 16:51:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
DemodulatorThread::~DemodulatorThread() {
|
2014-11-30 23:33:55 -05:00
|
|
|
delete workerThread;
|
|
|
|
delete workerQueue;
|
|
|
|
delete workerResults;
|
2014-11-16 16:51:45 -05:00
|
|
|
}
|
|
|
|
|
2014-12-01 18:59:07 -05:00
|
|
|
#ifdef __APPLE__
|
|
|
|
void *DemodulatorThread::threadMain() {
|
|
|
|
#else
|
2014-11-22 22:17:33 -05:00
|
|
|
void DemodulatorThread::threadMain() {
|
2014-12-01 18:59:07 -05:00
|
|
|
#endif
|
2014-11-22 22:17:33 -05:00
|
|
|
|
2014-11-26 21:05:19 -05:00
|
|
|
if (!initialized) {
|
|
|
|
initialize();
|
|
|
|
}
|
|
|
|
|
2014-12-14 20:23:52 -05:00
|
|
|
liquid_float_complex *in_buf = new liquid_float_complex[BUF_SIZE / 2];
|
|
|
|
liquid_float_complex *out_buf = new liquid_float_complex[BUF_SIZE / 2];
|
|
|
|
|
2014-11-30 17:11:29 -05:00
|
|
|
std::cout << "Demodulator thread started.." << std::endl;
|
2014-11-23 19:39:27 -05:00
|
|
|
while (!terminated) {
|
2014-11-22 22:17:33 -05:00
|
|
|
DemodulatorThreadIQData inp;
|
2014-11-22 22:33:32 -05:00
|
|
|
inputQueue->pop(inp);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-30 23:33:55 -05:00
|
|
|
bool bandwidthChanged = false;
|
|
|
|
DemodulatorThreadParameters bandwidthParams = params;
|
|
|
|
|
2014-11-26 21:05:19 -05:00
|
|
|
if (!commandQueue->empty()) {
|
|
|
|
bool paramsChanged = false;
|
|
|
|
while (!commandQueue->empty()) {
|
|
|
|
DemodulatorThreadCommand command;
|
|
|
|
commandQueue->pop(command);
|
|
|
|
switch (command.cmd) {
|
2014-11-30 23:33:55 -05:00
|
|
|
case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH:
|
2014-11-26 21:05:19 -05:00
|
|
|
if (command.int_value < 3000) {
|
|
|
|
command.int_value = 3000;
|
|
|
|
}
|
|
|
|
if (command.int_value > SRATE) {
|
|
|
|
command.int_value = SRATE;
|
|
|
|
}
|
2014-11-30 23:33:55 -05:00
|
|
|
bandwidthParams.bandwidth = command.int_value;
|
|
|
|
bandwidthChanged = true;
|
2014-11-26 21:05:19 -05:00
|
|
|
break;
|
2014-11-30 23:33:55 -05:00
|
|
|
case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_FREQUENCY:
|
2014-11-27 22:13:21 -05:00
|
|
|
params.frequency = command.int_value;
|
|
|
|
break;
|
2014-11-26 21:05:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-30 23:33:55 -05:00
|
|
|
if (bandwidthChanged) {
|
2014-12-01 01:14:32 -05:00
|
|
|
DemodulatorWorkerThreadCommand command(DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_BUILD_FILTERS);
|
|
|
|
command.audioSampleRate = bandwidthParams.audioSampleRate;
|
|
|
|
command.bandwidth = bandwidthParams.bandwidth;
|
|
|
|
command.frequency = bandwidthParams.frequency;
|
|
|
|
command.inputRate = bandwidthParams.inputRate;
|
2014-11-30 23:33:55 -05:00
|
|
|
|
2014-12-01 02:10:36 -05:00
|
|
|
workerQueue->push(command);
|
2014-11-26 21:05:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!initialized) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-11-27 22:13:21 -05:00
|
|
|
// Requested frequency is not center, shift it into the center!
|
|
|
|
if (inp.frequency != params.frequency) {
|
|
|
|
if ((params.frequency - inp.frequency) != shift_freq) {
|
|
|
|
shift_freq = params.frequency - inp.frequency;
|
2014-11-30 17:16:35 -05:00
|
|
|
if (abs(shift_freq) <= (int) ((float) (SRATE / 2) * 1.5)) {
|
|
|
|
nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((float) abs(shift_freq)) / ((float) SRATE)));
|
2014-11-27 22:13:21 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-30 17:16:35 -05:00
|
|
|
if (abs(shift_freq) > (int) ((float) (SRATE / 2) * 1.5)) {
|
2014-11-27 22:13:21 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
std::vector<signed char> *data = &inp.data;
|
|
|
|
if (data->size()) {
|
2014-12-14 20:23:52 -05:00
|
|
|
liquid_float_complex *temp_buf;
|
2014-11-27 22:13:21 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
for (int i = 0; i < BUF_SIZE / 2; i++) {
|
2014-12-14 20:23:52 -05:00
|
|
|
in_buf[i].real = (float) (*data)[i * 2] / 127.0f;
|
|
|
|
in_buf[i].imag = (float) (*data)[i * 2 + 1] / 127.0f;
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-12-14 20:23:52 -05:00
|
|
|
if (shift_freq != 0) {
|
|
|
|
if (shift_freq < 0) {
|
|
|
|
nco_crcf_mix_block_up(nco_shift, in_buf, out_buf, BUF_SIZE / 2);
|
2014-11-27 22:13:21 -05:00
|
|
|
} else {
|
2014-12-14 20:23:52 -05:00
|
|
|
nco_crcf_mix_block_down(nco_shift, in_buf, out_buf, BUF_SIZE / 2);
|
2014-11-27 22:13:21 -05:00
|
|
|
}
|
2014-12-14 20:23:52 -05:00
|
|
|
temp_buf = in_buf;
|
|
|
|
in_buf = out_buf;
|
|
|
|
out_buf = temp_buf;
|
2014-11-22 22:17:33 -05:00
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-12-14 20:23:52 -05:00
|
|
|
firfilt_crcf_execute_block(fir_filter, in_buf, BUF_SIZE / 2, out_buf);
|
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
int out_size = ceil((float) (BUF_SIZE / 2) * resample_ratio);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
liquid_float_complex resampled_output[out_size];
|
2014-12-14 20:23:52 -05:00
|
|
|
float demod_output[out_size];
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
unsigned int num_written; // number of values written to buffer
|
2014-12-14 20:23:52 -05:00
|
|
|
msresamp_crcf_execute(resampler, out_buf, (BUF_SIZE / 2), resampled_output, &num_written);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-12-14 20:23:52 -05:00
|
|
|
freqdem_demodulate_block(fdem, resampled_output, num_written, demod_output);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
for (int i = 0; i < num_written; i++) {
|
2014-12-14 20:23:52 -05:00
|
|
|
resampled_output[i].real = demod_output[i];
|
2014-11-30 17:17:28 -05:00
|
|
|
resampled_output[i].imag = 0;
|
2014-11-22 22:17:33 -05:00
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-26 21:05:19 -05:00
|
|
|
int audio_out_size = ceil((float) (num_written) * audio_resample_ratio);
|
2014-11-22 22:17:33 -05:00
|
|
|
liquid_float_complex resampled_audio_output[audio_out_size];
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
unsigned int num_audio_written;
|
2014-11-26 21:05:19 -05:00
|
|
|
msresamp_crcf_execute(audio_resampler, resampled_output, num_written, resampled_audio_output, &num_audio_written);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
std::vector<float> newBuffer;
|
|
|
|
newBuffer.resize(num_audio_written * 2);
|
|
|
|
for (int i = 0; i < num_audio_written; i++) {
|
|
|
|
liquid_float_complex y = resampled_audio_output[i];
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
newBuffer[i * 2] = y.real;
|
|
|
|
newBuffer[i * 2 + 1] = y.real;
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
AudioThreadInput ati;
|
|
|
|
ati.data = newBuffer;
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-30 17:11:29 -05:00
|
|
|
if (audioInputQueue != NULL) {
|
|
|
|
audioInputQueue->push(ati);
|
2014-11-22 22:17:33 -05:00
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
if (visOutQueue != NULL) {
|
|
|
|
visOutQueue->push(ati);
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
}
|
2014-12-01 02:10:36 -05:00
|
|
|
|
|
|
|
if (!workerResults->empty()) {
|
|
|
|
while (!workerResults->empty()) {
|
|
|
|
DemodulatorWorkerThreadResult result;
|
|
|
|
workerResults->pop(result);
|
|
|
|
|
|
|
|
switch (result.cmd) {
|
|
|
|
case DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS:
|
|
|
|
firfilt_crcf_destroy(fir_filter);
|
|
|
|
msresamp_crcf_destroy(resampler);
|
|
|
|
msresamp_crcf_destroy(audio_resampler);
|
|
|
|
|
|
|
|
fir_filter = result.fir_filter;
|
|
|
|
resampler = result.resampler;
|
|
|
|
audio_resampler = result.audio_resampler;
|
|
|
|
|
|
|
|
resample_ratio = result.resample_ratio;
|
|
|
|
audio_resample_ratio = result.audio_resample_ratio;
|
|
|
|
|
|
|
|
params.audioSampleRate = result.audioSampleRate;
|
|
|
|
params.bandwidth = result.bandwidth;
|
|
|
|
params.inputRate = result.inputRate;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
}
|
2014-11-30 17:11:29 -05:00
|
|
|
|
2014-12-14 20:23:52 -05:00
|
|
|
delete in_buf;
|
|
|
|
delete out_buf;
|
|
|
|
|
2014-11-30 17:11:29 -05:00
|
|
|
std::cout << "Demodulator thread done." << std::endl;
|
2014-12-11 19:07:21 -05:00
|
|
|
DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_TERMINATED);
|
|
|
|
tCmd.context = this;
|
|
|
|
threadQueueNotify->push(tCmd);
|
2014-11-16 16:51:45 -05:00
|
|
|
}
|
|
|
|
|
2014-11-23 19:39:27 -05:00
|
|
|
void DemodulatorThread::terminate() {
|
|
|
|
terminated = true;
|
|
|
|
DemodulatorThreadIQData inp; // push dummy to nudge queue
|
|
|
|
inputQueue->push(inp);
|
2014-11-30 23:33:55 -05:00
|
|
|
workerThread->terminate();
|
2014-11-23 19:39:27 -05:00
|
|
|
}
|