Moved demodulation code into it's own class

Preparing to make it more dynamic, able to tune multiple frequencies at
once and support remaining demodulation modes (AM, USB, LSB)
This commit is contained in:
Charles J. Cliffe 2014-11-09 18:12:02 -05:00
parent 14d7e431bd
commit a78a862cba
5 changed files with 227 additions and 191 deletions

View File

@ -90,6 +90,7 @@ SET (cubicsdr_sources
src/AppFrame.cpp
src/SDRThreadQueue.cpp
src/SDRThreadTask.cpp
src/Demodulator.cpp
)
SET (cubicsdr_headers
@ -101,6 +102,7 @@ SET (cubicsdr_headers
src/CubicSDRDefs.h
src/SDRThreadQueue.h
src/SDRThreadTask.h
src/Demodulator.h
)
#configure_files(${PROJECT_SOURCE_DIR}/shaders ${PROJECT_BINARY_DIR}/shaders COPYONLY)
#configure_files(${PROJECT_SOURCE_DIR}/png ${PROJECT_BINARY_DIR}/png COPYONLY)

169
src/Demodulator.cpp Normal file
View File

@ -0,0 +1,169 @@
#include "Demodulator.h"
static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, void *userData) {
Demodulator *src = (Demodulator *) userData;
float *out = (float*) outputBuffer;
if (!src->audio_queue.size()) {
for (int i = 0; i < framesPerBuffer * 2; i++) {
out[i] = 0;
}
return paContinue;
}
std::vector<float> *nextBuffer = src->audio_queue.front();
for (int i = 0; i < framesPerBuffer * 2; i++) {
out[i] = (*nextBuffer)[src->audio_queue_ptr];
src->audio_queue_ptr++;
if (src->audio_queue_ptr == nextBuffer->size()) {
src->audio_queue.pop();
delete nextBuffer;
src->audio_queue_ptr = 0;
if (!src->audio_queue.size()) {
break;
}
nextBuffer = src->audio_queue.front();
}
}
return paContinue;
}
Demodulator::Demodulator() {
bandwidth = 800000;
resample_ratio = (float) (bandwidth) / (float) SRATE;
audio_frequency = 44100;
audio_resample_ratio = (float) (audio_frequency) / (float) bandwidth;
PaError err;
err = Pa_Initialize();
if (err != paNoError) {
std::cout << "Error starting :(\n";
}
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
if (outputParameters.device == paNoDevice) {
std::cout << "Error: No default output device.\n";
}
outputParameters.channelCount = 2; /* Stereo output, most likely supported. */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
stream = NULL;
err = Pa_OpenStream(&stream, NULL, &outputParameters, 44100, 256, paClipOff, &patestCallback, this);
err = Pa_StartStream(stream);
if (err != paNoError) {
std::cout << "Error starting stream: " << Pa_GetErrorText(err) << std::endl;
std::cout << "\tPortAudio error: " << Pa_GetErrorText(err) << std::endl;
}
float fc = 0.5f * (bandwidth / SRATE); // filter cutoff frequency
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);
fir_filter = firfilt_crcf_create(h, h_len);
// create multi-stage arbitrary resampler object
resampler = msresamp_crcf_create(resample_ratio, As);
msresamp_crcf_print(resampler);
audio_resampler = msresamp_crcf_create(audio_resample_ratio, As);
msresamp_crcf_print(audio_resampler);
float kf = 0.1f;// modulation factor
fdem = freqdem_create(kf);
freqdem_print(fdem);
}
Demodulator::~Demodulator() {
PaError err;
err = Pa_StopStream(stream);
err = Pa_CloseStream(stream);
Pa_Terminate();
}
void Demodulator::writeBuffer(std::vector<signed char> *data) {
liquid_float_complex filtered_input[BUF_SIZE / 2];
for (int i = 0; i < BUF_SIZE / 2; i++) {
liquid_float_complex x;
liquid_float_complex y;
x.real = (float) (*data)[i * 2] / 127.0f;
x.imag = (float) (*data)[i * 2 + 1] / 127.0f;
firfilt_crcf_push(fir_filter, x); // push input sample
firfilt_crcf_execute(fir_filter, &y); // compute output
filtered_input[i] = y;
}
int out_size = ceil((float) (BUF_SIZE / 2) * resample_ratio);
liquid_float_complex resampled_output[out_size];
unsigned int num_written; // number of values written to buffer
msresamp_crcf_execute(resampler, filtered_input, (BUF_SIZE / 2), resampled_output, &num_written);
float waveform_ceil = 0, waveform_floor = 0;
float pcm = 0;
for (int i = 0; i < num_written; i++) {
freqdem_demodulate(fdem, resampled_output[i], &pcm);
resampled_output[i].real = (float) pcm;
resampled_output[i].imag = 0;
if (waveform_ceil < resampled_output[i].real) {
waveform_ceil = resampled_output[i].real;
}
if (waveform_floor > resampled_output[i].real) {
waveform_floor = resampled_output[i].real;
}
}
int audio_out_size = ceil((float) (num_written) * audio_resample_ratio);
liquid_float_complex resampled_audio_output[audio_out_size];
unsigned int num_audio_written; // number of values written to buffer
msresamp_crcf_execute(audio_resampler, resampled_output, num_written, resampled_audio_output, &num_audio_written);
std::vector<float> *newBuffer = new std::vector<float>;
newBuffer->resize(num_audio_written * 2);
for (int i = 0; i < num_audio_written; i++) {
(*newBuffer)[i * 2] = resampled_audio_output[i].real;
(*newBuffer)[i * 2 + 1] = resampled_audio_output[i].real;
}
audio_queue.push(newBuffer);
if (waveform_points.size() != num_audio_written * 2) {
waveform_points.resize(num_audio_written * 2);
}
for (int i = 0, iMax = waveform_points.size() / 2; i < iMax; i++) {
waveform_points[i * 2 + 1] = resampled_audio_output[i].real * 0.5f;
waveform_points[i * 2] = ((double) i / (double) iMax);
}
}

46
src/Demodulator.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include <vector>
#include <queue>
#include <cstring>
#include <iostream>
#include <math.h>
#include "CubicSDRDefs.h"
#include "liquid/liquid.h"
#include "portaudio.h"
#ifdef WIN32
#include "pa_stream.h"
#include "pa_debugprint.h"
#endif
static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, void *userData);
class Demodulator {
public:
std::queue<std::vector<float> *> audio_queue;
unsigned int audio_queue_ptr;
std::vector<float> waveform_points;
Demodulator();
~Demodulator();
void writeBuffer(std::vector<signed char> *data);
private:
firfilt_crcf fir_filter;
msresamp_crcf resampler;
msresamp_crcf audio_resampler;
float resample_ratio;
unsigned int bandwidth;
unsigned int audio_frequency;
float audio_resample_ratio;
PaStreamParameters outputParameters;
PaStream *stream;
freqdem fdem;
};

View File

@ -15,10 +15,6 @@
#include "AppFrame.h"
#include <algorithm>
#ifdef WIN32
#include "pa_debugprint.h"
#endif
wxString glGetwxString(GLenum name) {
const GLubyte *v = glGetString(name);
if (v == 0) {
@ -106,49 +102,12 @@ EVT_KEY_DOWN(TestGLCanvas::OnKeyDown)
EVT_IDLE(TestGLCanvas::OnIdle)
wxEND_EVENT_TABLE()
static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, void *userData) {
TestGLCanvas *src = (TestGLCanvas *) userData;
float *out = (float*) outputBuffer;
if (!src->audio_queue.size()) {
for (int i = 0; i < framesPerBuffer * 2; i++) {
out[i] = 0;
}
return paContinue;
}
std::vector<float> *nextBuffer = src->audio_queue.front();
for (int i = 0; i < framesPerBuffer * 2; i++) {
out[i] = (*nextBuffer)[src->audio_queue_ptr];
src->audio_queue_ptr++;
if (src->audio_queue_ptr == nextBuffer->size()) {
src->audio_queue.pop();
delete nextBuffer;
src->audio_queue_ptr = 0;
if (!src->audio_queue.size()) {
break;
}
nextBuffer = src->audio_queue.front();
}
}
return paContinue;
}
TestGLCanvas::TestGLCanvas(wxWindow *parent, int *attribList) :
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE), parent(parent) {
bandwidth = 800000;
resample_ratio = (float) (bandwidth) / (float) SRATE;
audio_frequency = 44100;
audio_resample_ratio = (float) (audio_frequency) / (float) bandwidth;
int in_block_size = BUF_SIZE / 2;
int out_block_size = FFT_SIZE;
@ -161,62 +120,11 @@ TestGLCanvas::TestGLCanvas(wxWindow *parent, int *attribList) :
fft_ceil_ma = fft_ceil_maa = 1.0;
PaError err;
err = Pa_Initialize();
if (err != paNoError) {
std::cout << "Error starting :(\n";
}
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
if (outputParameters.device == paNoDevice) {
std::cout << "Error: No default output device.\n";
}
outputParameters.channelCount = 2; /* Stereo output, most likely supported. */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
stream = NULL;
err = Pa_OpenStream(&stream, NULL, &outputParameters, 44100, 256, paClipOff, &patestCallback, this);
err = Pa_StartStream(stream);
if (err != paNoError) {
std::cout << "Error starting stream: " << Pa_GetErrorText(err) << std::endl;
std::cout << "\tPortAudio error: " << Pa_GetErrorText(err) << std::endl;
}
float fc = 0.5f * (bandwidth / SRATE); // filter cutoff frequency
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);
fir_filter = firfilt_crcf_create(h, h_len);
// create multi-stage arbitrary resampler object
resampler = msresamp_crcf_create(resample_ratio, As);
msresamp_crcf_print(resampler);
audio_resampler = msresamp_crcf_create(audio_resample_ratio, As);
msresamp_crcf_print(audio_resampler);
float kf = 0.1f; // modulation factor
fdem = freqdem_create(kf);
freqdem_print(fdem);
}
TestGLCanvas::~TestGLCanvas() {
PaError err;
err = Pa_StopStream(stream);
err = Pa_CloseStream(stream);
Pa_Terminate();
}
void TestGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
@ -226,7 +134,9 @@ void TestGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
PrimaryGLContext& canvas = wxGetApp().GetContext(this);
glViewport(0, 0, ClientSize.x, ClientSize.y);
canvas.Plot(spectrum_points, waveform_points);
std::vector<float> null_pts;
canvas.Plot(spectrum_points, test_demod.waveform_points);
SwapBuffers();
}
@ -277,35 +187,14 @@ void TestGLCanvas::setData(std::vector<signed char> *data) {
spectrum_points.resize(FFT_SIZE * 2);
}
fftw_execute(plan[0]);
liquid_float_complex filtered_input[BUF_SIZE / 2];
for (int i = 0; i < BUF_SIZE / 2; i++) {
liquid_float_complex x;
liquid_float_complex y;
x.real = (float) (*data)[i * 2] / 127.0f;
x.imag = (float) (*data)[i * 2 + 1] / 127.0f;
firfilt_crcf_push(fir_filter, x); // push input sample
firfilt_crcf_execute(fir_filter, &y); // compute output
filtered_input[i] = y;
in[i][0] = x.real;
in[i][1] = x.imag;
in[i][0] = (float) (*data)[i * 2] / 127.0f;
in[i][1] = (float) (*data)[i * 2 + 1] / 127.0f;
}
int out_size = ceil((float) (BUF_SIZE / 2) * resample_ratio);
liquid_float_complex resampled_output[out_size];
unsigned int num_written; // number of values written to buffer
msresamp_crcf_execute(resampler, filtered_input, (BUF_SIZE / 2), resampled_output, &num_written);
fftw_execute(plan[0]);
double fft_ceil = 0;
// fft_floor,
if (fft_result.size() < FFT_SIZE) {
fft_result.resize(FFT_SIZE);
@ -350,48 +239,7 @@ void TestGLCanvas::setData(std::vector<signed char> *data) {
spectrum_points[i * 2] = ((double) i / (double) iMax);
}
float waveform_ceil = 0, waveform_floor = 0;
float pcm = 0;
for (int i = 0; i < num_written; i++) {
freqdem_demodulate(fdem, resampled_output[i], &pcm);
resampled_output[i].real = (float) pcm;
resampled_output[i].imag = 0;
if (waveform_ceil < resampled_output[i].real) {
waveform_ceil = resampled_output[i].real;
}
if (waveform_floor > resampled_output[i].real) {
waveform_floor = resampled_output[i].real;
}
}
int audio_out_size = ceil((float) (num_written) * audio_resample_ratio);
liquid_float_complex resampled_audio_output[audio_out_size];
unsigned int num_audio_written; // number of values written to buffer
msresamp_crcf_execute(audio_resampler, resampled_output, num_written, resampled_audio_output, &num_audio_written);
if (waveform_points.size() != num_audio_written * 2) {
waveform_points.resize(num_audio_written * 2);
}
for (int i = 0, iMax = waveform_points.size() / 2; i < iMax; i++) {
waveform_points[i * 2 + 1] = resampled_audio_output[i].real * 0.5f;
waveform_points[i * 2] = ((double) i / (double) iMax);
}
std::vector<float> *newBuffer = new std::vector<float>;
newBuffer->resize(num_audio_written * 2);
for (int i = 0; i < num_audio_written; i++) {
(*newBuffer)[i * 2] = resampled_audio_output[i].real;
(*newBuffer)[i * 2 + 1] = resampled_audio_output[i].real;
}
audio_queue.push(newBuffer);
test_demod.writeBuffer(data);
}
}

View File

@ -8,13 +8,7 @@
#include "CubicSDRDefs.h"
#include "fftw3.h"
#include "liquid/liquid.h"
#include "portaudio.h"
#ifdef WIN32
#include "pa_stream.h"
#endif
#include "Demodulator.h"
class PrimaryGLContext: public wxGLContext {
public:
@ -32,9 +26,6 @@ public:
void setData(std::vector<signed char> *data);
std::queue< std::vector <float> * > audio_queue;
unsigned int audio_queue_ptr;
private:
void OnPaint(wxPaintEvent& event);
void OnKeyDown(wxKeyEvent& event);
@ -43,36 +34,16 @@ private:
wxWindow *parent;
std::vector<float> spectrum_points;
std::vector<float> waveform_points;
fftw_complex *in, *out[2];
fftw_plan plan[2];
firfilt_crcf fir_filter;
float pre_r;
float pre_j;
float droop_ofs, droop_ofs_ma, droop_ofs_maa;
msresamp_crcf resampler;
msresamp_crcf audio_resampler;
float resample_ratio;
freqdem fdem;
float fft_ceil_ma, fft_ceil_maa;
std::vector<float> fft_result;
std::vector<float> fft_result_ma;
std::vector<float> fft_result_maa;
unsigned int bandwidth;
unsigned int audio_frequency;
float audio_resample_ratio;
PaStreamParameters outputParameters;
PaStream *stream;
Demodulator test_demod;
wxDECLARE_EVENT_TABLE();
};