2014-11-16 16:51:45 -05:00
|
|
|
#include "DemodulatorThread.h"
|
|
|
|
#include "CubicSDRDefs.h"
|
|
|
|
#include <vector>
|
|
|
|
|
2014-11-26 21:05:19 -05:00
|
|
|
DemodulatorThread::DemodulatorThread(DemodulatorThreadInputQueue* pQueue) :
|
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(
|
|
|
|
1), resampler(NULL), commandQueue(NULL), fir_filter(NULL) {
|
2014-11-26 22:29:23 -05:00
|
|
|
|
|
|
|
float kf = 0.75; // modulation factor
|
|
|
|
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-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-22 22:17:33 -05:00
|
|
|
void DemodulatorThread::threadMain() {
|
|
|
|
|
2014-11-26 21:05:19 -05:00
|
|
|
if (!initialized) {
|
|
|
|
initialize();
|
|
|
|
}
|
|
|
|
|
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-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-27 22:13:21 -05:00
|
|
|
case DemodulatorThreadCommand::SDR_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;
|
|
|
|
}
|
|
|
|
params.bandwidth = command.int_value;
|
|
|
|
paramsChanged = true;
|
|
|
|
break;
|
2014-11-27 22:13:21 -05:00
|
|
|
case DemodulatorThreadCommand::SDR_THREAD_CMD_SET_FREQUENCY:
|
|
|
|
params.frequency = command.int_value;
|
|
|
|
break;
|
2014-11-26 21:05:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (paramsChanged) {
|
|
|
|
initialize();
|
|
|
|
while (!inputQueue->empty()) { // catch up
|
|
|
|
inputQueue->pop(inp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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()) {
|
|
|
|
liquid_float_complex filtered_input[BUF_SIZE / 2];
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-27 22:13:21 -05:00
|
|
|
liquid_float_complex x, y, z;
|
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
for (int i = 0; i < BUF_SIZE / 2; i++) {
|
2014-11-27 22:13:21 -05:00
|
|
|
if (shift_freq != 0) {
|
|
|
|
nco_crcf_step(nco_shift);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-27 22:13:21 -05:00
|
|
|
z.real = (float) (*data)[i * 2] / 127.0f;
|
|
|
|
z.imag = (float) (*data)[i * 2 + 1] / 127.0f;
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-27 22:13:21 -05:00
|
|
|
if (shift_freq < 0) {
|
|
|
|
nco_crcf_mix_up(nco_shift, z, &x);
|
|
|
|
} else {
|
|
|
|
nco_crcf_mix_down(nco_shift, z, &x);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
x.real = (float) (*data)[i * 2] / 127.0f;
|
|
|
|
x.imag = (float) (*data)[i * 2 + 1] / 127.0f;
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
firfilt_crcf_push(fir_filter, x); // push input sample
|
|
|
|
firfilt_crcf_execute(fir_filter, &y); // compute output
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
filtered_input[i] = y;
|
|
|
|
}
|
2014-11-16 16:51:45 -05:00
|
|
|
|
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-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
|
|
|
|
msresamp_crcf_execute(resampler, filtered_input, (BUF_SIZE / 2), resampled_output, &num_written);
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
float pcm = 0;
|
2014-11-16 16:51:45 -05:00
|
|
|
|
2014-11-22 22:17:33 -05:00
|
|
|
for (int i = 0; i < num_written; i++) {
|
|
|
|
freqdem_demodulate(fdem, resampled_output[i], &pcm);
|
|
|
|
resampled_output[i].real = (float) pcm;
|
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-11-30 17:11:29 -05:00
|
|
|
|
|
|
|
std::cout << "Demodulator thread done." << std::endl;
|
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);
|
|
|
|
}
|