mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-23 04:08:36 -05:00
Experimental AM/LSB/USB demodulation
This commit is contained in:
parent
955ec54b44
commit
8b89b27b40
@ -150,7 +150,16 @@ AppFrame::AppFrame() :
|
|||||||
}
|
}
|
||||||
|
|
||||||
wxMenuBar *menuBar = new wxMenuBar;
|
wxMenuBar *menuBar = new wxMenuBar;
|
||||||
menuBar->Append(menu, wxT("&Device"));
|
menuBar->Append(menu, wxT("Output &Device"));
|
||||||
|
|
||||||
|
wxMenu *demodMenu = new wxMenu;
|
||||||
|
demod_menuitems[DemodulatorType::DEMOD_TYPE_FM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_FM,wxT("FM"),wxT("Description?"));
|
||||||
|
demod_menuitems[DemodulatorType::DEMOD_TYPE_AM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_AM,wxT("AM"),wxT("Description?"));
|
||||||
|
demod_menuitems[DemodulatorType::DEMOD_TYPE_LSB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_LSB,wxT("LSB"),wxT("Description?"));
|
||||||
|
demod_menuitems[DemodulatorType::DEMOD_TYPE_USB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_USB,wxT("USB"),wxT("Description?"));
|
||||||
|
|
||||||
|
menuBar->Append(demodMenu, wxT("Demodulaton &Type"));
|
||||||
|
|
||||||
|
|
||||||
SetMenuBar(menuBar);
|
SetMenuBar(menuBar);
|
||||||
|
|
||||||
@ -177,6 +186,22 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
|||||||
activeDemodulator = NULL;
|
activeDemodulator = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (activeDemodulator) {
|
||||||
|
if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_FM) {
|
||||||
|
activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_FM);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
} else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_AM) {
|
||||||
|
activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_AM);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
} else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_LSB) {
|
||||||
|
activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_LSB);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
} else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_USB) {
|
||||||
|
activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_USB);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) {
|
void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) {
|
||||||
@ -207,6 +232,8 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
int outputDevice = demod->getOutputDevice();
|
int outputDevice = demod->getOutputDevice();
|
||||||
scopeCanvas->setDeviceName(output_devices[outputDevice].name);
|
scopeCanvas->setDeviceName(output_devices[outputDevice].name);
|
||||||
output_device_menuitems[outputDevice]->Check(true);
|
output_device_menuitems[outputDevice]->Check(true);
|
||||||
|
DemodulatorType dType = demod->getDemodulatorType();
|
||||||
|
demod_menuitems[dType]->Check(true);
|
||||||
}
|
}
|
||||||
if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
|
if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
|
||||||
if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) {
|
if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#define wxID_RT_AUDIO_DEVICE 1000
|
#define wxID_RT_AUDIO_DEVICE 1000
|
||||||
|
#define wxID_DEMOD_TYPE 1500
|
||||||
|
|
||||||
// Define a new frame type
|
// Define a new frame type
|
||||||
class AppFrame: public wxFrame {
|
class AppFrame: public wxFrame {
|
||||||
@ -41,5 +42,7 @@ private:
|
|||||||
std::map<int,RtAudio::DeviceInfo> output_devices;
|
std::map<int,RtAudio::DeviceInfo> output_devices;
|
||||||
std::map<int,wxMenuItem *> output_device_menuitems;
|
std::map<int,wxMenuItem *> output_device_menuitems;
|
||||||
|
|
||||||
|
std::map<DemodulatorType,wxMenuItem *> demod_menuitems;
|
||||||
|
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
@ -207,3 +207,12 @@ void DemodulatorInstance::setOutputDevice(int device_id) {
|
|||||||
int DemodulatorInstance::getOutputDevice() {
|
int DemodulatorInstance::getOutputDevice() {
|
||||||
return audioThread->getOutputDevice();
|
return audioThread->getOutputDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DemodulatorInstance::setDemodulatorType(DemodulatorType demod_type_in) {
|
||||||
|
demodulatorThread->setDemodulatorType(demod_type_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
DemodulatorType DemodulatorInstance::getDemodulatorType() {
|
||||||
|
return demodulatorThread->getDemodulatorType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,9 @@ public:
|
|||||||
void setOutputDevice(int device_id);
|
void setOutputDevice(int device_id);
|
||||||
int getOutputDevice();
|
int getOutputDevice();
|
||||||
|
|
||||||
|
void setDemodulatorType(DemodulatorType demod_type_in);
|
||||||
|
DemodulatorType getDemodulatorType();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<std::string *> label; //
|
std::atomic<std::string *> label; //
|
||||||
bool terminated; //
|
bool terminated; //
|
||||||
|
@ -8,10 +8,18 @@
|
|||||||
|
|
||||||
DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* pQueue, DemodulatorThreadControlCommandQueue *threadQueueControl,
|
DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* pQueue, DemodulatorThreadControlCommandQueue *threadQueueControl,
|
||||||
DemodulatorThreadCommandQueue* threadQueueNotify) :
|
DemodulatorThreadCommandQueue* threadQueueNotify) :
|
||||||
postInputQueue(pQueue), visOutQueue(NULL), audioInputQueue(NULL), agc(NULL), stereo(false), terminated(false), threadQueueNotify(
|
postInputQueue(pQueue), visOutQueue(NULL), audioInputQueue(NULL), agc(NULL), am_max(1), am_max_ma(1), am_max_maa(1), stereo(false), terminated(
|
||||||
threadQueueNotify), threadQueueControl(threadQueueControl), squelch_level(0), squelch_tolerance(0), signal_level(0), squelch_enabled(false) {
|
false), demodulatorType(DemodulatorType::DEMOD_TYPE_FM), threadQueueNotify(threadQueueNotify), threadQueueControl(threadQueueControl), squelch_level(
|
||||||
|
0), squelch_tolerance(0), signal_level(0), squelch_enabled(false) {
|
||||||
|
|
||||||
fdem = freqdem_create(0.5);
|
fdem = freqdem_create(0.5);
|
||||||
|
liquid_ampmodem_type type_lsb = LIQUID_AMPMODEM_LSB;
|
||||||
|
ampdem_lsb = ampmodem_create(1.0, 0.5, type_lsb, 1);
|
||||||
|
liquid_ampmodem_type type_usb = LIQUID_AMPMODEM_USB;
|
||||||
|
ampdem_usb = ampmodem_create(1.0, -0.5, type_usb, 1);
|
||||||
|
liquid_ampmodem_type type_dsb = LIQUID_AMPMODEM_DSB;
|
||||||
|
ampdem = ampmodem_create(0.5, 0.0, type_dsb, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
DemodulatorThread::~DemodulatorThread() {
|
DemodulatorThread::~DemodulatorThread() {
|
||||||
}
|
}
|
||||||
@ -66,16 +74,6 @@ void DemodulatorThread::threadMain() {
|
|||||||
|
|
||||||
std::cout << "Demodulator thread started.." << std::endl;
|
std::cout << "Demodulator thread started.." << std::endl;
|
||||||
|
|
||||||
std::deque<AudioThreadInput *> buffers;
|
|
||||||
std::deque<AudioThreadInput *>::iterator buffers_i;
|
|
||||||
|
|
||||||
std::vector<liquid_float_complex> resampled_data;
|
|
||||||
std::vector<liquid_float_complex> agc_data;
|
|
||||||
std::vector<float> demod_output;
|
|
||||||
std::vector<float> demod_output_stereo;
|
|
||||||
std::vector<float> resampled_audio_output;
|
|
||||||
std::vector<float> resampled_audio_output_stereo;
|
|
||||||
|
|
||||||
double freq_index = 0;
|
double freq_index = 0;
|
||||||
|
|
||||||
while (!terminated) {
|
while (!terminated) {
|
||||||
@ -101,6 +99,11 @@ void DemodulatorThread::threadMain() {
|
|||||||
resampler = inp->resampler;
|
resampler = inp->resampler;
|
||||||
audio_resampler = inp->audio_resampler;
|
audio_resampler = inp->audio_resampler;
|
||||||
stereo_resampler = inp->stereo_resampler;
|
stereo_resampler = inp->stereo_resampler;
|
||||||
|
|
||||||
|
ampmodem_reset(ampdem_lsb);
|
||||||
|
ampmodem_reset(ampdem_usb);
|
||||||
|
ampmodem_reset(ampdem);
|
||||||
|
freqdem_reset(fdem);
|
||||||
}
|
}
|
||||||
|
|
||||||
int out_size = ceil((double) (bufSize) * inp->resample_ratio);
|
int out_size = ceil((double) (bufSize) * inp->resample_ratio);
|
||||||
@ -108,17 +111,17 @@ void DemodulatorThread::threadMain() {
|
|||||||
if (agc_data.size() != out_size) {
|
if (agc_data.size() != out_size) {
|
||||||
if (agc_data.capacity() < out_size) {
|
if (agc_data.capacity() < out_size) {
|
||||||
agc_data.reserve(out_size);
|
agc_data.reserve(out_size);
|
||||||
|
agc_am_data.reserve(out_size);
|
||||||
resampled_data.reserve(out_size);
|
resampled_data.reserve(out_size);
|
||||||
}
|
}
|
||||||
agc_data.resize(out_size);
|
agc_data.resize(out_size);
|
||||||
resampled_data.resize(out_size);
|
resampled_data.resize(out_size);
|
||||||
|
agc_am_data.resize(out_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int num_written;
|
unsigned int num_written;
|
||||||
msresamp_crcf_execute(resampler, &(inp->data[0]), bufSize, &resampled_data[0], &num_written);
|
msresamp_crcf_execute(resampler, &(inp->data[0]), bufSize, &resampled_data[0], &num_written);
|
||||||
|
|
||||||
agc_crcf_execute_block(agc, &resampled_data[0], num_written, &agc_data[0]);
|
|
||||||
|
|
||||||
double audio_resample_ratio = inp->audio_resample_ratio;
|
double audio_resample_ratio = inp->audio_resample_ratio;
|
||||||
|
|
||||||
if (demod_output.size() != num_written) {
|
if (demod_output.size() != num_written) {
|
||||||
@ -130,7 +133,51 @@ void DemodulatorThread::threadMain() {
|
|||||||
|
|
||||||
int audio_out_size = ceil((double) (num_written) * audio_resample_ratio);
|
int audio_out_size = ceil((double) (num_written) * audio_resample_ratio);
|
||||||
|
|
||||||
|
agc_crcf_execute_block(agc, &resampled_data[0], num_written, &agc_data[0]);
|
||||||
|
|
||||||
|
float current_level = 0;
|
||||||
|
|
||||||
|
current_level = ((60.0 / fabs(agc_crcf_get_rssi(agc))) / 15.0 - signal_level);
|
||||||
|
|
||||||
|
if (agc_crcf_get_signal_level(agc) > current_level) {
|
||||||
|
current_level = agc_crcf_get_signal_level(agc);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (demodulatorType) {
|
||||||
|
case DemodulatorType::DEMOD_TYPE_FM:
|
||||||
freqdem_demodulate_block(fdem, &agc_data[0], num_written, &demod_output[0]);
|
freqdem_demodulate_block(fdem, &agc_data[0], num_written, &demod_output[0]);
|
||||||
|
break;
|
||||||
|
case DemodulatorType::DEMOD_TYPE_LSB:
|
||||||
|
for (int i = 0; i < num_written; i++) {
|
||||||
|
ampmodem_demodulate(ampdem_lsb, resampled_data[i], &demod_output[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case DemodulatorType::DEMOD_TYPE_USB:
|
||||||
|
for (int i = 0; i < num_written; i++) {
|
||||||
|
ampmodem_demodulate(ampdem_usb, resampled_data[i], &demod_output[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case DemodulatorType::DEMOD_TYPE_AM:
|
||||||
|
am_max = 0;
|
||||||
|
for (int i = 0; i < num_written; i++) {
|
||||||
|
ampmodem_demodulate(ampdem, resampled_data[i], &demod_output[i]);
|
||||||
|
if (demod_output[i] > am_max) {
|
||||||
|
am_max = demod_output[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
am_max_ma = am_max_ma + (am_max - am_max_ma) * 0.03;
|
||||||
|
am_max_maa = am_max_maa + (am_max_ma - am_max_maa) * 0.03;
|
||||||
|
|
||||||
|
float gain = 0.95/am_max_maa;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_written; i++) {
|
||||||
|
demod_output[i] *= gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (audio_out_size != resampled_audio_output.size()) {
|
if (audio_out_size != resampled_audio_output.size()) {
|
||||||
if (resampled_audio_output.capacity() < audio_out_size) {
|
if (resampled_audio_output.capacity() < audio_out_size) {
|
||||||
@ -174,15 +221,12 @@ void DemodulatorThread::threadMain() {
|
|||||||
msresamp_rrrf_execute(stereo_resampler, &demod_output_stereo[0], num_written, &resampled_audio_output_stereo[0], &num_audio_written);
|
msresamp_rrrf_execute(stereo_resampler, &demod_output_stereo[0], num_written, &resampled_audio_output_stereo[0], &num_audio_written);
|
||||||
}
|
}
|
||||||
|
|
||||||
float current_level = ((60.0/fabs(agc_crcf_get_rssi(agc)))/15.0 - signal_level); //agc_crcf_get_signal_level(agc);
|
|
||||||
|
|
||||||
if (current_level > signal_level) {
|
if (current_level > signal_level) {
|
||||||
signal_level = signal_level + (current_level-signal_level) * 0.5;
|
signal_level = signal_level + (current_level - signal_level) * 0.5;
|
||||||
} else {
|
} else {
|
||||||
signal_level = signal_level + (current_level-signal_level) * 0.05;
|
signal_level = signal_level + (current_level - signal_level) * 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AudioThreadInput *ati = NULL;
|
AudioThreadInput *ati = NULL;
|
||||||
|
|
||||||
if (audioInputQueue != NULL) {
|
if (audioInputQueue != NULL) {
|
||||||
@ -242,8 +286,8 @@ void DemodulatorThread::threadMain() {
|
|||||||
ati_vis->data.resize(stereoSize);
|
ati_vis->data.resize(stereoSize);
|
||||||
|
|
||||||
for (int i = 0; i < stereoSize / 2; i++) {
|
for (int i = 0; i < stereoSize / 2; i++) {
|
||||||
ati_vis->data[i] = ati->data[i*2];
|
ati_vis->data[i] = ati->data[i * 2];
|
||||||
ati_vis->data[i + stereoSize / 2] = ati->data[i*2+1];
|
ati_vis->data[i + stereoSize / 2] = ati->data[i * 2 + 1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ati_vis->channels = 1;
|
ati_vis->channels = 1;
|
||||||
@ -352,3 +396,11 @@ void DemodulatorThread::setSquelchLevel(float signal_level_in) {
|
|||||||
float DemodulatorThread::getSquelchLevel() {
|
float DemodulatorThread::getSquelchLevel() {
|
||||||
return squelch_level;
|
return squelch_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DemodulatorThread::setDemodulatorType(DemodulatorType demod_type_in) {
|
||||||
|
demodulatorType = demod_type_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
DemodulatorType DemodulatorThread::getDemodulatorType() {
|
||||||
|
return demodulatorType;
|
||||||
|
}
|
||||||
|
@ -34,13 +34,14 @@ public:
|
|||||||
void initialize();
|
void initialize();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
void setStereo(bool state);
|
void setStereo(bool state);bool isStereo();
|
||||||
bool isStereo();
|
|
||||||
|
|
||||||
float getSignalLevel();
|
float getSignalLevel();
|
||||||
void setSquelchLevel(float signal_level_in);
|
void setSquelchLevel(float signal_level_in);
|
||||||
float getSquelchLevel();
|
float getSquelchLevel();
|
||||||
|
|
||||||
|
void setDemodulatorType(DemodulatorType demod_type_in);
|
||||||
|
DemodulatorType getDemodulatorType();
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
static void *pthread_helper(void *context) {
|
static void *pthread_helper(void *context) {
|
||||||
@ -49,20 +50,39 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
std::deque<AudioThreadInput *> buffers;
|
||||||
|
std::deque<AudioThreadInput *>::iterator buffers_i;
|
||||||
|
|
||||||
|
std::vector<liquid_float_complex> resampled_data;
|
||||||
|
std::vector<liquid_float_complex> agc_data;
|
||||||
|
std::vector<float> agc_am_data;
|
||||||
|
std::vector<float> demod_output;
|
||||||
|
std::vector<float> demod_output_stereo;
|
||||||
|
std::vector<float> resampled_audio_output;
|
||||||
|
std::vector<float> resampled_audio_output_stereo;
|
||||||
|
|
||||||
DemodulatorThreadPostInputQueue* postInputQueue;
|
DemodulatorThreadPostInputQueue* postInputQueue;
|
||||||
DemodulatorThreadOutputQueue* visOutQueue;
|
DemodulatorThreadOutputQueue* visOutQueue;
|
||||||
AudioThreadInputQueue *audioInputQueue;
|
AudioThreadInputQueue *audioInputQueue;
|
||||||
|
|
||||||
freqdem fdem;
|
freqdem fdem;
|
||||||
|
ampmodem ampdem;
|
||||||
|
ampmodem ampdem_usb;
|
||||||
|
ampmodem ampdem_lsb;
|
||||||
|
|
||||||
agc_crcf agc;
|
agc_crcf agc;
|
||||||
|
|
||||||
|
float am_max;
|
||||||
|
float am_max_ma;
|
||||||
|
float am_max_maa;
|
||||||
|
|
||||||
std::atomic<bool> stereo;
|
std::atomic<bool> stereo;
|
||||||
std::atomic<bool> terminated;
|
std::atomic<bool> terminated;
|
||||||
|
std::atomic<DemodulatorType> demodulatorType;
|
||||||
|
|
||||||
DemodulatorThreadCommandQueue* threadQueueNotify;
|
DemodulatorThreadCommandQueue* threadQueueNotify;
|
||||||
DemodulatorThreadControlCommandQueue *threadQueueControl;
|
DemodulatorThreadControlCommandQueue *threadQueueControl;
|
||||||
std::atomic<float> squelch_level;
|
std::atomic<float> squelch_level;
|
||||||
float squelch_tolerance;
|
float squelch_tolerance;
|
||||||
std::atomic<float> signal_level;
|
std::atomic<float> signal_level;bool squelch_enabled;
|
||||||
bool squelch_enabled;
|
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
|
|
||||||
#include <wx/numformatter.h>
|
#include <wx/numformatter.h>
|
||||||
|
|
||||||
#define MIN_FM_BANDWIDTH 10000
|
#define MIN_FM_BANDWIDTH 2000
|
||||||
|
#define MIN_AM_BANDWIDTH 2000
|
||||||
|
|
||||||
|
|
||||||
wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint)
|
wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint)
|
||||||
EVT_KEY_DOWN(WaterfallCanvas::OnKeyDown)
|
EVT_KEY_DOWN(WaterfallCanvas::OnKeyDown)
|
||||||
|
Loading…
Reference in New Issue
Block a user