mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2025-08-01 05:02:26 -04:00
Demodulator and Audio threads working
Data now flows from SDR IQ Thread -> Demodulator Thread -> Audio Thread. Seems to have added a significant audio delay though..
This commit is contained in:
parent
2a34fc9cf9
commit
739854cd7f
@ -90,7 +90,6 @@ SET (cubicsdr_sources
|
|||||||
src/AppFrame.cpp
|
src/AppFrame.cpp
|
||||||
src/SDRThreadQueue.cpp
|
src/SDRThreadQueue.cpp
|
||||||
src/SDRThreadTask.cpp
|
src/SDRThreadTask.cpp
|
||||||
src/Demodulator.cpp
|
|
||||||
src/DemodulatorThread.cpp
|
src/DemodulatorThread.cpp
|
||||||
src/DemodulatorThreadQueue.cpp
|
src/DemodulatorThreadQueue.cpp
|
||||||
src/DemodulatorThreadTask.cpp
|
src/DemodulatorThreadTask.cpp
|
||||||
@ -115,7 +114,6 @@ SET (cubicsdr_headers
|
|||||||
src/CubicSDRDefs.h
|
src/CubicSDRDefs.h
|
||||||
src/SDRThreadQueue.h
|
src/SDRThreadQueue.h
|
||||||
src/SDRThreadTask.h
|
src/SDRThreadTask.h
|
||||||
src/Demodulator.h
|
|
||||||
src/DemodulatorThread.h
|
src/DemodulatorThread.h
|
||||||
src/DemodulatorThreadQueue.h
|
src/DemodulatorThreadQueue.h
|
||||||
src/DemodulatorThreadTask.h
|
src/DemodulatorThreadTask.h
|
||||||
|
@ -142,8 +142,12 @@ void AppFrame::OnEventInput(wxThreadEvent& event) {
|
|||||||
std::vector<signed char> *new_buffer = event.GetPayload<std::vector<signed char> *>();
|
std::vector<signed char> *new_buffer = event.GetPayload<std::vector<signed char> *>();
|
||||||
// std::cout << new_buffer->size() << std::endl;
|
// std::cout << new_buffer->size() << std::endl;
|
||||||
if (new_buffer->size()) {
|
if (new_buffer->size()) {
|
||||||
test_demod.writeBuffer(new_buffer);
|
DemodulatorThreadTask task = DemodulatorThreadTask(DemodulatorThreadTask::DEMOD_THREAD_DATA);
|
||||||
scopeCanvas->setWaveformPoints(test_demod.waveform_points);
|
task.setData(*new_buffer);
|
||||||
|
threadQueueDemod->addTask(task, DemodulatorThreadQueue::DEMOD_PRIORITY_HIGHEST);
|
||||||
|
|
||||||
|
// test_demod.writeBuffer(new_buffer);
|
||||||
|
// scopeCanvas->setWaveformPoints(test_demod.waveform_points);
|
||||||
spectrumCanvas->setData(new_buffer);
|
spectrumCanvas->setData(new_buffer);
|
||||||
waterfallCanvas->setData(new_buffer);
|
waterfallCanvas->setData(new_buffer);
|
||||||
} else {
|
} else {
|
||||||
@ -152,7 +156,6 @@ void AppFrame::OnEventInput(wxThreadEvent& event) {
|
|||||||
delete new_buffer;
|
delete new_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Demodulator -> Audio
|
// Demodulator -> Audio
|
||||||
void AppFrame::OnDemodInput(wxThreadEvent& event) {
|
void AppFrame::OnDemodInput(wxThreadEvent& event) {
|
||||||
std::vector<float> *new_buffer = event.GetPayload<std::vector<float> *>();
|
std::vector<float> *new_buffer = event.GetPayload<std::vector<float> *>();
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include "ScopeCanvas.h"
|
#include "ScopeCanvas.h"
|
||||||
#include "SpectrumCanvas.h"
|
#include "SpectrumCanvas.h"
|
||||||
#include "WaterfallCanvas.h"
|
#include "WaterfallCanvas.h"
|
||||||
#include "Demodulator.h"
|
|
||||||
|
|
||||||
// Define a new frame type
|
// Define a new frame type
|
||||||
class AppFrame: public wxFrame {
|
class AppFrame: public wxFrame {
|
||||||
@ -44,8 +43,6 @@ private:
|
|||||||
wxCriticalSection m_pThreadCS;
|
wxCriticalSection m_pThreadCS;
|
||||||
unsigned int frequency;
|
unsigned int frequency;
|
||||||
|
|
||||||
Demodulator test_demod;
|
|
||||||
|
|
||||||
// event table
|
// event table
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//wxDEFINE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
|
//wxDEFINE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
|
||||||
|
|
||||||
AudioThread::AudioThread(AudioThreadQueue* pQueue, int id) :
|
AudioThread::AudioThread(AudioThreadQueue* pQueue, int id) :
|
||||||
wxThread(wxTHREAD_DETACHED), m_pQueue(pQueue), m_ID(id) {
|
wxThread(wxTHREAD_DETACHED), m_pQueue(pQueue), m_ID(id), audio_queue_ptr(0) {
|
||||||
|
|
||||||
}
|
}
|
||||||
AudioThread::~AudioThread() {
|
AudioThread::~AudioThread() {
|
||||||
@ -30,16 +30,15 @@ static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned
|
|||||||
return paContinue;
|
return paContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> *nextBuffer = src->audio_queue.front();
|
std::vector<float> nextBuffer = src->audio_queue.front();
|
||||||
|
|
||||||
for (int i = 0; i < framesPerBuffer * 2; i++) {
|
for (int i = 0; i < framesPerBuffer * 2; i++) {
|
||||||
out[i] = (*nextBuffer)[src->audio_queue_ptr];
|
out[i] = nextBuffer[src->audio_queue_ptr];
|
||||||
|
|
||||||
src->audio_queue_ptr++;
|
src->audio_queue_ptr++;
|
||||||
|
|
||||||
if (src->audio_queue_ptr == nextBuffer->size()) {
|
if (src->audio_queue_ptr == nextBuffer.size()) {
|
||||||
src->audio_queue.pop();
|
src->audio_queue.pop();
|
||||||
delete nextBuffer;
|
|
||||||
src->audio_queue_ptr = 0;
|
src->audio_queue_ptr = 0;
|
||||||
if (!src->audio_queue.size()) {
|
if (!src->audio_queue.size()) {
|
||||||
break;
|
break;
|
||||||
@ -107,10 +106,9 @@ wxThread::ExitCode AudioThread::Entry() {
|
|||||||
while (m_pQueue->stackSize()) {
|
while (m_pQueue->stackSize()) {
|
||||||
AudioThreadTask task = m_pQueue->pop(); // pop a task from the queue. this will block the worker thread if queue is empty
|
AudioThreadTask task = m_pQueue->pop(); // pop a task from the queue. this will block the worker thread if queue is empty
|
||||||
switch (task.m_cmd) {
|
switch (task.m_cmd) {
|
||||||
// case AudioThreadTask::AUDIO_THREAD_TUNING:
|
case AudioThreadTask::AUDIO_THREAD_DATA:
|
||||||
//
|
audio_queue.push(task.getData());
|
||||||
// audio_queue.push(newBuffer);
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,12 @@ static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned
|
|||||||
//wxDECLARE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
|
//wxDECLARE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
EVENT_AUDIO_INPUT = wxID_HIGHEST + 1
|
EVENT_AUDIO_INPUT = wxID_HIGHEST + 3
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioThread: public wxThread {
|
class AudioThread: public wxThread {
|
||||||
public:
|
public:
|
||||||
std::queue<std::vector<float> *> audio_queue;
|
std::queue<std::vector<float> > audio_queue;
|
||||||
unsigned int audio_queue_ptr;
|
unsigned int audio_queue_ptr;
|
||||||
|
|
||||||
AudioThread(AudioThreadQueue* pQueue, int id = 0);
|
AudioThread(AudioThreadQueue* pQueue, int id = 0);
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
#include "Demodulator.h"
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
Demodulator::Demodulator() {
|
|
||||||
|
|
||||||
bandwidth = 200000;
|
|
||||||
resample_ratio = (float) (bandwidth) / (float) SRATE;
|
|
||||||
wbfm_frequency = 100000;
|
|
||||||
wbfm_resample_ratio = (float) (wbfm_frequency) / (float) bandwidth;
|
|
||||||
audio_frequency = AUDIO_FREQUENCY;
|
|
||||||
audio_resample_ratio = (float) (audio_frequency) / (float) wbfm_frequency;
|
|
||||||
|
|
||||||
|
|
||||||
float fc = 0.5f * ((float)bandwidth / (float)SRATE) * 0.75; // 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);
|
|
||||||
|
|
||||||
h_len = estimate_req_filter_len(ft, As);
|
|
||||||
liquid_firdes_kaiser(h_len, 32000.0/(float)wbfm_frequency, As, mu, h);
|
|
||||||
|
|
||||||
fir_audio_filter = firfilt_crcf_create(h, h_len);
|
|
||||||
|
|
||||||
// create multi-stage arbitrary resampler object
|
|
||||||
resampler = msresamp_crcf_create(resample_ratio, As);
|
|
||||||
msresamp_crcf_print(resampler);
|
|
||||||
|
|
||||||
wbfm_resampler = msresamp_crcf_create(wbfm_resample_ratio, As);
|
|
||||||
msresamp_crcf_print(wbfm_resampler);
|
|
||||||
|
|
||||||
audio_resampler = msresamp_crcf_create(audio_resample_ratio, As);
|
|
||||||
msresamp_crcf_print(audio_resampler);
|
|
||||||
|
|
||||||
float kf = 0.75; // modulation factor
|
|
||||||
|
|
||||||
fdem = freqdem_create(kf);
|
|
||||||
freqdem_print(fdem);
|
|
||||||
}
|
|
||||||
|
|
||||||
Demodulator::~Demodulator() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Demodulator::writeBuffer(std::vector<signed char> *data) {
|
|
||||||
if (data->size()) {
|
|
||||||
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 wbfm_out_size = ceil((float) (num_written) * wbfm_resample_ratio);
|
|
||||||
liquid_float_complex resampled_wbfm_output[wbfm_out_size];
|
|
||||||
|
|
||||||
unsigned int num_wbfm_written;
|
|
||||||
msresamp_crcf_execute(wbfm_resampler, resampled_output, num_written, resampled_wbfm_output, &num_wbfm_written);
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < num_wbfm_written; i++) {
|
|
||||||
firfilt_crcf_push(fir_audio_filter, resampled_wbfm_output[i]);
|
|
||||||
firfilt_crcf_execute(fir_audio_filter, &resampled_wbfm_output[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int audio_out_size = ceil((float) (num_wbfm_written) * audio_resample_ratio);
|
|
||||||
liquid_float_complex resampled_audio_output[audio_out_size];
|
|
||||||
|
|
||||||
unsigned int num_audio_written;
|
|
||||||
msresamp_crcf_execute(audio_resampler, resampled_wbfm_output, num_wbfm_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++) {
|
|
||||||
// liquid_float_complex y = resampled_audio_output[i];
|
|
||||||
//
|
|
||||||
// (*newBuffer)[i * 2] = y.real;
|
|
||||||
// (*newBuffer)[i * 2 + 1] = y.real;
|
|
||||||
// }
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <queue>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "CubicSDRDefs.h"
|
|
||||||
#include "liquid/liquid.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Demodulator {
|
|
||||||
public:
|
|
||||||
std::vector<float> waveform_points;
|
|
||||||
|
|
||||||
Demodulator();
|
|
||||||
~Demodulator();
|
|
||||||
|
|
||||||
void writeBuffer(std::vector<signed char> *data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
firfilt_crcf fir_filter;
|
|
||||||
firfilt_crcf fir_audio_filter;
|
|
||||||
|
|
||||||
unsigned int bandwidth;
|
|
||||||
|
|
||||||
msresamp_crcf resampler;
|
|
||||||
float resample_ratio;
|
|
||||||
|
|
||||||
msresamp_crcf wbfm_resampler;
|
|
||||||
float wbfm_resample_ratio;
|
|
||||||
unsigned int wbfm_frequency;
|
|
||||||
|
|
||||||
msresamp_crcf audio_resampler;
|
|
||||||
float audio_resample_ratio;
|
|
||||||
|
|
||||||
unsigned int audio_frequency;
|
|
||||||
|
|
||||||
freqdem fdem;
|
|
||||||
};
|
|
@ -61,7 +61,7 @@ wxThread::ExitCode DemodulatorThread::Entry() {
|
|||||||
DemodulatorThreadTask task = m_pQueue->pop(); // pop a task from the queue. this will block the worker thread if queue is empty
|
DemodulatorThreadTask task = m_pQueue->pop(); // pop a task from the queue. this will block the worker thread if queue is empty
|
||||||
switch (task.m_cmd) {
|
switch (task.m_cmd) {
|
||||||
case DemodulatorThreadTask::DEMOD_THREAD_DATA:
|
case DemodulatorThreadTask::DEMOD_THREAD_DATA:
|
||||||
std::vector<unsigned char> *data = &task.getData();
|
std::vector<signed char> *data = &task.getData();
|
||||||
if (data->size()) {
|
if (data->size()) {
|
||||||
liquid_float_complex filtered_input[BUF_SIZE / 2];
|
liquid_float_complex filtered_input[BUF_SIZE / 2];
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
//wxDECLARE_EVENT(wxEVT_COMMAND_DemodulatorThread_INPUT, wxThreadEvent);
|
//wxDECLARE_EVENT(wxEVT_COMMAND_DemodulatorThread_INPUT, wxThreadEvent);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
EVENT_DEMOD_INPUT = wxID_HIGHEST + 1
|
EVENT_DEMOD_INPUT = wxID_HIGHEST + 2
|
||||||
};
|
};
|
||||||
|
|
||||||
class DemodulatorThread: public wxThread {
|
class DemodulatorThread: public wxThread {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "DemodulatorThreadTask.h"
|
#include "DemodulatorThreadTask.h"
|
||||||
|
|
||||||
void DemodulatorThreadTask::setData(std::vector<unsigned char> &data_in) {
|
void DemodulatorThreadTask::setData(std::vector<signed char> &data_in) {
|
||||||
data = data_in;
|
data = data_in;
|
||||||
}
|
}
|
||||||
std::vector<unsigned char> &DemodulatorThreadTask::getData() {
|
std::vector<signed char> &DemodulatorThreadTask::getData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,10 @@ public:
|
|||||||
m_cmd(cmd) {
|
m_cmd(cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setData(std::vector<unsigned char> &data_in);
|
void setData(std::vector<signed char> &data_in);
|
||||||
std::vector<unsigned char> &getData();
|
std::vector<signed char> &getData();
|
||||||
|
|
||||||
DEMOD_THREAD_COMMAND m_cmd;
|
DEMOD_THREAD_COMMAND m_cmd;
|
||||||
|
|
||||||
std::vector<unsigned char> data;
|
std::vector<signed char> data;
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include "SpectrumContext.h"
|
#include "SpectrumContext.h"
|
||||||
|
|
||||||
#include "fftw3.h"
|
#include "fftw3.h"
|
||||||
#include "Demodulator.h"
|
|
||||||
|
|
||||||
class SpectrumCanvas: public wxGLCanvas {
|
class SpectrumCanvas: public wxGLCanvas {
|
||||||
public:
|
public:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user