mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-05 08:51:18 -05:00
Merge pull request #22 from cjcliffe/ui-demodulator
Demodulator updates and More
This commit is contained in:
commit
cfe35dc922
@ -136,6 +136,9 @@ SET (cubicsdr_sources
|
|||||||
src/util/MouseTracker.cpp
|
src/util/MouseTracker.cpp
|
||||||
src/util/GLFont.cpp
|
src/util/GLFont.cpp
|
||||||
src/visual/PrimaryGLContext.cpp
|
src/visual/PrimaryGLContext.cpp
|
||||||
|
src/visual/InteractiveCanvas.cpp
|
||||||
|
src/visual/MeterCanvas.cpp
|
||||||
|
src/visual/MeterContext.cpp
|
||||||
src/visual/ScopeCanvas.cpp
|
src/visual/ScopeCanvas.cpp
|
||||||
src/visual/ScopeContext.cpp
|
src/visual/ScopeContext.cpp
|
||||||
src/visual/SpectrumCanvas.cpp
|
src/visual/SpectrumCanvas.cpp
|
||||||
@ -165,6 +168,9 @@ SET (cubicsdr_headers
|
|||||||
src/util/MouseTracker.h
|
src/util/MouseTracker.h
|
||||||
src/util/GLFont.h
|
src/util/GLFont.h
|
||||||
src/visual/PrimaryGLContext.h
|
src/visual/PrimaryGLContext.h
|
||||||
|
src/visual/InteractiveCanvas.h
|
||||||
|
src/visual/MeterCanvas.h
|
||||||
|
src/visual/MeterContext.h
|
||||||
src/visual/ScopeCanvas.h
|
src/visual/ScopeCanvas.h
|
||||||
src/visual/ScopeContext.h
|
src/visual/ScopeContext.h
|
||||||
src/visual/SpectrumCanvas.h
|
src/visual/SpectrumCanvas.h
|
||||||
|
21
README.md
21
README.md
@ -38,20 +38,19 @@ Basic Goals and Status:
|
|||||||
- [ ] 3D visuals
|
- [ ] 3D visuals
|
||||||
- Demodulation:
|
- Demodulation:
|
||||||
- [x] Multiple demodulators per IQ stream
|
- [x] Multiple demodulators per IQ stream
|
||||||
- [ ] Audio device selection
|
- [x] Audio device selection
|
||||||
- [ ] Modes
|
- [x] Modes
|
||||||
- [x] FM
|
- [x] FM
|
||||||
- [x] WFM
|
- [x] FM stereo
|
||||||
- [x] WBFM stereo
|
- [x] AM
|
||||||
- [ ] AM
|
- [x] LSB
|
||||||
- [ ] LSB
|
- [x] USB
|
||||||
- [ ] USB
|
|
||||||
- [ ] Controls
|
- [ ] Controls
|
||||||
- [ ] Display Frequency and allow manual adjustments
|
- [ ] Display Frequency and allow manual adjustments
|
||||||
- [ ] Allow selection of demodulation type
|
- [x] Allow selection of demodulation type
|
||||||
- [ ] Display separate zoomed-in view of current waterfall and spectrum, allow adjustments
|
- [x] Display separate zoomed-in view of current waterfall and spectrum, allow adjustments
|
||||||
- [ ] Display signal level and allow squelch control
|
- [x] Display signal level and allow squelch control
|
||||||
- [ ] Display audio output selection
|
- [x] Display audio output selection
|
||||||
- [ ] Volume control
|
- [ ] Volume control
|
||||||
- [ ] Basic noise reduction / filter controls?
|
- [ ] Basic noise reduction / filter controls?
|
||||||
- Basic Input Controls
|
- Basic Input Controls
|
||||||
|
156
src/AppFrame.cpp
156
src/AppFrame.cpp
@ -18,25 +18,62 @@
|
|||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include <wx/panel.h>
|
||||||
|
|
||||||
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
|
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
|
||||||
//EVT_MENU(wxID_NEW, AppFrame::OnNewWindow)
|
//EVT_MENU(wxID_NEW, AppFrame::OnNewWindow)
|
||||||
EVT_MENU(wxID_CLOSE, AppFrame::OnClose)
|
EVT_MENU(wxID_CLOSE, AppFrame::OnClose)
|
||||||
|
EVT_MENU(wxID_ANY, AppFrame::OnMenu)
|
||||||
|
|
||||||
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
|
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
|
||||||
EVT_IDLE(AppFrame::OnIdle)
|
EVT_IDLE(AppFrame::OnIdle)
|
||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
AppFrame::AppFrame() :
|
AppFrame::AppFrame() :
|
||||||
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) {
|
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")), activeDemodulator(NULL) {
|
||||||
|
|
||||||
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
|
||||||
|
wxBoxSizer *demodOpts = new wxBoxSizer(wxVERTICAL);
|
||||||
|
wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL);
|
||||||
|
wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
demodSpectrumCanvas = new SpectrumCanvas(this, NULL);
|
||||||
|
demodSpectrumCanvas->Setup(1024);
|
||||||
|
demodSpectrumCanvas->SetView(DEFAULT_FREQ, 300000);
|
||||||
|
demodVisuals->Add(demodSpectrumCanvas, 1, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
|
demodVisuals->AddSpacer(1);
|
||||||
|
|
||||||
|
demodWaterfallCanvas = new WaterfallCanvas(this, NULL);
|
||||||
|
demodWaterfallCanvas->Setup(1024, 256);
|
||||||
|
demodWaterfallCanvas->SetView(DEFAULT_FREQ, 300000);
|
||||||
|
demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
|
||||||
|
demodSpectrumCanvas->attachWaterfallCanvas(demodWaterfallCanvas);
|
||||||
|
demodVisuals->Add(demodWaterfallCanvas, 3, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
|
demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
|
demodTray->AddSpacer(2);
|
||||||
|
|
||||||
|
demodSignalMeter = new MeterCanvas(this, NULL);
|
||||||
|
demodSignalMeter->setMax(0.5);
|
||||||
|
demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
|
demodTray->AddSpacer(2);
|
||||||
|
|
||||||
scopeCanvas = new ScopeCanvas(this, NULL);
|
scopeCanvas = new ScopeCanvas(this, NULL);
|
||||||
vbox->Add(scopeCanvas, 1, wxEXPAND | wxALL, 0);
|
demodTray->Add(scopeCanvas, 30, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
|
vbox->Add(demodTray, 2, wxEXPAND | wxALL, 0);
|
||||||
vbox->AddSpacer(2);
|
vbox->AddSpacer(2);
|
||||||
spectrumCanvas = new SpectrumCanvas(this, NULL);
|
spectrumCanvas = new SpectrumCanvas(this, NULL);
|
||||||
|
spectrumCanvas->Setup(2048);
|
||||||
vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0);
|
vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0);
|
||||||
vbox->AddSpacer(2);
|
vbox->AddSpacer(2);
|
||||||
waterfallCanvas = new WaterfallCanvas(this, NULL);
|
waterfallCanvas = new WaterfallCanvas(this, NULL);
|
||||||
|
waterfallCanvas->Setup(2048, 512);
|
||||||
|
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
|
||||||
|
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
|
||||||
vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0);
|
vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
this->SetSizer(vbox);
|
this->SetSizer(vbox);
|
||||||
@ -47,12 +84,52 @@ AppFrame::AppFrame() :
|
|||||||
// SetIcon(wxICON(sample));
|
// SetIcon(wxICON(sample));
|
||||||
|
|
||||||
// Make a menubar
|
// Make a menubar
|
||||||
wxMenu *menu = new wxMenu;
|
// wxMenu *menu = new wxMenu;
|
||||||
// menu->Append(wxID_NEW);
|
// menu->Append(wxID_NEW);
|
||||||
// menu->AppendSeparator();
|
// menu->AppendSeparator();
|
||||||
menu->Append(wxID_CLOSE);
|
// menu->Append(wxID_CLOSE);
|
||||||
|
// wxMenuBar *menuBar = new wxMenuBar;
|
||||||
|
// menuBar->Append(menu, wxT("&File"));
|
||||||
|
|
||||||
|
wxMenu *menu = new wxMenu;
|
||||||
|
|
||||||
|
std::vector<RtAudio::DeviceInfo>::iterator devices_i;
|
||||||
|
std::map<int, RtAudio::DeviceInfo>::iterator mdevices_i;
|
||||||
|
AudioThread::enumerateDevices(devices);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (devices_i = devices.begin(); devices_i != devices.end(); devices_i++) {
|
||||||
|
if (devices_i->inputChannels) {
|
||||||
|
input_devices[i] = *devices_i;
|
||||||
|
}
|
||||||
|
if (devices_i->outputChannels) {
|
||||||
|
output_devices[i] = *devices_i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
for (mdevices_i = output_devices.begin(); mdevices_i != output_devices.end(); mdevices_i++) {
|
||||||
|
wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE + mdevices_i->first, mdevices_i->second.name, wxT("Description?"));
|
||||||
|
itm->SetId(wxID_RT_AUDIO_DEVICE + mdevices_i->first);
|
||||||
|
if (mdevices_i->second.isDefaultOutput) {
|
||||||
|
itm->Check(true);
|
||||||
|
}
|
||||||
|
output_device_menuitems[mdevices_i->first] = itm;
|
||||||
|
}
|
||||||
|
|
||||||
wxMenuBar *menuBar = new wxMenuBar;
|
wxMenuBar *menuBar = new wxMenuBar;
|
||||||
menuBar->Append(menu, wxT("&File"));
|
menuBar->Append(menu, wxT("Active Demodulator &Output"));
|
||||||
|
|
||||||
|
wxMenu *demodMenu = new wxMenu;
|
||||||
|
demod_menuitems[DEMOD_TYPE_FM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_FM, wxT("FM"), wxT("Description?"));
|
||||||
|
demod_menuitems[DEMOD_TYPE_AM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_AM, wxT("AM"), wxT("Description?"));
|
||||||
|
demod_menuitems[DEMOD_TYPE_LSB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_LSB, wxT("LSB"), wxT("Description?"));
|
||||||
|
demod_menuitems[DEMOD_TYPE_USB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_USB, wxT("USB"), wxT("Description?"));
|
||||||
|
|
||||||
|
menuBar->Append(demodMenu, wxT("Active Demodulator &Type"));
|
||||||
|
|
||||||
SetMenuBar(menuBar);
|
SetMenuBar(menuBar);
|
||||||
|
|
||||||
@ -70,13 +147,34 @@ AppFrame::AppFrame() :
|
|||||||
|
|
||||||
AppFrame::~AppFrame() {
|
AppFrame::~AppFrame() {
|
||||||
|
|
||||||
// delete t_SDR;
|
}
|
||||||
|
|
||||||
|
void AppFrame::OnMenu(wxCommandEvent& event) {
|
||||||
|
if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE + devices.size()) {
|
||||||
|
if (activeDemodulator) {
|
||||||
|
activeDemodulator->setOutputDevice(event.GetId() - wxID_RT_AUDIO_DEVICE);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeDemodulator) {
|
||||||
|
if (event.GetId() == wxID_DEMOD_TYPE_FM) {
|
||||||
|
activeDemodulator->setDemodulatorType(DEMOD_TYPE_FM);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
} else if (event.GetId() == wxID_DEMOD_TYPE_AM) {
|
||||||
|
activeDemodulator->setDemodulatorType(DEMOD_TYPE_AM);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
} else if (event.GetId() == wxID_DEMOD_TYPE_LSB) {
|
||||||
|
activeDemodulator->setDemodulatorType(DEMOD_TYPE_LSB);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
} else if (event.GetId() == wxID_DEMOD_TYPE_USB) {
|
||||||
|
activeDemodulator->setDemodulatorType(DEMOD_TYPE_USB);
|
||||||
|
activeDemodulator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) {
|
void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) {
|
||||||
|
|
||||||
// true is to force the frame to close
|
|
||||||
Close(true);
|
Close(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,14 +193,48 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
// std::this_thread::sleep_for(std::chrono::milliseconds(4));
|
// std::this_thread::sleep_for(std::chrono::milliseconds(4));
|
||||||
// std::this_thread::yield();
|
// std::this_thread::yield();
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
|
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||||
|
|
||||||
|
if (demod) {
|
||||||
|
if (demod != activeDemodulator) {
|
||||||
|
demodSignalMeter->setInputValue(demod->getSquelchLevel());
|
||||||
|
int outputDevice = demod->getOutputDevice();
|
||||||
|
scopeCanvas->setDeviceName(output_devices[outputDevice].name);
|
||||||
|
output_device_menuitems[outputDevice]->Check(true);
|
||||||
|
int dType = demod->getDemodulatorType();
|
||||||
|
demod_menuitems[dType]->Check(true);
|
||||||
|
}
|
||||||
|
if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
|
||||||
|
if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) {
|
||||||
|
demodWaterfallCanvas->SetCenterFrequency(demod->getParams().frequency);
|
||||||
|
demodSpectrumCanvas->SetCenterFrequency(demod->getParams().frequency);
|
||||||
|
}
|
||||||
|
unsigned int demodBw = (unsigned int) ceil((float) demod->getParams().bandwidth * 2.5);
|
||||||
|
if (demodBw > SRATE / 2) {
|
||||||
|
demodBw = SRATE / 2;
|
||||||
|
}
|
||||||
|
if (demodBw < 80000) {
|
||||||
|
demodBw = 80000;
|
||||||
|
}
|
||||||
|
demodWaterfallCanvas->SetBandwidth(demodBw);
|
||||||
|
demodSpectrumCanvas->SetBandwidth(demodBw);
|
||||||
|
}
|
||||||
|
demodSignalMeter->setLevel(demod->getSignalLevel());
|
||||||
|
if (demodSignalMeter->inputChanged()) {
|
||||||
|
demod->setSquelchLevel(demodSignalMeter->getInputValue());
|
||||||
|
}
|
||||||
|
activeDemodulator = demod;
|
||||||
|
}
|
||||||
|
|
||||||
if (!wxGetApp().getIQVisualQueue()->empty()) {
|
if (!wxGetApp().getIQVisualQueue()->empty()) {
|
||||||
DemodulatorThreadIQData *iqData;
|
DemodulatorThreadIQData *iqData;
|
||||||
wxGetApp().getIQVisualQueue()->pop(iqData);
|
wxGetApp().getIQVisualQueue()->pop(iqData);
|
||||||
|
|
||||||
if (iqData && iqData->data.size()) {
|
if (iqData && iqData->data.size()) {
|
||||||
spectrumCanvas->setData(&iqData->data);
|
// spectrumCanvas->setData(iqData);
|
||||||
waterfallCanvas->setData(&iqData->data);
|
waterfallCanvas->setData(iqData);
|
||||||
|
demodWaterfallCanvas->setData(iqData);
|
||||||
delete iqData;
|
delete iqData;
|
||||||
} else {
|
} else {
|
||||||
std::cout << "Incoming IQ data empty?" << std::endl;
|
std::cout << "Incoming IQ data empty?" << std::endl;
|
||||||
@ -123,7 +255,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
scopeCanvas->waveform_points[i * 2] = ((double) i / (double) iMax);
|
scopeCanvas->waveform_points[i * 2] = ((double) i / (double) iMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
scopeCanvas->setDivider(demodAudioData->channels == 2);
|
scopeCanvas->setStereo(demodAudioData->channels == 2);
|
||||||
|
|
||||||
delete demodAudioData;
|
delete demodAudioData;
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,6 +6,15 @@
|
|||||||
#include "ScopeCanvas.h"
|
#include "ScopeCanvas.h"
|
||||||
#include "SpectrumCanvas.h"
|
#include "SpectrumCanvas.h"
|
||||||
#include "WaterfallCanvas.h"
|
#include "WaterfallCanvas.h"
|
||||||
|
#include "MeterCanvas.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#define wxID_RT_AUDIO_DEVICE 1000
|
||||||
|
#define wxID_DEMOD_TYPE_FM 2000
|
||||||
|
#define wxID_DEMOD_TYPE_AM 2001
|
||||||
|
#define wxID_DEMOD_TYPE_LSB 2002
|
||||||
|
#define wxID_DEMOD_TYPE_USB 2003
|
||||||
|
|
||||||
// Define a new frame type
|
// Define a new frame type
|
||||||
class AppFrame: public wxFrame {
|
class AppFrame: public wxFrame {
|
||||||
@ -16,6 +25,7 @@ public:
|
|||||||
void OnEventInput(wxThreadEvent& event);
|
void OnEventInput(wxThreadEvent& event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void OnMenu(wxCommandEvent& event);
|
||||||
void OnClose(wxCommandEvent& event);
|
void OnClose(wxCommandEvent& event);
|
||||||
void OnNewWindow(wxCommandEvent& event);
|
void OnNewWindow(wxCommandEvent& event);
|
||||||
void OnIdle(wxIdleEvent& event);
|
void OnIdle(wxIdleEvent& event);
|
||||||
@ -23,7 +33,19 @@ private:
|
|||||||
ScopeCanvas *scopeCanvas;
|
ScopeCanvas *scopeCanvas;
|
||||||
SpectrumCanvas *spectrumCanvas;
|
SpectrumCanvas *spectrumCanvas;
|
||||||
WaterfallCanvas *waterfallCanvas;
|
WaterfallCanvas *waterfallCanvas;
|
||||||
|
SpectrumCanvas *demodSpectrumCanvas;
|
||||||
|
WaterfallCanvas *demodWaterfallCanvas;
|
||||||
|
MeterCanvas *demodSignalMeter;
|
||||||
// event table
|
// event table
|
||||||
|
|
||||||
|
DemodulatorInstance *activeDemodulator;
|
||||||
|
|
||||||
|
std::vector<RtAudio::DeviceInfo> devices;
|
||||||
|
std::map<int,RtAudio::DeviceInfo> input_devices;
|
||||||
|
std::map<int,RtAudio::DeviceInfo> output_devices;
|
||||||
|
std::map<int,wxMenuItem *> output_device_menuitems;
|
||||||
|
|
||||||
|
std::map<int,wxMenuItem *> demod_menuitems;
|
||||||
|
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
@ -28,6 +28,7 @@ bool CubicSDR::OnInit() {
|
|||||||
sdrThread = new SDRThread(threadCmdQueueSDR);
|
sdrThread = new SDRThread(threadCmdQueueSDR);
|
||||||
|
|
||||||
sdrPostThread = new SDRPostThread();
|
sdrPostThread = new SDRPostThread();
|
||||||
|
sdrPostThread->setNumVisSamples(2048);
|
||||||
|
|
||||||
iqPostDataQueue = new SDRThreadIQDataQueue;
|
iqPostDataQueue = new SDRThreadIQDataQueue;
|
||||||
iqVisualQueue = new DemodulatorThreadInputQueue;
|
iqVisualQueue = new DemodulatorThreadInputQueue;
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
#define BUF_SIZE (16384*2)
|
#define BUF_SIZE (16384*2)
|
||||||
#define SRATE 2000000
|
#define SRATE 2000000
|
||||||
#else
|
#else
|
||||||
#define BUF_SIZE (16384*4)
|
#define BUF_SIZE (16384*5)
|
||||||
#define SRATE 2500000
|
#define SRATE 2500000
|
||||||
#endif
|
#endif
|
||||||
#define FFT_SIZE 2048
|
#define DEFAULT_FFT_SIZE 2048
|
||||||
|
|
||||||
#define DEFAULT_FREQ 98900000
|
//#define DEFAULT_FREQ 98900000
|
||||||
|
#define DEFAULT_FREQ 132000000
|
||||||
#define AUDIO_FREQUENCY 44100
|
#define AUDIO_FREQUENCY 44100
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -10,7 +10,7 @@ std::map<int, std::thread *> AudioThread::deviceThread;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
AudioThread::AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify) :
|
AudioThread::AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify) :
|
||||||
currentInput(NULL), inputQueue(inputQueue), audio_queue_ptr(0), underflow_count(0), terminated(false), active(false), gain(1.0), threadQueueNotify(
|
currentInput(NULL), inputQueue(inputQueue), audio_queue_ptr(0), underflow_count(0), terminated(false), active(false), output_device(-1), gain(1.0), threadQueueNotify(
|
||||||
threadQueueNotify) {
|
threadQueueNotify) {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
boundThreads = new std::vector<AudioThread *>;
|
boundThreads = new std::vector<AudioThread *>;
|
||||||
@ -25,8 +25,10 @@ AudioThread::~AudioThread() {
|
|||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
void AudioThread::bindThread(AudioThread *other) {
|
void AudioThread::bindThread(AudioThread *other) {
|
||||||
|
if (std::find(boundThreads.load()->begin(), boundThreads.load()->end(), other) == boundThreads.load()->end()) {
|
||||||
boundThreads.load()->push_back(other);
|
boundThreads.load()->push_back(other);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AudioThread::removeThread(AudioThread *other) {
|
void AudioThread::removeThread(AudioThread *other) {
|
||||||
std::vector<AudioThread *>::iterator i;
|
std::vector<AudioThread *>::iterator i;
|
||||||
@ -211,11 +213,15 @@ static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBu
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void AudioThread::enumerateDevices() {
|
void AudioThread::enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs) {
|
||||||
int numDevices = dac.getDeviceCount();
|
RtAudio endac;
|
||||||
|
|
||||||
|
int numDevices = endac.getDeviceCount();
|
||||||
|
|
||||||
for (int i = 0; i < numDevices; i++) {
|
for (int i = 0; i < numDevices; i++) {
|
||||||
RtAudio::DeviceInfo info = dac.getDeviceInfo(i);
|
RtAudio::DeviceInfo info = endac.getDeviceInfo(i);
|
||||||
|
|
||||||
|
devs.push_back(info);
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
@ -259,6 +265,70 @@ void AudioThread::enumerateDevices() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioThread::setupDevice(int deviceId) {
|
||||||
|
parameters.deviceId = deviceId;
|
||||||
|
parameters.nChannels = 2;
|
||||||
|
parameters.firstChannel = 0;
|
||||||
|
unsigned int sampleRate = AUDIO_FREQUENCY;
|
||||||
|
unsigned int bufferFrames = 256;
|
||||||
|
|
||||||
|
RtAudio::StreamOptions opts;
|
||||||
|
opts.streamName = "CubicSDR Audio Output";
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (deviceController.find(output_device.load()) != deviceController.end()) {
|
||||||
|
deviceController[output_device.load()]->removeThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.priority = sched_get_priority_max(SCHED_FIFO);
|
||||||
|
// opts.flags = RTAUDIO_MINIMIZE_LATENCY;
|
||||||
|
opts.flags = RTAUDIO_SCHEDULE_REALTIME;
|
||||||
|
|
||||||
|
if (deviceController.find(parameters.deviceId) == deviceController.end()) {
|
||||||
|
deviceController[parameters.deviceId] = new AudioThread(NULL, NULL);
|
||||||
|
deviceController[parameters.deviceId]->setInitOutputDevice(parameters.deviceId);
|
||||||
|
deviceController[parameters.deviceId]->bindThread(this);
|
||||||
|
deviceThread[parameters.deviceId] = new std::thread(&AudioThread::threadMain, deviceController[parameters.deviceId]);
|
||||||
|
} else if (deviceController[parameters.deviceId] == this) {
|
||||||
|
dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts);
|
||||||
|
dac.startStream();
|
||||||
|
} else {
|
||||||
|
deviceController[parameters.deviceId]->bindThread(this);
|
||||||
|
}
|
||||||
|
active = true;
|
||||||
|
#else
|
||||||
|
if (dac.isStreamOpen()) {
|
||||||
|
if (dac.isStreamRunning()) {
|
||||||
|
dac.stopStream();
|
||||||
|
}
|
||||||
|
dac.closeStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts);
|
||||||
|
dac.startStream();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
} catch (RtAudioError& e) {
|
||||||
|
e.printMessage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_device = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioThread::getOutputDevice() {
|
||||||
|
if (output_device == -1) {
|
||||||
|
return dac.getDefaultOutputDevice();
|
||||||
|
}
|
||||||
|
return output_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioThread::setInitOutputDevice(int deviceId) {
|
||||||
|
output_device = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioThread::threadMain() {
|
void AudioThread::threadMain() {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
pthread_t tID = pthread_self(); // ID of this thread
|
pthread_t tID = pthread_self(); // ID of this thread
|
||||||
@ -274,46 +344,17 @@ void AudioThread::threadMain() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters.deviceId = dac.getDefaultOutputDevice();
|
setupDevice((output_device.load() == -1)?(dac.getDefaultOutputDevice()):output_device.load());
|
||||||
parameters.nChannels = 2;
|
|
||||||
parameters.firstChannel = 0;
|
|
||||||
unsigned int sampleRate = AUDIO_FREQUENCY;
|
|
||||||
unsigned int bufferFrames = 256;
|
|
||||||
|
|
||||||
RtAudio::StreamOptions opts;
|
std::cout << "Audio thread started." << std::endl;
|
||||||
opts.streamName = "CubicSDR Audio Output";
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
opts.priority = sched_get_priority_max(SCHED_FIFO);
|
|
||||||
// opts.flags = RTAUDIO_MINIMIZE_LATENCY;
|
|
||||||
opts.flags = RTAUDIO_SCHEDULE_REALTIME;
|
|
||||||
|
|
||||||
if (deviceController.find(parameters.deviceId) == deviceController.end()) {
|
|
||||||
deviceController[parameters.deviceId] = new AudioThread(NULL, NULL);
|
|
||||||
deviceController[parameters.deviceId]->bindThread(this);
|
|
||||||
deviceThread[parameters.deviceId] = new std::thread(&AudioThread::threadMain, deviceController[parameters.deviceId]);
|
|
||||||
} else if (deviceController[parameters.deviceId] == this) {
|
|
||||||
dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts);
|
|
||||||
dac.startStream();
|
|
||||||
} else {
|
|
||||||
deviceController[parameters.deviceId]->bindThread(this);
|
|
||||||
}
|
|
||||||
active = true;
|
|
||||||
#else
|
|
||||||
dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts);
|
|
||||||
dac.startStream();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
} catch (RtAudioError& e) {
|
|
||||||
e.printMessage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!terminated) {
|
while (!terminated) {
|
||||||
AudioThreadCommand command;
|
AudioThreadCommand command;
|
||||||
cmdQueue.pop(command);
|
cmdQueue.pop(command);
|
||||||
|
|
||||||
|
if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE) {
|
||||||
|
setupDevice(command.int_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@ -367,7 +408,7 @@ void AudioThread::setActive(bool state) {
|
|||||||
while (!inputQueue->empty()) { // flush queue
|
while (!inputQueue->empty()) { // flush queue
|
||||||
inputQueue->pop(dummy);
|
inputQueue->pop(dummy);
|
||||||
if (dummy) {
|
if (dummy) {
|
||||||
delete dummy;
|
dummy->decRefCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deviceController[parameters.deviceId]->bindThread(this);
|
deviceController[parameters.deviceId]->bindThread(this);
|
||||||
@ -376,10 +417,15 @@ void AudioThread::setActive(bool state) {
|
|||||||
while (!inputQueue->empty()) { // flush queue
|
while (!inputQueue->empty()) { // flush queue
|
||||||
inputQueue->pop(dummy);
|
inputQueue->pop(dummy);
|
||||||
if (dummy) {
|
if (dummy) {
|
||||||
delete dummy;
|
dummy->decRefCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
active = state;
|
active = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AudioThreadCommandQueue *AudioThread::getCommandQueue() {
|
||||||
|
return &cmdQueue;
|
||||||
|
}
|
||||||
|
@ -61,19 +61,25 @@ public:
|
|||||||
std::atomic<unsigned int> underflow_count;
|
std::atomic<unsigned int> underflow_count;
|
||||||
std::atomic<bool> terminated;
|
std::atomic<bool> terminated;
|
||||||
std::atomic<bool> active;
|
std::atomic<bool> active;
|
||||||
|
std::atomic<int> output_device;
|
||||||
float gain;
|
float gain;
|
||||||
|
|
||||||
AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify);
|
AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify);
|
||||||
~AudioThread();
|
~AudioThread();
|
||||||
|
|
||||||
void enumerateDevices();
|
static void enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs);
|
||||||
|
|
||||||
|
void setupDevice(int deviceId);
|
||||||
|
void setInitOutputDevice(int deviceId);
|
||||||
|
int getOutputDevice();
|
||||||
void threadMain();
|
void threadMain();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
bool isActive();
|
bool isActive();
|
||||||
void setActive(bool state);
|
void setActive(bool state);
|
||||||
|
|
||||||
|
AudioThreadCommandQueue *getCommandQueue();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RtAudio dac;
|
RtAudio dac;
|
||||||
RtAudio::StreamParameters parameters;
|
RtAudio::StreamParameters parameters;
|
||||||
|
@ -7,9 +7,12 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
enum DemodulatorType {
|
#define DEMOD_TYPE_NULL 0
|
||||||
DEMOD_TYPE_NULL, DEMOD_TYPE_AM, DEMOD_TYPE_FM, DEMOD_TYPE_LSB, DEMOD_TYPE_USB
|
#define DEMOD_TYPE_FM 1
|
||||||
};
|
#define DEMOD_TYPE_AM 2
|
||||||
|
#define DEMOD_TYPE_LSB 3
|
||||||
|
#define DEMOD_TYPE_USB 4
|
||||||
|
|
||||||
|
|
||||||
class DemodulatorThread;
|
class DemodulatorThread;
|
||||||
class DemodulatorThreadCommand {
|
class DemodulatorThreadCommand {
|
||||||
@ -41,14 +44,15 @@ public:
|
|||||||
class DemodulatorThreadControlCommand {
|
class DemodulatorThreadControlCommand {
|
||||||
public:
|
public:
|
||||||
enum DemodulatorThreadControlCommandEnum {
|
enum DemodulatorThreadControlCommandEnum {
|
||||||
DEMOD_THREAD_CMD_CTL_NULL, DEMOD_THREAD_CMD_CTL_SQUELCH_AUTO, DEMOD_THREAD_CMD_CTL_SQUELCH_OFF
|
DEMOD_THREAD_CMD_CTL_NULL, DEMOD_THREAD_CMD_CTL_SQUELCH_AUTO, DEMOD_THREAD_CMD_CTL_SQUELCH_OFF, DEMOD_THREAD_CMD_CTL_TYPE
|
||||||
};
|
};
|
||||||
|
|
||||||
DemodulatorThreadControlCommand() :
|
DemodulatorThreadControlCommand() :
|
||||||
cmd(DEMOD_THREAD_CMD_CTL_NULL) {
|
cmd(DEMOD_THREAD_CMD_CTL_NULL), demodType(DEMOD_TYPE_NULL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DemodulatorThreadControlCommandEnum cmd;
|
DemodulatorThreadControlCommandEnum cmd;
|
||||||
|
int demodType;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DemodulatorThreadIQData: public ReferenceCounter {
|
class DemodulatorThreadIQData: public ReferenceCounter {
|
||||||
@ -71,10 +75,10 @@ class DemodulatorThreadPostIQData: public ReferenceCounter {
|
|||||||
public:
|
public:
|
||||||
std::vector<liquid_float_complex> data;
|
std::vector<liquid_float_complex> data;
|
||||||
int bandwidth;
|
int bandwidth;
|
||||||
float audio_resample_ratio;
|
double audio_resample_ratio;
|
||||||
msresamp_rrrf audio_resampler;
|
msresamp_rrrf audio_resampler;
|
||||||
msresamp_rrrf stereo_resampler;
|
msresamp_rrrf stereo_resampler;
|
||||||
float resample_ratio;
|
double resample_ratio;
|
||||||
msresamp_crcf resampler;
|
msresamp_crcf resampler;
|
||||||
|
|
||||||
DemodulatorThreadPostIQData() :
|
DemodulatorThreadPostIQData() :
|
||||||
@ -122,7 +126,7 @@ public:
|
|||||||
unsigned int bandwidth; // set equal to disable second stage re-sampling?
|
unsigned int bandwidth; // set equal to disable second stage re-sampling?
|
||||||
unsigned int audioSampleRate;
|
unsigned int audioSampleRate;
|
||||||
|
|
||||||
DemodulatorType demodType;
|
int demodType;
|
||||||
|
|
||||||
DemodulatorThreadParameters() :
|
DemodulatorThreadParameters() :
|
||||||
frequency(0), inputRate(SRATE), bandwidth(200000), audioSampleRate(
|
frequency(0), inputRate(SRATE), bandwidth(200000), audioSampleRate(
|
||||||
|
@ -19,6 +19,8 @@ DemodulatorInstance::DemodulatorInstance() :
|
|||||||
audioThread = new AudioThread(audioInputQueue, threadQueueNotify);
|
audioThread = new AudioThread(audioInputQueue, threadQueueNotify);
|
||||||
|
|
||||||
demodulatorThread->setAudioInputQueue(audioInputQueue);
|
demodulatorThread->setAudioInputQueue(audioInputQueue);
|
||||||
|
|
||||||
|
currentDemodType = demodulatorThread->getDemodulatorType();
|
||||||
}
|
}
|
||||||
|
|
||||||
DemodulatorInstance::~DemodulatorInstance() {
|
DemodulatorInstance::~DemodulatorInstance() {
|
||||||
@ -181,3 +183,44 @@ void DemodulatorInstance::setSquelchEnabled(bool state) {
|
|||||||
squelch = state;
|
squelch = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float DemodulatorInstance::getSignalLevel() {
|
||||||
|
return demodulatorThread->getSignalLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DemodulatorInstance::setSquelchLevel(float signal_level_in) {
|
||||||
|
demodulatorThread->setSquelchLevel(signal_level_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float DemodulatorInstance::getSquelchLevel() {
|
||||||
|
return demodulatorThread->getSquelchLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DemodulatorInstance::setOutputDevice(int device_id) {
|
||||||
|
if (audioThread) {
|
||||||
|
AudioThreadCommand command;
|
||||||
|
command.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE;
|
||||||
|
command.int_value = device_id;
|
||||||
|
audioThread->getCommandQueue()->push(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DemodulatorInstance::getOutputDevice() {
|
||||||
|
return audioThread->getOutputDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DemodulatorInstance::setDemodulatorType(int demod_type_in) {
|
||||||
|
if (demodulatorThread && threadQueueControl) {
|
||||||
|
DemodulatorThreadControlCommand command;
|
||||||
|
command.cmd = DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_TYPE;
|
||||||
|
currentDemodType = demod_type_in;
|
||||||
|
command.demodType = demod_type_in;
|
||||||
|
threadQueueControl->push(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DemodulatorInstance::getDemodulatorType() {
|
||||||
|
return currentDemodType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -52,13 +52,30 @@ public:
|
|||||||
bool isStereo();
|
bool isStereo();
|
||||||
void setStereo(bool state);
|
void setStereo(bool state);
|
||||||
|
|
||||||
void squelchAuto();bool isSquelchEnabled();
|
void squelchAuto();
|
||||||
|
bool isSquelchEnabled();
|
||||||
void setSquelchEnabled(bool state);
|
void setSquelchEnabled(bool state);
|
||||||
|
|
||||||
|
float getSignalLevel();
|
||||||
|
void setSquelchLevel(float signal_level_in);
|
||||||
|
float getSquelchLevel();
|
||||||
|
|
||||||
|
void setOutputDevice(int device_id);
|
||||||
|
int getOutputDevice();
|
||||||
|
|
||||||
|
void setDemodulatorType(int demod_type_in);
|
||||||
|
int getDemodulatorType();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<std::string *> label;bool terminated;bool demodTerminated;bool audioTerminated;bool preDemodTerminated;
|
std::atomic<std::string *> label; //
|
||||||
|
bool terminated; //
|
||||||
|
bool demodTerminated; //
|
||||||
|
bool audioTerminated; //
|
||||||
|
bool preDemodTerminated;
|
||||||
std::atomic<bool> active;
|
std::atomic<bool> active;
|
||||||
std::atomic<bool> squelch;
|
std::atomic<bool> squelch;
|
||||||
std::atomic<bool> stereo;
|
std::atomic<bool> stereo;
|
||||||
|
|
||||||
|
int currentDemodType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn, DemodulatorThreadPostInputQueue* pQueueOut,
|
DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn, DemodulatorThreadPostInputQueue* pQueueOut,
|
||||||
DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) :
|
DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) :
|
||||||
inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), stereo_resampler(NULL), resample_ratio(1), audio_resample_ratio(
|
inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), stereo_resampler(NULL), resample_ratio(
|
||||||
1), resampler(NULL), commandQueue(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl(
|
1), audio_resample_ratio(1), resampler(NULL), commandQueue(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl(
|
||||||
threadQueueControl) {
|
threadQueueControl) {
|
||||||
|
|
||||||
float kf = 0.5; // modulation factor
|
float kf = 0.5; // modulation factor
|
||||||
@ -30,33 +30,17 @@ DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn
|
|||||||
void DemodulatorPreThread::initialize() {
|
void DemodulatorPreThread::initialize() {
|
||||||
initialized = false;
|
initialized = false;
|
||||||
|
|
||||||
resample_ratio = (float) (params.bandwidth) / (float) params.inputRate;
|
resample_ratio = (double) (params.bandwidth) / (double) params.inputRate;
|
||||||
audio_resample_ratio = (float) (params.audioSampleRate) / (float) params.bandwidth;
|
audio_resample_ratio = (double) (params.audioSampleRate) / (double) params.bandwidth;
|
||||||
|
|
||||||
float As = 60.0f; // stop-band attenuation [dB]
|
float As = 60.0f; // stop-band attenuation [dB]
|
||||||
|
|
||||||
// create multi-stage arbitrary resampler object
|
|
||||||
if (resampler) {
|
|
||||||
msresamp_crcf_destroy(resampler);
|
|
||||||
}
|
|
||||||
resampler = msresamp_crcf_create(resample_ratio, As);
|
resampler = msresamp_crcf_create(resample_ratio, As);
|
||||||
// msresamp_crcf_print(resampler);
|
|
||||||
|
|
||||||
if (audio_resampler) {
|
|
||||||
msresamp_rrrf_destroy(audio_resampler);
|
|
||||||
}
|
|
||||||
audio_resampler = msresamp_rrrf_create(audio_resample_ratio, As);
|
audio_resampler = msresamp_rrrf_create(audio_resample_ratio, As);
|
||||||
// msresamp_crcf_print(audio_resampler);
|
|
||||||
|
|
||||||
if (stereo_resampler) {
|
|
||||||
msresamp_rrrf_destroy(stereo_resampler);
|
|
||||||
}
|
|
||||||
stereo_resampler = msresamp_rrrf_create(audio_resample_ratio, As);
|
stereo_resampler = msresamp_rrrf_create(audio_resample_ratio, As);
|
||||||
|
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
// std::cout << "inputResampleRate " << params.bandwidth << std::endl;
|
// std::cout << "inputResampleRate " << params.bandwidth << std::endl;
|
||||||
|
|
||||||
last_params = params;
|
last_params = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +88,8 @@ void DemodulatorPreThread::threadMain() {
|
|||||||
commandQueue->pop(command);
|
commandQueue->pop(command);
|
||||||
switch (command.cmd) {
|
switch (command.cmd) {
|
||||||
case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH:
|
case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH:
|
||||||
if (command.int_value < 3000) {
|
if (command.int_value < 1500) {
|
||||||
command.int_value = 3000;
|
command.int_value = 1500;
|
||||||
}
|
}
|
||||||
if (command.int_value > SRATE) {
|
if (command.int_value > SRATE) {
|
||||||
command.int_value = SRATE;
|
command.int_value = SRATE;
|
||||||
@ -138,13 +122,13 @@ void DemodulatorPreThread::threadMain() {
|
|||||||
if (inp->frequency != params.frequency) {
|
if (inp->frequency != params.frequency) {
|
||||||
if ((params.frequency - inp->frequency) != shift_freq) {
|
if ((params.frequency - inp->frequency) != shift_freq) {
|
||||||
shift_freq = params.frequency - inp->frequency;
|
shift_freq = params.frequency - inp->frequency;
|
||||||
if (abs(shift_freq) <= (int) ((float) (SRATE / 2) * 1.5)) {
|
if (abs(shift_freq) <= (int) ((double) (SRATE / 2) * 1.5)) {
|
||||||
nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((float) abs(shift_freq)) / ((float) SRATE)));
|
nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((double) abs(shift_freq)) / ((double) SRATE)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abs(shift_freq) > (int) ((float) (SRATE / 2) * 1.5)) {
|
if (abs(shift_freq) > (int) ((double) (SRATE / 2) * 1.5)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,11 +53,11 @@ protected:
|
|||||||
AudioThreadInputQueue *audioInputQueue;
|
AudioThreadInputQueue *audioInputQueue;
|
||||||
|
|
||||||
msresamp_crcf resampler;
|
msresamp_crcf resampler;
|
||||||
float resample_ratio;
|
double resample_ratio;
|
||||||
|
|
||||||
msresamp_rrrf audio_resampler;
|
msresamp_rrrf audio_resampler;
|
||||||
msresamp_rrrf stereo_resampler;
|
msresamp_rrrf stereo_resampler;
|
||||||
float audio_resample_ratio;
|
double audio_resample_ratio;
|
||||||
|
|
||||||
DemodulatorThreadParameters params;
|
DemodulatorThreadParameters params;
|
||||||
DemodulatorThreadParameters last_params;
|
DemodulatorThreadParameters last_params;
|
||||||
|
@ -8,12 +8,16 @@
|
|||||||
|
|
||||||
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), squelch_enabled(false) {
|
false), 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);
|
||||||
|
ampdem_lsb = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_LSB, 1);
|
||||||
|
ampdem_usb = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_USB, 1);
|
||||||
|
ampdem = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_DSB, 0);
|
||||||
|
ampdem_active = ampdem;
|
||||||
|
|
||||||
float kf = 0.5; // modulation factor
|
|
||||||
fdem = freqdem_create(kf);
|
|
||||||
// freqdem_print(fdem);
|
|
||||||
}
|
}
|
||||||
DemodulatorThread::~DemodulatorThread() {
|
DemodulatorThread::~DemodulatorThread() {
|
||||||
}
|
}
|
||||||
@ -36,7 +40,7 @@ void DemodulatorThread::threadMain() {
|
|||||||
firfilt_rrrf fir_filter2 = NULL;
|
firfilt_rrrf fir_filter2 = NULL;
|
||||||
msresamp_crcf resampler = NULL;
|
msresamp_crcf resampler = NULL;
|
||||||
|
|
||||||
float fc = 0.5 * ((double) 36000 / (double) AUDIO_FREQUENCY); // filter cutoff frequency
|
double fc = 0.5 * ((double) 36000 / (double) AUDIO_FREQUENCY); // filter cutoff frequency
|
||||||
if (fc <= 0) {
|
if (fc <= 0) {
|
||||||
fc = 0;
|
fc = 0;
|
||||||
}
|
}
|
||||||
@ -60,24 +64,29 @@ void DemodulatorThread::threadMain() {
|
|||||||
firhilbf firR2C = firhilbf_create(m, slsl);
|
firhilbf firR2C = firhilbf_create(m, slsl);
|
||||||
firhilbf firC2R = firhilbf_create(m, slsl);
|
firhilbf firC2R = firhilbf_create(m, slsl);
|
||||||
|
|
||||||
nco_crcf nco_shift = nco_crcf_create(LIQUID_NCO);
|
nco_crcf nco_stereo_shift = nco_crcf_create(LIQUID_NCO);
|
||||||
float shift_freq = 0;
|
double nco_stereo_shift_freq = 0;
|
||||||
|
|
||||||
|
nco_crcf nco_ssb_shift_up = nco_crcf_create(LIQUID_NCO);
|
||||||
|
nco_crcf_set_frequency(nco_ssb_shift_up, (2.0 * M_PI) * 0.25);
|
||||||
|
|
||||||
|
|
||||||
|
nco_crcf nco_ssb_shift_down = nco_crcf_create(LIQUID_NCO);
|
||||||
|
nco_crcf_set_frequency(nco_ssb_shift_down, (2.0 * M_PI) * 0.25);
|
||||||
|
|
||||||
|
// estimate required filter length and generate filter
|
||||||
|
h_len = estimate_req_filter_len(ft,100.0);
|
||||||
|
float h2[h_len];
|
||||||
|
liquid_firdes_kaiser(h_len,0.25,As,0.0,h2);
|
||||||
|
|
||||||
|
firfilt_crcf ssb_fir_filter = firfilt_crcf_create(h2, h_len);
|
||||||
|
|
||||||
|
|
||||||
agc = agc_crcf_create();
|
agc = agc_crcf_create();
|
||||||
agc_crcf_set_bandwidth(agc, 1e-3f);
|
agc_crcf_set_bandwidth(agc, 0.9);
|
||||||
|
|
||||||
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) {
|
||||||
@ -103,25 +112,30 @@ 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((float) (bufSize) * inp->resample_ratio);
|
int out_size = ceil((double) (bufSize) * inp->resample_ratio) + 512;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
float audio_resample_ratio = inp->audio_resample_ratio;
|
|
||||||
|
|
||||||
if (demod_output.size() != num_written) {
|
if (demod_output.size() != num_written) {
|
||||||
if (demod_output.capacity() < num_written) {
|
if (demod_output.capacity() < num_written) {
|
||||||
@ -130,9 +144,64 @@ void DemodulatorThread::threadMain() {
|
|||||||
demod_output.resize(num_written);
|
demod_output.resize(num_written);
|
||||||
}
|
}
|
||||||
|
|
||||||
int audio_out_size = ceil((float) (num_written) * audio_resample_ratio);
|
int audio_out_size = ceil((double) (num_written) * audio_resample_ratio) + 512;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (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]);
|
||||||
|
} else {
|
||||||
|
float p;
|
||||||
|
switch (demodulatorType) {
|
||||||
|
case DEMOD_TYPE_LSB:
|
||||||
|
for (int i = 0; i < num_written; i++) { // Reject upper band
|
||||||
|
nco_crcf_mix_up(nco_ssb_shift_up, resampled_data[i], &x);
|
||||||
|
nco_crcf_step(nco_ssb_shift_up);
|
||||||
|
firfilt_crcf_push(ssb_fir_filter, x);
|
||||||
|
firfilt_crcf_execute(ssb_fir_filter, &x);
|
||||||
|
nco_crcf_mix_down(nco_ssb_shift_down, x, &resampled_data[i]);
|
||||||
|
nco_crcf_step(nco_ssb_shift_down);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DEMOD_TYPE_USB:
|
||||||
|
for (int i = 0; i < num_written; i++) { // Reject lower band
|
||||||
|
nco_crcf_mix_down(nco_ssb_shift_down, resampled_data[i], &x);
|
||||||
|
nco_crcf_step(nco_ssb_shift_down);
|
||||||
|
firfilt_crcf_push(ssb_fir_filter, x);
|
||||||
|
firfilt_crcf_execute(ssb_fir_filter, &x);
|
||||||
|
nco_crcf_mix_up(nco_ssb_shift_up, x, &resampled_data[i]);
|
||||||
|
nco_crcf_step(nco_ssb_shift_up);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DEMOD_TYPE_AM:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
am_max = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_written; i++) {
|
||||||
|
ampmodem_demodulate(ampdem_active, 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.05;
|
||||||
|
am_max_maa = am_max_maa + (am_max_ma - am_max_maa) * 0.05;
|
||||||
|
|
||||||
|
float gain = 0.95 / am_max_maa;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_written; i++) {
|
||||||
|
demod_output[i] *= gain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -152,17 +221,17 @@ void DemodulatorThread::threadMain() {
|
|||||||
demod_output_stereo.resize(num_written);
|
demod_output_stereo.resize(num_written);
|
||||||
}
|
}
|
||||||
|
|
||||||
double freq = (2.0 * M_PI) * (((float) abs(38000)) / ((float) inp->bandwidth));
|
double freq = (2.0 * M_PI) * (((double) abs(38000)) / ((double) inp->bandwidth));
|
||||||
|
|
||||||
if (shift_freq != freq) {
|
if (nco_stereo_shift_freq != freq) {
|
||||||
nco_crcf_set_frequency(nco_shift, freq);
|
nco_crcf_set_frequency(nco_stereo_shift, freq);
|
||||||
shift_freq = freq;
|
nco_stereo_shift_freq = freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < num_written; i++) {
|
for (int i = 0; i < num_written; i++) {
|
||||||
firhilbf_r2c_execute(firR2C, demod_output[i], &x);
|
firhilbf_r2c_execute(firR2C, demod_output[i], &x);
|
||||||
nco_crcf_mix_down(nco_shift, x, &y);
|
nco_crcf_mix_down(nco_stereo_shift, x, &y);
|
||||||
nco_crcf_step(nco_shift);
|
nco_crcf_step(nco_stereo_shift);
|
||||||
firhilbf_c2r_execute(firC2R, y, &demod_output_stereo[i]);
|
firhilbf_c2r_execute(firC2R, y, &demod_output_stereo[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,10 +245,16 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current_level > signal_level) {
|
||||||
|
signal_level = signal_level + (current_level - signal_level) * 0.5;
|
||||||
|
} else {
|
||||||
|
signal_level = signal_level + (current_level - signal_level) * 0.05;
|
||||||
|
}
|
||||||
|
|
||||||
AudioThreadInput *ati = NULL;
|
AudioThreadInput *ati = NULL;
|
||||||
|
|
||||||
if (audioInputQueue != NULL) {
|
if (audioInputQueue != NULL) {
|
||||||
if (!squelch_enabled || ((agc_crcf_get_signal_level(agc)) >= 0.1)) {
|
if (!squelch_enabled || (signal_level >= squelch_level)) {
|
||||||
|
|
||||||
for (buffers_i = buffers.begin(); buffers_i != buffers.end(); buffers_i++) {
|
for (buffers_i = buffers.begin(); buffers_i != buffers.end(); buffers_i++) {
|
||||||
if ((*buffers_i)->getRefCount() <= 0) {
|
if ((*buffers_i)->getRefCount() <= 0) {
|
||||||
@ -259,6 +334,8 @@ void DemodulatorThread::threadMain() {
|
|||||||
visOutQueue->push(ati_vis);
|
visOutQueue->push(ati_vis);
|
||||||
}
|
}
|
||||||
if (!threadQueueControl->empty()) {
|
if (!threadQueueControl->empty()) {
|
||||||
|
int newDemodType = DEMOD_TYPE_NULL;
|
||||||
|
|
||||||
while (!threadQueueControl->empty()) {
|
while (!threadQueueControl->empty()) {
|
||||||
DemodulatorThreadControlCommand command;
|
DemodulatorThreadControlCommand command;
|
||||||
threadQueueControl->pop(command);
|
threadQueueControl->pop(command);
|
||||||
@ -274,10 +351,30 @@ void DemodulatorThread::threadMain() {
|
|||||||
squelch_tolerance = 1;
|
squelch_tolerance = 1;
|
||||||
squelch_enabled = false;
|
squelch_enabled = false;
|
||||||
break;
|
break;
|
||||||
|
case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_TYPE:
|
||||||
|
newDemodType = command.demodType;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newDemodType != DEMOD_TYPE_NULL) {
|
||||||
|
switch (newDemodType) {
|
||||||
|
case DEMOD_TYPE_FM:
|
||||||
|
break;
|
||||||
|
case DEMOD_TYPE_LSB:
|
||||||
|
ampdem_active = ampdem_lsb;
|
||||||
|
break;
|
||||||
|
case DEMOD_TYPE_USB:
|
||||||
|
ampdem_active = ampdem_usb;
|
||||||
|
break;
|
||||||
|
case DEMOD_TYPE_AM:
|
||||||
|
ampdem_active = ampdem;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
demodulatorType = newDemodType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inp->decRefCount();
|
inp->decRefCount();
|
||||||
@ -302,7 +399,11 @@ void DemodulatorThread::threadMain() {
|
|||||||
agc_crcf_destroy(agc);
|
agc_crcf_destroy(agc);
|
||||||
firhilbf_destroy(firR2C);
|
firhilbf_destroy(firR2C);
|
||||||
firhilbf_destroy(firC2R);
|
firhilbf_destroy(firC2R);
|
||||||
nco_crcf_destroy(nco_shift);
|
// firhilbf_destroy(firR2Cssb);
|
||||||
|
// firhilbf_destroy(firC2Rssb);
|
||||||
|
nco_crcf_destroy(nco_stereo_shift);
|
||||||
|
nco_crcf_destroy(nco_ssb_shift_up);
|
||||||
|
nco_crcf_destroy(nco_ssb_shift_down);
|
||||||
|
|
||||||
while (!buffers.empty()) {
|
while (!buffers.empty()) {
|
||||||
AudioThreadInput *audioDataDel = buffers.front();
|
AudioThreadInput *audioDataDel = buffers.front();
|
||||||
@ -330,3 +431,27 @@ void DemodulatorThread::setStereo(bool state) {
|
|||||||
bool DemodulatorThread::isStereo() {
|
bool DemodulatorThread::isStereo() {
|
||||||
return stereo;
|
return stereo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float DemodulatorThread::getSignalLevel() {
|
||||||
|
return signal_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DemodulatorThread::setSquelchLevel(float signal_level_in) {
|
||||||
|
if (!squelch_enabled) {
|
||||||
|
squelch_enabled = true;
|
||||||
|
}
|
||||||
|
squelch_level = signal_level_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
float DemodulatorThread::getSquelchLevel() {
|
||||||
|
return squelch_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DemodulatorThread::setDemodulatorType(int demod_type_in) {
|
||||||
|
demodulatorType = demod_type_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DemodulatorThread::getDemodulatorType() {
|
||||||
|
return demodulatorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
typedef ThreadQueue<AudioThreadInput *> DemodulatorThreadOutputQueue;
|
typedef ThreadQueue<AudioThreadInput *> DemodulatorThreadOutputQueue;
|
||||||
|
|
||||||
#define DEMOD_VIS_SIZE 2048
|
#define DEMOD_VIS_SIZE 1024
|
||||||
|
|
||||||
class DemodulatorThread {
|
class DemodulatorThread {
|
||||||
public:
|
public:
|
||||||
@ -32,12 +32,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
void setStereo(bool state);
|
void setStereo(bool state);
|
||||||
bool isStereo();
|
bool isStereo();
|
||||||
|
|
||||||
|
float getSignalLevel();
|
||||||
|
void setSquelchLevel(float signal_level_in);
|
||||||
|
float getSquelchLevel();
|
||||||
|
|
||||||
|
void setDemodulatorType(int demod_type_in);
|
||||||
|
int getDemodulatorType();
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
static void *pthread_helper(void *context) {
|
static void *pthread_helper(void *context) {
|
||||||
return ((DemodulatorThread *) context)->threadMain();
|
return ((DemodulatorThread *) context)->threadMain();
|
||||||
@ -45,18 +51,41 @@ 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_active;
|
||||||
|
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<int> demodulatorType;
|
||||||
|
|
||||||
DemodulatorThreadCommandQueue* threadQueueNotify;
|
DemodulatorThreadCommandQueue* threadQueueNotify;
|
||||||
DemodulatorThreadControlCommandQueue *threadQueueControl;
|
DemodulatorThreadControlCommandQueue *threadQueueControl;
|
||||||
float squelch_level;
|
std::atomic<float> squelch_level;
|
||||||
float squelch_tolerance;bool squelch_enabled;
|
float squelch_tolerance;
|
||||||
|
std::atomic<float> signal_level;
|
||||||
|
bool squelch_enabled;
|
||||||
};
|
};
|
||||||
|
@ -34,8 +34,8 @@ void DemodulatorWorkerThread::threadMain() {
|
|||||||
if (filterChanged) {
|
if (filterChanged) {
|
||||||
DemodulatorWorkerThreadResult result(DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS);
|
DemodulatorWorkerThreadResult result(DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS);
|
||||||
|
|
||||||
result.resample_ratio = (float) (filterCommand.bandwidth) / (float) filterCommand.inputRate;
|
result.resample_ratio = (double) (filterCommand.bandwidth) / (double) filterCommand.inputRate;
|
||||||
result.audio_resample_ratio = (float) (filterCommand.audioSampleRate) / (float) filterCommand.bandwidth;
|
result.audio_resample_ratio = (double) (filterCommand.audioSampleRate) / (double) filterCommand.bandwidth;
|
||||||
|
|
||||||
float As = 60.0f; // stop-band attenuation [dB]
|
float As = 60.0f; // stop-band attenuation [dB]
|
||||||
|
|
||||||
@ -46,7 +46,6 @@ void DemodulatorWorkerThread::threadMain() {
|
|||||||
result.audioSampleRate = filterCommand.audioSampleRate;
|
result.audioSampleRate = filterCommand.audioSampleRate;
|
||||||
result.bandwidth = filterCommand.bandwidth;
|
result.bandwidth = filterCommand.bandwidth;
|
||||||
result.inputRate = filterCommand.inputRate;
|
result.inputRate = filterCommand.inputRate;
|
||||||
|
|
||||||
resultQueue->push(result);
|
resultQueue->push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,10 +36,10 @@ public:
|
|||||||
DemodulatorThreadResultEnum cmd;
|
DemodulatorThreadResultEnum cmd;
|
||||||
|
|
||||||
msresamp_crcf resampler;
|
msresamp_crcf resampler;
|
||||||
float resample_ratio;
|
double resample_ratio;
|
||||||
msresamp_rrrf audio_resampler;
|
msresamp_rrrf audio_resampler;
|
||||||
msresamp_rrrf stereo_resampler;
|
msresamp_rrrf stereo_resampler;
|
||||||
float audio_resample_ratio;
|
double audio_resample_ratio;
|
||||||
|
|
||||||
unsigned int inputRate;
|
unsigned int inputRate;
|
||||||
unsigned int bandwidth;
|
unsigned int bandwidth;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
SDRPostThread::SDRPostThread() :
|
SDRPostThread::SDRPostThread() :
|
||||||
sample_rate(SRATE), iqDataOutQueue(NULL), iqDataInQueue(NULL), iqVisualQueue(NULL), terminated(false), dcFilter(NULL) {
|
sample_rate(SRATE), iqDataOutQueue(NULL), iqDataInQueue(NULL), iqVisualQueue(NULL), terminated(false), dcFilter(NULL), num_vis_samples(2048) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDRPostThread::~SDRPostThread() {
|
SDRPostThread::~SDRPostThread() {
|
||||||
@ -34,6 +34,14 @@ void SDRPostThread::setIQVisualQueue(DemodulatorThreadInputQueue *iqVisQueue) {
|
|||||||
iqVisualQueue = iqVisQueue;
|
iqVisualQueue = iqVisQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SDRPostThread::setNumVisSamples(int num_vis_samples_in) {
|
||||||
|
num_vis_samples = num_vis_samples_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SDRPostThread::getNumVisSamples() {
|
||||||
|
return num_vis_samples;
|
||||||
|
}
|
||||||
|
|
||||||
void SDRPostThread::threadMain() {
|
void SDRPostThread::threadMain() {
|
||||||
int n_read;
|
int n_read;
|
||||||
double seconds = 0.0;
|
double seconds = 0.0;
|
||||||
@ -89,7 +97,9 @@ void SDRPostThread::threadMain() {
|
|||||||
|
|
||||||
if (iqVisualQueue != NULL && iqVisualQueue.load()->empty()) {
|
if (iqVisualQueue != NULL && iqVisualQueue.load()->empty()) {
|
||||||
DemodulatorThreadIQData *visualDataOut = new DemodulatorThreadIQData;
|
DemodulatorThreadIQData *visualDataOut = new DemodulatorThreadIQData;
|
||||||
visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + FFT_SIZE);
|
visualDataOut->frequency = data_in->frequency;
|
||||||
|
visualDataOut->bandwidth = data_in->bandwidth;
|
||||||
|
visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + num_vis_samples);
|
||||||
iqVisualQueue.load()->push(visualDataOut);
|
iqVisualQueue.load()->push(visualDataOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +132,7 @@ void SDRPostThread::threadMain() {
|
|||||||
DemodulatorInstance *demod = *i;
|
DemodulatorInstance *demod = *i;
|
||||||
|
|
||||||
if (demod->getParams().frequency != data_in->frequency
|
if (demod->getParams().frequency != data_in->frequency
|
||||||
&& abs(data_in->frequency - demod->getParams().frequency) > (int) ((float) ((float) SRATE / 2.0))) {
|
&& abs(data_in->frequency - demod->getParams().frequency) > (int) ((double) ((double) SRATE / 2.0))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
activeDemods++;
|
activeDemods++;
|
||||||
@ -156,7 +166,7 @@ void SDRPostThread::threadMain() {
|
|||||||
DemodulatorThreadInputQueue *demodQueue = demod->threadQueueDemod;
|
DemodulatorThreadInputQueue *demodQueue = demod->threadQueueDemod;
|
||||||
|
|
||||||
if (demod->getParams().frequency != data_in->frequency
|
if (demod->getParams().frequency != data_in->frequency
|
||||||
&& abs(data_in->frequency - demod->getParams().frequency) > (int) ((float) ((float) SRATE / 2.0))) {
|
&& abs(data_in->frequency - demod->getParams().frequency) > (int) ((double) ((double) SRATE / 2.0))) {
|
||||||
if (demod->isActive()) {
|
if (demod->isActive()) {
|
||||||
demod->setActive(false);
|
demod->setActive(false);
|
||||||
DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData;
|
DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData;
|
||||||
|
@ -15,6 +15,9 @@ public:
|
|||||||
void setIQDataOutQueue(DemodulatorThreadInputQueue* iqDataQueue);
|
void setIQDataOutQueue(DemodulatorThreadInputQueue* iqDataQueue);
|
||||||
void setIQVisualQueue(DemodulatorThreadInputQueue* iqVisQueue);
|
void setIQVisualQueue(DemodulatorThreadInputQueue* iqVisQueue);
|
||||||
|
|
||||||
|
void setNumVisSamples(int num_vis_samples_in);
|
||||||
|
int getNumVisSamples();
|
||||||
|
|
||||||
void threadMain();
|
void threadMain();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
@ -30,4 +33,5 @@ protected:
|
|||||||
std::vector<DemodulatorInstance *> demodulators_remove;
|
std::vector<DemodulatorInstance *> demodulators_remove;
|
||||||
std::atomic<bool> terminated;
|
std::atomic<bool> terminated;
|
||||||
iirfilt_crcf dcFilter;
|
iirfilt_crcf dcFilter;
|
||||||
|
int num_vis_samples;
|
||||||
};
|
};
|
||||||
|
@ -382,7 +382,7 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A
|
|||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(xpos, ypos, 0.0f);
|
glTranslatef(xpos, ypos, 0.0f);
|
||||||
|
|
||||||
switch (hAlign) {
|
switch (vAlign) {
|
||||||
case GLFONT_ALIGN_TOP:
|
case GLFONT_ALIGN_TOP:
|
||||||
glTranslatef(0.0, -size, 0.0);
|
glTranslatef(0.0, -size, 0.0);
|
||||||
break;
|
break;
|
||||||
@ -393,7 +393,7 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (vAlign) {
|
switch (hAlign) {
|
||||||
case GLFONT_ALIGN_RIGHT:
|
case GLFONT_ALIGN_RIGHT:
|
||||||
glTranslatef(-msgWidth, 0.0, 0.0);
|
glTranslatef(-msgWidth, 0.0, 0.0);
|
||||||
break;
|
break;
|
||||||
@ -445,5 +445,6 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A
|
|||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ void MouseTracker::OnMouseMoved(wxMouseEvent& event) {
|
|||||||
const wxSize ClientSize = target->GetClientSize();
|
const wxSize ClientSize = target->GetClientSize();
|
||||||
|
|
||||||
mouseX = (float) event.m_x / (float) ClientSize.x;
|
mouseX = (float) event.m_x / (float) ClientSize.x;
|
||||||
mouseY = (float) event.m_y / (float) ClientSize.y;
|
mouseY = 1.0 - (float) event.m_y / (float) ClientSize.y;
|
||||||
|
|
||||||
deltaMouseX = mouseX - lastMouseX;
|
deltaMouseX = mouseX - lastMouseX;
|
||||||
deltaMouseY = mouseY - lastMouseY;
|
deltaMouseY = mouseY - lastMouseY;
|
||||||
@ -17,11 +17,11 @@ void MouseTracker::OnMouseMoved(wxMouseEvent& event) {
|
|||||||
if (isMouseDown) {
|
if (isMouseDown) {
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
if (horizDragLock && vertDragLock) {
|
if (horizDragLock && vertDragLock) {
|
||||||
target->WarpPointer(originMouseX * ClientSize.x, originMouseY * ClientSize.y);
|
target->WarpPointer(originMouseX * ClientSize.x, (1.0-originMouseY) * ClientSize.y);
|
||||||
mouseX = originMouseX;
|
mouseX = originMouseX;
|
||||||
mouseY = originMouseY;
|
mouseY = originMouseY;
|
||||||
} else if (vertDragLock && mouseY != lastMouseY) {
|
} else if (vertDragLock && mouseY != lastMouseY) {
|
||||||
target->WarpPointer(event.m_x, originMouseY * ClientSize.y);
|
target->WarpPointer(event.m_x, (1.0-originMouseY) * ClientSize.y);
|
||||||
mouseY = originMouseY;
|
mouseY = originMouseY;
|
||||||
} else if (horizDragLock && mouseX != lastMouseX) {
|
} else if (horizDragLock && mouseX != lastMouseX) {
|
||||||
target->WarpPointer(originMouseX * ClientSize.x, event.m_y);
|
target->WarpPointer(originMouseX * ClientSize.x, event.m_y);
|
||||||
@ -42,7 +42,7 @@ void MouseTracker::OnMouseDown(wxMouseEvent& event) {
|
|||||||
const wxSize ClientSize = target->GetClientSize();
|
const wxSize ClientSize = target->GetClientSize();
|
||||||
|
|
||||||
mouseX = lastMouseX = (float) event.m_x / (float) ClientSize.x;
|
mouseX = lastMouseX = (float) event.m_x / (float) ClientSize.x;
|
||||||
mouseY = lastMouseY = (float) event.m_y / (float) ClientSize.y;
|
mouseY = lastMouseY = 1.0 - (float) event.m_y / (float) ClientSize.y;
|
||||||
|
|
||||||
originMouseX = mouseX;
|
originMouseX = mouseX;
|
||||||
originMouseY = mouseY;
|
originMouseY = mouseY;
|
||||||
|
133
src/visual/InteractiveCanvas.cpp
Normal file
133
src/visual/InteractiveCanvas.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include "InteractiveCanvas.h"
|
||||||
|
|
||||||
|
#include "wx/wxprec.h"
|
||||||
|
|
||||||
|
#ifndef WX_PRECOMP
|
||||||
|
#include "wx/wx.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !wxUSE_GLCANVAS
|
||||||
|
#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "CubicSDR.h"
|
||||||
|
#include "CubicSDRDefs.h"
|
||||||
|
#include "AppFrame.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <wx/numformatter.h>
|
||||||
|
|
||||||
|
InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *attribList) :
|
||||||
|
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
||||||
|
wxFULL_REPAINT_ON_RESIZE), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), center_freq(0), bandwidth(0), last_bandwidth(0), isView(
|
||||||
|
false) {
|
||||||
|
mTracker.setTarget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
InteractiveCanvas::~InteractiveCanvas() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::SetView(int center_freq_in, int bandwidth_in) {
|
||||||
|
isView = true;
|
||||||
|
center_freq = center_freq_in;
|
||||||
|
bandwidth = bandwidth_in;
|
||||||
|
last_bandwidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::DisableView() {
|
||||||
|
isView = false;
|
||||||
|
center_freq = wxGetApp().getFrequency();
|
||||||
|
bandwidth = SRATE;
|
||||||
|
last_bandwidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int InteractiveCanvas::GetFrequencyAt(float x) {
|
||||||
|
int iqCenterFreq = GetCenterFrequency();
|
||||||
|
int iqBandwidth = GetBandwidth();
|
||||||
|
int freq = iqCenterFreq - (int) (0.5 * (float) iqBandwidth) + (int) ((float) x * (float) iqBandwidth);
|
||||||
|
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::SetCenterFrequency(unsigned int center_freq_in) {
|
||||||
|
center_freq = center_freq_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int InteractiveCanvas::GetCenterFrequency() {
|
||||||
|
if (isView) {
|
||||||
|
return center_freq;
|
||||||
|
} else {
|
||||||
|
return (unsigned int) wxGetApp().getFrequency();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::SetBandwidth(unsigned int bandwidth_in) {
|
||||||
|
bandwidth = bandwidth_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int InteractiveCanvas::GetBandwidth() {
|
||||||
|
if (isView) {
|
||||||
|
return bandwidth;
|
||||||
|
} else {
|
||||||
|
return SRATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::OnKeyUp(wxKeyEvent& event) {
|
||||||
|
shiftDown = event.ShiftDown();
|
||||||
|
altDown = event.AltDown();
|
||||||
|
ctrlDown = event.ControlDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::OnKeyDown(wxKeyEvent& event) {
|
||||||
|
float angle = 5.0;
|
||||||
|
|
||||||
|
shiftDown = event.ShiftDown();
|
||||||
|
altDown = event.AltDown();
|
||||||
|
ctrlDown = event.ControlDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::mouseMoved(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseMoved(event);
|
||||||
|
|
||||||
|
shiftDown = event.ShiftDown();
|
||||||
|
altDown = event.AltDown();
|
||||||
|
ctrlDown = event.ControlDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::mouseDown(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseDown(event);
|
||||||
|
|
||||||
|
shiftDown = event.ShiftDown();
|
||||||
|
altDown = event.AltDown();
|
||||||
|
ctrlDown = event.ControlDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::mouseWheelMoved(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseWheelMoved(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::mouseReleased(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseReleased(event);
|
||||||
|
|
||||||
|
shiftDown = event.ShiftDown();
|
||||||
|
altDown = event.AltDown();
|
||||||
|
ctrlDown = event.ControlDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::mouseLeftWindow(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseLeftWindow(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::mouseEnterWindow(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseEnterWindow(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::setStatusText(std::string statusText) {
|
||||||
|
((wxFrame*) parent)->GetStatusBar()->SetStatusText(statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveCanvas::setStatusText(std::string statusText, int value) {
|
||||||
|
((wxFrame*) parent)->GetStatusBar()->SetStatusText(
|
||||||
|
wxString::Format(statusText.c_str(), wxNumberFormatter::ToString((long) value, wxNumberFormatter::Style_WithThousandsSep)));
|
||||||
|
}
|
52
src/visual/InteractiveCanvas.h
Normal file
52
src/visual/InteractiveCanvas.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "wx/glcanvas.h"
|
||||||
|
#include "wx/timer.h"
|
||||||
|
|
||||||
|
#include "MouseTracker.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class InteractiveCanvas: public wxGLCanvas {
|
||||||
|
public:
|
||||||
|
InteractiveCanvas(wxWindow *parent, int *attribList = NULL);
|
||||||
|
~InteractiveCanvas();
|
||||||
|
|
||||||
|
int GetFrequencyAt(float x);
|
||||||
|
|
||||||
|
void SetView(int center_freq_in, int bandwidth_in);
|
||||||
|
void DisableView();
|
||||||
|
|
||||||
|
void SetCenterFrequency(unsigned int center_freq_in);
|
||||||
|
unsigned int GetCenterFrequency();
|
||||||
|
|
||||||
|
void SetBandwidth(unsigned int bandwidth_in);
|
||||||
|
unsigned int GetBandwidth();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void OnKeyDown(wxKeyEvent& event);
|
||||||
|
void OnKeyUp(wxKeyEvent& event);
|
||||||
|
|
||||||
|
void mouseMoved(wxMouseEvent& event);
|
||||||
|
void mouseDown(wxMouseEvent& event);
|
||||||
|
void mouseWheelMoved(wxMouseEvent& event);
|
||||||
|
void mouseReleased(wxMouseEvent& event);
|
||||||
|
void mouseEnterWindow(wxMouseEvent& event);
|
||||||
|
void mouseLeftWindow(wxMouseEvent& event);
|
||||||
|
|
||||||
|
void setStatusText(std::string statusText);
|
||||||
|
void setStatusText(std::string statusText, int value);
|
||||||
|
|
||||||
|
wxWindow *parent;
|
||||||
|
MouseTracker mTracker;
|
||||||
|
|
||||||
|
bool shiftDown;
|
||||||
|
bool altDown;
|
||||||
|
bool ctrlDown;
|
||||||
|
|
||||||
|
unsigned int center_freq;
|
||||||
|
unsigned int bandwidth;
|
||||||
|
unsigned int last_bandwidth;
|
||||||
|
|
||||||
|
bool isView;
|
||||||
|
};
|
||||||
|
|
131
src/visual/MeterCanvas.cpp
Normal file
131
src/visual/MeterCanvas.cpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include "MeterCanvas.h"
|
||||||
|
|
||||||
|
#include "wx/wxprec.h"
|
||||||
|
|
||||||
|
#ifndef WX_PRECOMP
|
||||||
|
#include "wx/wx.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !wxUSE_GLCANVAS
|
||||||
|
#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "CubicSDR.h"
|
||||||
|
#include "CubicSDRDefs.h"
|
||||||
|
#include "AppFrame.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
wxBEGIN_EVENT_TABLE(MeterCanvas, wxGLCanvas) EVT_PAINT(MeterCanvas::OnPaint)
|
||||||
|
EVT_IDLE(MeterCanvas::OnIdle)
|
||||||
|
EVT_MOTION(MeterCanvas::mouseMoved)
|
||||||
|
EVT_LEFT_DOWN(MeterCanvas::mouseDown)
|
||||||
|
EVT_LEFT_UP(MeterCanvas::mouseReleased)
|
||||||
|
EVT_LEAVE_WINDOW(MeterCanvas::mouseLeftWindow)
|
||||||
|
EVT_ENTER_WINDOW(MeterCanvas::mouseEnterWindow)
|
||||||
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
|
MeterCanvas::MeterCanvas(wxWindow *parent, int *attribList) :
|
||||||
|
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
||||||
|
wxFULL_REPAINT_ON_RESIZE), parent(parent), level(0), level_max(1), inputValue(0), userInputValue(0), shiftDown(false), altDown(false), ctrlDown(false) {
|
||||||
|
|
||||||
|
glContext = new MeterContext(this, &wxGetApp().GetContext(this));
|
||||||
|
mTracker.setTarget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
MeterCanvas::~MeterCanvas() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::setLevel(float level_in) {
|
||||||
|
level = level_in;
|
||||||
|
}
|
||||||
|
float MeterCanvas::getLevel() {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::setMax(float max_in) {
|
||||||
|
level_max = max_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MeterCanvas::setInputValue(float slider_in) {
|
||||||
|
userInputValue = inputValue = slider_in;
|
||||||
|
}
|
||||||
|
bool MeterCanvas::inputChanged() {
|
||||||
|
return (inputValue != userInputValue);
|
||||||
|
}
|
||||||
|
float MeterCanvas::getInputValue() {
|
||||||
|
inputValue = userInputValue;
|
||||||
|
return userInputValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||||
|
wxPaintDC dc(this);
|
||||||
|
const wxSize ClientSize = GetClientSize();
|
||||||
|
|
||||||
|
glContext->SetCurrent(*this);
|
||||||
|
glViewport(0, 0, ClientSize.x, ClientSize.y);
|
||||||
|
|
||||||
|
glContext->DrawBegin();
|
||||||
|
if (mTracker.mouseInView()) {
|
||||||
|
glContext->Draw(0.4, 0.4, 0.4, 0.5, mTracker.getMouseY());
|
||||||
|
}
|
||||||
|
|
||||||
|
glContext->Draw(0.1, 0.75, 0.1, 0.5, level/level_max);
|
||||||
|
|
||||||
|
glContext->Draw(0.75, 0.1, 0.1, 0.5, userInputValue/level_max);
|
||||||
|
|
||||||
|
glContext->DrawEnd();
|
||||||
|
|
||||||
|
SwapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::OnIdle(wxIdleEvent &event) {
|
||||||
|
Refresh(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::mouseMoved(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseMoved(event);
|
||||||
|
|
||||||
|
shiftDown = event.ShiftDown();
|
||||||
|
altDown = event.AltDown();
|
||||||
|
ctrlDown = event.ControlDown();
|
||||||
|
|
||||||
|
if (mTracker.mouseDown()) {
|
||||||
|
userInputValue = mTracker.getMouseY()*level_max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::mouseDown(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseDown(event);
|
||||||
|
|
||||||
|
shiftDown = event.ShiftDown();
|
||||||
|
altDown = event.AltDown();
|
||||||
|
ctrlDown = event.ControlDown();
|
||||||
|
|
||||||
|
userInputValue = mTracker.getMouseY()*level_max;
|
||||||
|
mTracker.setHorizDragLock(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::mouseWheelMoved(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseWheelMoved(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::mouseReleased(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseReleased(event);
|
||||||
|
|
||||||
|
shiftDown = event.ShiftDown();
|
||||||
|
altDown = event.AltDown();
|
||||||
|
ctrlDown = event.ControlDown();
|
||||||
|
|
||||||
|
userInputValue = mTracker.getMouseY()*level_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::mouseLeftWindow(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseLeftWindow(event);
|
||||||
|
SetCursor(wxCURSOR_CROSS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCanvas::mouseEnterWindow(wxMouseEvent& event) {
|
||||||
|
mTracker.OnMouseEnterWindow(event);
|
||||||
|
SetCursor(wxCURSOR_CROSS);
|
||||||
|
}
|
57
src/visual/MeterCanvas.h
Normal file
57
src/visual/MeterCanvas.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "wx/glcanvas.h"
|
||||||
|
#include "wx/timer.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "MeterContext.h"
|
||||||
|
#include "MouseTracker.h"
|
||||||
|
|
||||||
|
#include "fftw3.h"
|
||||||
|
#include "Timer.h"
|
||||||
|
|
||||||
|
class MeterCanvas: public wxGLCanvas {
|
||||||
|
public:
|
||||||
|
std::vector<float> waveform_points;
|
||||||
|
|
||||||
|
MeterCanvas(wxWindow *parent, int *attribList = NULL);
|
||||||
|
~MeterCanvas();
|
||||||
|
|
||||||
|
void setLevel(float level_in);
|
||||||
|
float getLevel();
|
||||||
|
|
||||||
|
void setMax(float max_in);
|
||||||
|
|
||||||
|
bool setInputValue(float slider_in);
|
||||||
|
bool inputChanged();
|
||||||
|
float getInputValue();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OnPaint(wxPaintEvent& event);
|
||||||
|
void OnIdle(wxIdleEvent &event);
|
||||||
|
|
||||||
|
void mouseMoved(wxMouseEvent& event);
|
||||||
|
void mouseDown(wxMouseEvent& event);
|
||||||
|
void mouseWheelMoved(wxMouseEvent& event);
|
||||||
|
void mouseReleased(wxMouseEvent& event);
|
||||||
|
void mouseEnterWindow(wxMouseEvent& event);
|
||||||
|
void mouseLeftWindow(wxMouseEvent& event);
|
||||||
|
|
||||||
|
MouseTracker mTracker;
|
||||||
|
wxWindow *parent;
|
||||||
|
MeterContext *glContext;
|
||||||
|
|
||||||
|
float level;
|
||||||
|
float level_max;
|
||||||
|
|
||||||
|
float inputValue;
|
||||||
|
float userInputValue;
|
||||||
|
|
||||||
|
bool shiftDown;
|
||||||
|
bool altDown;
|
||||||
|
bool ctrlDown;
|
||||||
|
wxDECLARE_EVENT_TABLE();
|
||||||
|
};
|
||||||
|
|
40
src/visual/MeterContext.cpp
Normal file
40
src/visual/MeterContext.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include "MeterContext.h"
|
||||||
|
#include "MeterCanvas.h"
|
||||||
|
|
||||||
|
MeterContext::MeterContext(MeterCanvas *canvas, wxGLContext *sharedContext) :
|
||||||
|
PrimaryGLContext(canvas, sharedContext) {
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterContext::DrawBegin() {
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterContext::Draw(float r, float g, float b, float a, float level) {
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_ONE, GL_ONE);
|
||||||
|
glColor4f(r, g, b, a);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glVertex2f(1.0, -1.0 + 2.0 * level);
|
||||||
|
glVertex2f(-1.0, -1.0 + 2.0 * level);
|
||||||
|
glVertex2f(-1.0, -1.0);
|
||||||
|
glVertex2f(1.0, -1.0);
|
||||||
|
glEnd();
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterContext::DrawEnd() {
|
||||||
|
glFlush();
|
||||||
|
|
||||||
|
CheckGLError();
|
||||||
|
}
|
||||||
|
|
19
src/visual/MeterContext.h
Normal file
19
src/visual/MeterContext.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "PrimaryGLContext.h"
|
||||||
|
#include "Gradient.h"
|
||||||
|
|
||||||
|
#define NUM_WATERFALL_LINES 512
|
||||||
|
|
||||||
|
class MeterCanvas;
|
||||||
|
|
||||||
|
class MeterContext: public PrimaryGLContext {
|
||||||
|
public:
|
||||||
|
MeterContext(MeterCanvas *canvas, wxGLContext *sharedContext);
|
||||||
|
|
||||||
|
void DrawBegin();
|
||||||
|
void Draw(float r, float g, float b, float a, float level);
|
||||||
|
void DrawEnd();
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
@ -92,7 +92,7 @@ GLFont &PrimaryGLContext::getFont(GLFontSize esize) {
|
|||||||
return fonts[esize];
|
return fonts[esize];
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float g, float b) {
|
void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float g, float b, int center_freq, int srate) {
|
||||||
if (!demod) {
|
if (!demod) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -103,7 +103,11 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float
|
|||||||
float viewHeight = (float) vp[3];
|
float viewHeight = (float) vp[3];
|
||||||
float viewWidth = (float) vp[2];
|
float viewWidth = (float) vp[2];
|
||||||
|
|
||||||
float uxPos = (float) (demod->getParams().frequency - (wxGetApp().getFrequency() - SRATE / 2)) / (float) SRATE;
|
if (center_freq == -1) {
|
||||||
|
center_freq = wxGetApp().getFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
float uxPos = (float) (demod->getParams().frequency - (center_freq - srate / 2)) / (float) srate;
|
||||||
uxPos = (uxPos - 0.5) * 2.0;
|
uxPos = (uxPos - 0.5) * 2.0;
|
||||||
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
@ -112,7 +116,7 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float
|
|||||||
glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
|
glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
|
||||||
glColor4f(r, g, b, 0.6);
|
glColor4f(r, g, b, 0.6);
|
||||||
|
|
||||||
float ofs = ((float) demod->getParams().bandwidth) / (float) SRATE;
|
float ofs = ((float) demod->getParams().bandwidth) / (float) srate;
|
||||||
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
|
glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
|
||||||
glColor4f(r, g, b, 0.2);
|
glColor4f(r, g, b, 0.2);
|
||||||
@ -149,12 +153,16 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, float b) {
|
void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, float b, int center_freq, int srate) {
|
||||||
if (!demod) {
|
if (!demod) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float uxPos = (float) (demod->getParams().frequency - (wxGetApp().getFrequency() - SRATE / 2)) / (float) SRATE;
|
if (center_freq == -1) {
|
||||||
|
center_freq = wxGetApp().getFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
float uxPos = (float) (demod->getParams().frequency - (center_freq - srate / 2)) / (float) srate;
|
||||||
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
@ -167,7 +175,7 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, f
|
|||||||
glVertex3f((uxPos - 0.5) * 2.0, 1.0, 0.0);
|
glVertex3f((uxPos - 0.5) * 2.0, 1.0, 0.0);
|
||||||
glVertex3f((uxPos - 0.5) * 2.0, -1.0, 0.0);
|
glVertex3f((uxPos - 0.5) * 2.0, -1.0, 0.0);
|
||||||
|
|
||||||
float ofs = ((float) demod->getParams().bandwidth) / (float) SRATE;
|
float ofs = ((float) demod->getParams().bandwidth) / (float) srate;
|
||||||
|
|
||||||
glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0);
|
glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0);
|
||||||
glVertex3f((uxPos - 0.5) * 2.0 - ofs, -1.0, 0.0);
|
glVertex3f((uxPos - 0.5) * 2.0 - ofs, -1.0, 0.0);
|
||||||
@ -191,7 +199,7 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, f
|
|||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b, float w) {
|
void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b, float w, int center_freq, int srate) {
|
||||||
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||||
|
|
||||||
int bw = 0;
|
int bw = 0;
|
||||||
@ -218,7 +226,7 @@ void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b,
|
|||||||
if (w) {
|
if (w) {
|
||||||
ofs = w;
|
ofs = w;
|
||||||
} else {
|
} else {
|
||||||
ofs = ((float) bw) / (float) SRATE;
|
ofs = ((float) bw) / (float) srate;
|
||||||
}
|
}
|
||||||
|
|
||||||
glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0);
|
glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0);
|
||||||
|
@ -23,9 +23,9 @@ public:
|
|||||||
void BeginDraw();
|
void BeginDraw();
|
||||||
void EndDraw();
|
void EndDraw();
|
||||||
|
|
||||||
void DrawFreqSelector(float uxPos, float r = 1, float g = 1, float b = 1, float w = 0);
|
void DrawFreqSelector(float uxPos, float r = 1, float g = 1, float b = 1, float w = 0, int center_freq = -1, int srate = SRATE);
|
||||||
void DrawDemod(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1);
|
void DrawDemod(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1, int center_freq = -1, int srate = SRATE);
|
||||||
void DrawDemodInfo(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1);
|
void DrawDemodInfo(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1, int center_freq = -1, int srate = SRATE);
|
||||||
|
|
||||||
static GLFont &getFont(GLFontSize esize);
|
static GLFont &getFont(GLFontSize esize);
|
||||||
|
|
||||||
|
@ -21,10 +21,9 @@ wxEND_EVENT_TABLE()
|
|||||||
|
|
||||||
ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) :
|
ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) :
|
||||||
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
||||||
wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), divider(false) {
|
wxFULL_REPAINT_ON_RESIZE), parent(parent), stereo(false) {
|
||||||
|
|
||||||
glContext = new ScopeContext(this, &wxGetApp().GetContext(this));
|
glContext = new ScopeContext(this, &wxGetApp().GetContext(this));
|
||||||
timer.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopeCanvas::~ScopeCanvas() {
|
ScopeCanvas::~ScopeCanvas() {
|
||||||
@ -35,8 +34,13 @@ void ScopeCanvas::setWaveformPoints(std::vector<float> &waveform_points_in) {
|
|||||||
waveform_points = waveform_points_in;
|
waveform_points = waveform_points_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopeCanvas::setDivider(bool state) {
|
void ScopeCanvas::setStereo(bool state) {
|
||||||
divider = state;
|
stereo = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeCanvas::setDeviceName(std::string device_name) {
|
||||||
|
deviceName = device_name;
|
||||||
|
deviceName.append(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||||
@ -47,20 +51,16 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
glViewport(0, 0, ClientSize.x, ClientSize.y);
|
glViewport(0, 0, ClientSize.x, ClientSize.y);
|
||||||
|
|
||||||
glContext->DrawBegin();
|
glContext->DrawBegin();
|
||||||
glContext->Plot(waveform_points);
|
if (!deviceName.empty()) {
|
||||||
if (divider) {
|
glContext->DrawDeviceName(deviceName);
|
||||||
glContext->DrawDivider();
|
|
||||||
}
|
}
|
||||||
|
glContext->Plot(waveform_points, stereo);
|
||||||
glContext->DrawEnd();
|
glContext->DrawEnd();
|
||||||
|
|
||||||
|
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopeCanvas::OnIdle(wxIdleEvent &event) {
|
void ScopeCanvas::OnIdle(wxIdleEvent &event) {
|
||||||
// timer.update();
|
|
||||||
// frameTimer += timer.lastUpdateSeconds();
|
|
||||||
// if (frameTimer > 1.0/30.0) {
|
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
// frameTimer = 0;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,7 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include "ScopeContext.h"
|
#include "ScopeContext.h"
|
||||||
|
|
||||||
#include "fftw3.h"
|
#include "fftw3.h"
|
||||||
#include "Timer.h"
|
|
||||||
|
|
||||||
class ScopeCanvas: public wxGLCanvas {
|
class ScopeCanvas: public wxGLCanvas {
|
||||||
public:
|
public:
|
||||||
@ -19,7 +17,8 @@ public:
|
|||||||
~ScopeCanvas();
|
~ScopeCanvas();
|
||||||
|
|
||||||
void setWaveformPoints(std::vector<float> &waveform_points_in);
|
void setWaveformPoints(std::vector<float> &waveform_points_in);
|
||||||
void setDivider(bool state);
|
void setStereo(bool state);
|
||||||
|
void setDeviceName(std::string device_name);
|
||||||
private:
|
private:
|
||||||
void OnPaint(wxPaintEvent& event);
|
void OnPaint(wxPaintEvent& event);
|
||||||
|
|
||||||
@ -28,9 +27,8 @@ private:
|
|||||||
wxWindow *parent;
|
wxWindow *parent;
|
||||||
|
|
||||||
ScopeContext *glContext;
|
ScopeContext *glContext;
|
||||||
Timer timer;
|
std::string deviceName;
|
||||||
float frameTimer;
|
bool stereo;
|
||||||
bool divider;
|
|
||||||
// event table
|
// event table
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
@ -20,19 +20,68 @@ void ScopeContext::DrawBegin() {
|
|||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopeContext::Plot(std::vector<float> &points) {
|
void ScopeContext::Plot(std::vector<float> &points, bool stereo) {
|
||||||
glColor3f(1.0, 1.0, 1.0);
|
glColor3f(1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
if (stereo) {
|
||||||
|
glColor3f(0.7, 0.7, 0.7);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex2f(-1.0, 0.0);
|
||||||
|
glVertex2f(1.0, 0.0);
|
||||||
|
glEnd();
|
||||||
|
glColor3f(0.3, 0.3, 0.3);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex2f(-1.0, 0.5);
|
||||||
|
glVertex2f(1.0, 0.5);
|
||||||
|
glVertex2f(-1.0, -0.5);
|
||||||
|
glVertex2f(1.0, -0.5);
|
||||||
|
glEnd();
|
||||||
|
} else {
|
||||||
|
glColor3f(0.3, 0.3, 0.3);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex2f(-1.0, 0.0);
|
||||||
|
glVertex2f(1.0, 0.0);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
glColor3f(0.9, 0.9, 0.9);
|
||||||
if (points.size()) {
|
if (points.size()) {
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glVertexPointer(2, GL_FLOAT, 0, &points[0]);
|
||||||
|
if (stereo) {
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(-1.0f, 0.5f, 0.0f);
|
||||||
|
glScalef(4.0f, 0.92f, 1.0f);
|
||||||
|
glDrawArrays(GL_LINE_STRIP, 0, points.size() / 4);
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(-3.0f, -0.5f, 0.0f);
|
||||||
|
glPushMatrix();
|
||||||
|
glScalef(4.0f, 0.92f, 1.0f);
|
||||||
|
glDrawArrays(GL_LINE_STRIP, points.size() / 4, points.size() / 4);
|
||||||
|
glPopMatrix();
|
||||||
|
glPopMatrix();
|
||||||
|
} else {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(-1.0f, 0.0f, 0.0f);
|
glTranslatef(-1.0f, 0.0f, 0.0f);
|
||||||
glScalef(2.0f, 2.0f, 1.0f);
|
glScalef(2.0f, 2.0f, 1.0f);
|
||||||
glEnableClientState (GL_VERTEX_ARRAY);
|
|
||||||
glVertexPointer(2, GL_FLOAT, 0, &points[0]);
|
|
||||||
glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2);
|
glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2);
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeContext::DrawDeviceName(std::string deviceName) {
|
||||||
|
GLint vp[4];
|
||||||
|
glGetIntegerv( GL_VIEWPORT, vp);
|
||||||
|
float viewHeight = (float) vp[3];
|
||||||
|
float hPos = (float) (viewHeight - 20) / viewHeight;
|
||||||
|
|
||||||
|
glColor3f(0.65,0.65,0.65);
|
||||||
|
getFont(PrimaryGLContext::GLFONT_SIZE12).drawString(deviceName.c_str(), 1.0, hPos, 12, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopeContext::DrawEnd() {
|
void ScopeContext::DrawEnd() {
|
||||||
|
@ -12,7 +12,8 @@ public:
|
|||||||
ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext);
|
ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext);
|
||||||
|
|
||||||
void DrawBegin();
|
void DrawBegin();
|
||||||
void Plot(std::vector<float> &points);
|
void Plot(std::vector<float> &points, bool stereo=false);
|
||||||
|
void DrawDeviceName(std::string deviceName);
|
||||||
void DrawDivider();
|
void DrawDivider();
|
||||||
void DrawEnd();
|
void DrawEnd();
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "AppFrame.h"
|
#include "AppFrame.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <wx/numformatter.h>
|
#include <wx/numformatter.h>
|
||||||
|
#include "WaterfallCanvas.h"
|
||||||
|
|
||||||
wxBEGIN_EVENT_TABLE(SpectrumCanvas, wxGLCanvas) EVT_PAINT(SpectrumCanvas::OnPaint)
|
wxBEGIN_EVENT_TABLE(SpectrumCanvas, wxGLCanvas) EVT_PAINT(SpectrumCanvas::OnPaint)
|
||||||
EVT_IDLE(SpectrumCanvas::OnIdle)
|
EVT_IDLE(SpectrumCanvas::OnIdle)
|
||||||
@ -26,28 +27,40 @@ EVT_MOUSEWHEEL(SpectrumCanvas::mouseWheelMoved)
|
|||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
||||||
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
InteractiveCanvas(parent, attribList), fft_size(0), in(NULL), out(NULL), plan(NULL), fft_ceil_ma(1), fft_ceil_maa(1), fft_floor_ma(0), fft_floor_maa(
|
||||||
wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0) {
|
0), waterfallCanvas(NULL) {
|
||||||
|
|
||||||
int in_block_size = FFT_SIZE;
|
|
||||||
int out_block_size = FFT_SIZE;
|
|
||||||
|
|
||||||
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size);
|
|
||||||
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size);
|
|
||||||
plan = fftw_plan_dft_1d(out_block_size, in, out, FFTW_FORWARD, FFTW_MEASURE);
|
|
||||||
|
|
||||||
fft_ceil_ma = fft_ceil_maa = 100.0;
|
|
||||||
fft_floor_ma = fft_floor_maa = 0.0;
|
|
||||||
|
|
||||||
glContext = new SpectrumContext(this, &wxGetApp().GetContext(this));
|
glContext = new SpectrumContext(this, &wxGetApp().GetContext(this));
|
||||||
timer.start();
|
|
||||||
|
|
||||||
mTracker.setTarget(this);
|
|
||||||
mTracker.setVertDragLock(true);
|
mTracker.setVertDragLock(true);
|
||||||
|
|
||||||
SetCursor(wxCURSOR_SIZEWE);
|
SetCursor(wxCURSOR_SIZEWE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpectrumCanvas::Setup(int fft_size_in) {
|
||||||
|
if (fft_size == fft_size_in) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fft_size = fft_size_in;
|
||||||
|
|
||||||
|
if (in) {
|
||||||
|
free(in);
|
||||||
|
}
|
||||||
|
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
|
||||||
|
if (out) {
|
||||||
|
free(out);
|
||||||
|
}
|
||||||
|
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
|
||||||
|
if (plan) {
|
||||||
|
fftw_destroy_plan(plan);
|
||||||
|
}
|
||||||
|
plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE);
|
||||||
|
|
||||||
|
fft_ceil_ma = fft_ceil_maa = 100.0;
|
||||||
|
fft_floor_ma = fft_floor_maa = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
SpectrumCanvas::~SpectrumCanvas() {
|
SpectrumCanvas::~SpectrumCanvas() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -60,12 +73,12 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
glViewport(0, 0, ClientSize.x, ClientSize.y);
|
glViewport(0, 0, ClientSize.x, ClientSize.y);
|
||||||
|
|
||||||
glContext->BeginDraw();
|
glContext->BeginDraw();
|
||||||
glContext->Draw(spectrum_points);
|
glContext->Draw(spectrum_points, GetCenterFrequency(), GetBandwidth());
|
||||||
|
|
||||||
std::vector<DemodulatorInstance *> &demods = wxGetApp().getDemodMgr().getDemodulators();
|
std::vector<DemodulatorInstance *> &demods = wxGetApp().getDemodMgr().getDemodulators();
|
||||||
|
|
||||||
for (int i = 0, iMax = demods.size(); i < iMax; i++) {
|
for (int i = 0, iMax = demods.size(); i < iMax; i++) {
|
||||||
glContext->DrawDemodInfo(demods[i]);
|
glContext->DrawDemodInfo(demods[i], 1, 1, 1, GetCenterFrequency(), GetBandwidth());
|
||||||
}
|
}
|
||||||
|
|
||||||
glContext->EndDraw();
|
glContext->EndDraw();
|
||||||
@ -73,14 +86,23 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumCanvas::setData(std::vector<liquid_float_complex> *data) {
|
void SpectrumCanvas::setData(DemodulatorThreadIQData *input) {
|
||||||
|
if (!input) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<liquid_float_complex> *data = &input->data;
|
||||||
if (data && data->size()) {
|
if (data && data->size()) {
|
||||||
if (spectrum_points.size() < FFT_SIZE * 2) {
|
if (fft_size != data->size()) {
|
||||||
spectrum_points.resize(FFT_SIZE * 2);
|
Setup(data->size());
|
||||||
|
}
|
||||||
|
if (spectrum_points.size() < fft_size * 2) {
|
||||||
|
if (spectrum_points.capacity() < fft_size * 2) {
|
||||||
|
spectrum_points.reserve(fft_size * 2);
|
||||||
|
}
|
||||||
|
spectrum_points.resize(fft_size * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < FFT_SIZE; i++) {
|
for (int i = 0; i < fft_size; i++) {
|
||||||
in[i][0] = (*data)[i].real;
|
in[i][0] = (*data)[i].real;
|
||||||
in[i][1] = (*data)[i].imag;
|
in[i][1] = (*data)[i].imag;
|
||||||
}
|
}
|
||||||
@ -89,31 +111,33 @@ void SpectrumCanvas::setData(std::vector<liquid_float_complex> *data) {
|
|||||||
|
|
||||||
double fft_ceil = 0, fft_floor = 1;
|
double fft_ceil = 0, fft_floor = 1;
|
||||||
|
|
||||||
if (fft_result.size() < FFT_SIZE) {
|
if (fft_result.size() != fft_size) {
|
||||||
fft_result.resize(FFT_SIZE);
|
if (fft_result.capacity() < fft_size) {
|
||||||
fft_result_ma.resize(FFT_SIZE);
|
fft_result.reserve(fft_size);
|
||||||
fft_result_maa.resize(FFT_SIZE);
|
fft_result_ma.reserve(fft_size);
|
||||||
|
fft_result_maa.reserve(fft_size);
|
||||||
|
}
|
||||||
|
fft_result.resize(fft_size);
|
||||||
|
fft_result_ma.resize(fft_size);
|
||||||
|
fft_result_maa.resize(fft_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int n;
|
int n;
|
||||||
for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) {
|
for (int i = 0, iMax = fft_size / 2; i < iMax; i++) {
|
||||||
n = (i == 0) ? 1 : i;
|
n = (i == 0) ? 1 : i;
|
||||||
double a = out[n][0];
|
double a = out[n][0];
|
||||||
double b = out[n][1];
|
double b = out[n][1];
|
||||||
double c = sqrt(a * a + b * b);
|
double c = sqrt(a * a + b * b);
|
||||||
|
|
||||||
// n = (i == FFT_SIZE / 2) ? (FFT_SIZE / 2 + 1) : i;
|
double x = out[fft_size / 2 + n][0];
|
||||||
double x = out[FFT_SIZE / 2 + n][0];
|
double y = out[fft_size / 2 + n][1];
|
||||||
double y = out[FFT_SIZE / 2 + n][1];
|
|
||||||
double z = sqrt(x * x + y * y);
|
double z = sqrt(x * x + y * y);
|
||||||
|
|
||||||
fft_result[i] = (z);
|
fft_result[i] = (z);
|
||||||
fft_result[FFT_SIZE / 2 + i] = (c);
|
fft_result[fft_size / 2 + i] = (c);
|
||||||
}
|
}
|
||||||
|
|
||||||
float time_slice = (float) SRATE / (float) (BUF_SIZE / 2);
|
for (int i = 0, iMax = fft_size; i < iMax; i++) {
|
||||||
|
|
||||||
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) {
|
|
||||||
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65;
|
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65;
|
||||||
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65;
|
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65;
|
||||||
|
|
||||||
@ -136,7 +160,7 @@ void SpectrumCanvas::setData(std::vector<liquid_float_complex> *data) {
|
|||||||
|
|
||||||
// fftw_execute(plan[1]);
|
// fftw_execute(plan[1]);
|
||||||
|
|
||||||
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) {
|
for (int i = 0, iMax = fft_size; i < iMax; i++) {
|
||||||
float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa));
|
float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa));
|
||||||
spectrum_points[i * 2] = ((float) i / (float) iMax);
|
spectrum_points[i * 2] = ((float) i / (float) iMax);
|
||||||
spectrum_points[i * 2 + 1] = v;
|
spectrum_points[i * 2 + 1] = v;
|
||||||
@ -146,51 +170,66 @@ void SpectrumCanvas::setData(std::vector<liquid_float_complex> *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumCanvas::OnIdle(wxIdleEvent &event) {
|
void SpectrumCanvas::OnIdle(wxIdleEvent &event) {
|
||||||
// timer.update();
|
|
||||||
// frameTimer += timer.lastUpdateSeconds();
|
|
||||||
// if (frameTimer > 1.0/30.0) {
|
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
// frameTimer = 0;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumCanvas::mouseMoved(wxMouseEvent& event) {
|
void SpectrumCanvas::mouseMoved(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseMoved(event);
|
InteractiveCanvas::mouseMoved(event);
|
||||||
if (mTracker.mouseDown()) {
|
if (mTracker.mouseDown()) {
|
||||||
int freqChange = mTracker.getDeltaMouseX() * SRATE;
|
int freqChange = mTracker.getDeltaMouseX() * GetBandwidth();
|
||||||
|
|
||||||
if (freqChange != 0) {
|
if (freqChange != 0) {
|
||||||
int freq = wxGetApp().getFrequency();
|
int freq = wxGetApp().getFrequency();
|
||||||
|
|
||||||
|
if (isView) {
|
||||||
|
center_freq = center_freq - freqChange;
|
||||||
|
if (waterfallCanvas) {
|
||||||
|
waterfallCanvas->SetCenterFrequency(center_freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bw = (int) bandwidth;
|
||||||
|
int bwOfs = ((int) center_freq > freq) ? ((int) bandwidth / 2) : (-(int) bandwidth / 2);
|
||||||
|
int freqEdge = ((int) center_freq + bwOfs);
|
||||||
|
|
||||||
|
if (abs(freq - freqEdge) > (SRATE / 2)) {
|
||||||
|
freqChange = -(((int) center_freq > freq) ? (freqEdge - freq - (SRATE / 2)) : (freqEdge - freq + (SRATE / 2)));
|
||||||
|
} else {
|
||||||
|
freqChange = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (freqChange) {
|
||||||
freq -= freqChange;
|
freq -= freqChange;
|
||||||
wxGetApp().setFrequency(freq);
|
wxGetApp().setFrequency(freq);
|
||||||
|
setStatusText("Set center frequency: %s", freq);
|
||||||
((wxFrame*) parent)->GetStatusBar()->SetStatusText(
|
|
||||||
wxString::Format(wxT("Set center frequency: %s"),
|
|
||||||
wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setStatusText("Click and drag to adjust center frequency.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumCanvas::mouseDown(wxMouseEvent& event) {
|
void SpectrumCanvas::mouseDown(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseDown(event);
|
InteractiveCanvas::mouseDown(event);
|
||||||
SetCursor(wxCURSOR_CROSS);
|
SetCursor(wxCURSOR_CROSS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumCanvas::mouseWheelMoved(wxMouseEvent& event) {
|
void SpectrumCanvas::mouseWheelMoved(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseWheelMoved(event);
|
InteractiveCanvas::mouseWheelMoved(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumCanvas::mouseReleased(wxMouseEvent& event) {
|
void SpectrumCanvas::mouseReleased(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseReleased(event);
|
InteractiveCanvas::mouseReleased(event);
|
||||||
SetCursor(wxCURSOR_SIZEWE);
|
SetCursor(wxCURSOR_SIZEWE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumCanvas::mouseLeftWindow(wxMouseEvent& event) {
|
void SpectrumCanvas::mouseLeftWindow(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseLeftWindow(event);
|
InteractiveCanvas::mouseLeftWindow(event);
|
||||||
SetCursor(wxCURSOR_SIZEWE);
|
SetCursor(wxCURSOR_SIZEWE);
|
||||||
}
|
}
|
||||||
|
|
||||||
//void SpectrumCanvas::rightClick(wxMouseEvent& event) {}
|
void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) {
|
||||||
//void SpectrumCanvas::keyPressed(wxKeyEvent& event) {}
|
waterfallCanvas = canvas_in;
|
||||||
//void SpectrumCanvas::keyReleased(wxKeyEvent& event) {}
|
}
|
||||||
|
|
||||||
|
@ -6,18 +6,25 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
#include "InteractiveCanvas.h"
|
||||||
#include "SpectrumContext.h"
|
#include "SpectrumContext.h"
|
||||||
|
|
||||||
#include "fftw3.h"
|
#include "fftw3.h"
|
||||||
#include "Timer.h"
|
|
||||||
#include "MouseTracker.h"
|
#include "MouseTracker.h"
|
||||||
|
|
||||||
class SpectrumCanvas: public wxGLCanvas {
|
class WaterfallCanvas;
|
||||||
|
|
||||||
|
class SpectrumCanvas: public InteractiveCanvas {
|
||||||
public:
|
public:
|
||||||
|
std::vector<float> spectrum_points;
|
||||||
|
|
||||||
SpectrumCanvas(wxWindow *parent, int *attribList = NULL);
|
SpectrumCanvas(wxWindow *parent, int *attribList = NULL);
|
||||||
|
void Setup(int fft_size_in);
|
||||||
~SpectrumCanvas();
|
~SpectrumCanvas();
|
||||||
|
|
||||||
void setData(std::vector<liquid_float_complex> *data);
|
void setData(DemodulatorThreadIQData *input);
|
||||||
|
void attachWaterfallCanvas(WaterfallCanvas *canvas_in);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnPaint(wxPaintEvent& event);
|
void OnPaint(wxPaintEvent& event);
|
||||||
|
|
||||||
@ -31,24 +38,19 @@ private:
|
|||||||
// void rightClick(wxMouseEvent& event);
|
// void rightClick(wxMouseEvent& event);
|
||||||
void mouseLeftWindow(wxMouseEvent& event);
|
void mouseLeftWindow(wxMouseEvent& event);
|
||||||
|
|
||||||
wxWindow *parent;
|
|
||||||
std::vector<float> spectrum_points;
|
|
||||||
|
|
||||||
fftw_complex *in, *out;
|
fftw_complex *in, *out;
|
||||||
fftw_plan plan;
|
fftw_plan plan;
|
||||||
|
|
||||||
float fft_ceil_ma, fft_ceil_maa;
|
double fft_ceil_ma, fft_ceil_maa;
|
||||||
float fft_floor_ma, fft_floor_maa;
|
double fft_floor_ma, fft_floor_maa;
|
||||||
|
|
||||||
std::vector<float> fft_result;
|
std::vector<double> fft_result;
|
||||||
std::vector<float> fft_result_ma;
|
std::vector<double> fft_result_ma;
|
||||||
std::vector<float> fft_result_maa;
|
std::vector<double> fft_result_maa;
|
||||||
|
|
||||||
SpectrumContext *glContext;
|
SpectrumContext *glContext;
|
||||||
Timer timer;
|
WaterfallCanvas *waterfallCanvas;
|
||||||
float frameTimer;
|
int fft_size;
|
||||||
|
|
||||||
MouseTracker mTracker;
|
|
||||||
// event table
|
// event table
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
SpectrumContext::SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedContext) :
|
SpectrumContext::SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedContext) :
|
||||||
PrimaryGLContext(canvas, sharedContext) {
|
PrimaryGLContext(canvas, sharedContext), fft_size(0) {
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ SpectrumContext::SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedCont
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumContext::Draw(std::vector<float> &points) {
|
void SpectrumContext::Draw(std::vector<float> &points, int freq, int bandwidth) {
|
||||||
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
glColor3f(1.0, 1.0, 1.0);
|
glColor3f(1.0, 1.0, 1.0);
|
||||||
@ -35,9 +35,10 @@ void SpectrumContext::Draw(std::vector<float> &points) {
|
|||||||
glGetIntegerv( GL_VIEWPORT, vp);
|
glGetIntegerv( GL_VIEWPORT, vp);
|
||||||
|
|
||||||
float viewHeight = (float) vp[3];
|
float viewHeight = (float) vp[3];
|
||||||
|
float viewWidth = (float) vp[2];
|
||||||
|
|
||||||
float leftFreq = (float) wxGetApp().getFrequency() - ((float) SRATE / 2.0);
|
float leftFreq = (float) freq - ((float) bandwidth / 2.0);
|
||||||
float rightFreq = leftFreq + (float) SRATE;
|
float rightFreq = leftFreq + (float) bandwidth;
|
||||||
|
|
||||||
float firstMhz = floor(leftFreq / 1000000.0) * 1000000.0;
|
float firstMhz = floor(leftFreq / 1000000.0) * 1000000.0;
|
||||||
float mhzStart = ((firstMhz - leftFreq) / (rightFreq - leftFreq)) * 2.0;
|
float mhzStart = ((firstMhz - leftFreq) / (rightFreq - leftFreq)) * 2.0;
|
||||||
|
@ -11,7 +11,8 @@ class SpectrumContext: public PrimaryGLContext {
|
|||||||
public:
|
public:
|
||||||
SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedContext);
|
SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedContext);
|
||||||
|
|
||||||
void Draw(std::vector<float> &points);
|
void Draw(std::vector<float> &points, int freq, int bandwidth);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int fft_size;
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <wx/numformatter.h>
|
#include <wx/numformatter.h>
|
||||||
|
|
||||||
|
#define MIN_BANDWIDTH 1500
|
||||||
|
|
||||||
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)
|
||||||
EVT_KEY_UP(WaterfallCanvas::OnKeyUp)
|
EVT_KEY_UP(WaterfallCanvas::OnKeyUp)
|
||||||
@ -29,37 +31,58 @@ EVT_ENTER_WINDOW(WaterfallCanvas::mouseEnterWindow)
|
|||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
|
WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
|
||||||
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
InteractiveCanvas(parent, attribList), spectrumCanvas(NULL), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState(
|
||||||
wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState(
|
WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), waterfall_lines(0), plan(
|
||||||
WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false) {
|
NULL), in(NULL), out(NULL), resampler(NULL), resample_ratio(0), last_input_bandwidth(0), zoom(0) {
|
||||||
|
|
||||||
int in_block_size = FFT_SIZE;
|
glContext = new WaterfallContext(this, &wxGetApp().GetContext(this));
|
||||||
int out_block_size = FFT_SIZE;
|
|
||||||
|
|
||||||
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size);
|
nco_shift = nco_crcf_create(LIQUID_NCO);
|
||||||
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size);
|
shift_freq = 0;
|
||||||
plan = fftw_plan_dft_1d(out_block_size, in, out, FFTW_FORWARD, FFTW_MEASURE);
|
|
||||||
|
|
||||||
fft_ceil_ma = fft_ceil_maa = 100.0;
|
fft_ceil_ma = fft_ceil_maa = 100.0;
|
||||||
fft_floor_ma = fft_floor_maa = 0.0;
|
fft_floor_ma = fft_floor_maa = 0.0;
|
||||||
|
|
||||||
glContext = new WaterfallContext(this, &wxGetApp().GetContext(this));
|
|
||||||
timer.start();
|
|
||||||
|
|
||||||
mTracker.setTarget(this);
|
|
||||||
SetCursor(wxCURSOR_CROSS);
|
SetCursor(wxCURSOR_CROSS);
|
||||||
}
|
}
|
||||||
|
|
||||||
WaterfallCanvas::~WaterfallCanvas() {
|
WaterfallCanvas::~WaterfallCanvas() {
|
||||||
|
nco_crcf_destroy(nco_shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
int WaterfallCanvas::GetFrequencyAt(float x) {
|
void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) {
|
||||||
|
if (fft_size == fft_size_in && waterfall_lines_in == waterfall_lines) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fft_size = fft_size_in;
|
||||||
|
waterfall_lines = waterfall_lines_in;
|
||||||
|
|
||||||
int center_freq = wxGetApp().getFrequency();
|
if (in) {
|
||||||
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) x * (float) SRATE);
|
free(in);
|
||||||
|
}
|
||||||
|
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
|
||||||
|
if (out) {
|
||||||
|
free(out);
|
||||||
|
}
|
||||||
|
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
|
||||||
|
if (plan) {
|
||||||
|
fftw_destroy_plan(plan);
|
||||||
|
}
|
||||||
|
plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
|
||||||
return freq;
|
glContext->Setup(fft_size, waterfall_lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
WaterfallCanvas::DragState WaterfallCanvas::getDragState() {
|
||||||
|
return dragState;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaterfallCanvas::DragState WaterfallCanvas::getNextDragState() {
|
||||||
|
return nextDragState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaterfallCanvas::attachSpectrumCanvas(SpectrumCanvas *canvas_in) {
|
||||||
|
spectrumCanvas = canvas_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||||
@ -80,6 +103,9 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
bool isNew = shiftDown
|
bool isNew = shiftDown
|
||||||
|| (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive());
|
|| (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive());
|
||||||
|
|
||||||
|
int currentBandwidth = GetBandwidth();
|
||||||
|
int currentCenterFreq = GetCenterFrequency();
|
||||||
|
|
||||||
if (mTracker.mouseInView()) {
|
if (mTracker.mouseInView()) {
|
||||||
if (nextDragState == WF_DRAG_RANGE) {
|
if (nextDragState == WF_DRAG_RANGE) {
|
||||||
if (mTracker.mouseDown()) {
|
if (mTracker.mouseDown()) {
|
||||||
@ -87,47 +113,49 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
float centerPos = mTracker.getOriginMouseX() + width / 2.0;
|
float centerPos = mTracker.getOriginMouseX() + width / 2.0;
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
glContext->DrawDemod(lastActiveDemodulator);
|
glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||||
glContext->DrawFreqSelector(centerPos, 0, 1, 0, width ? width : (1.0 / (float) ClientSize.x));
|
glContext->DrawFreqSelector(centerPos, 0, 1, 0, width ? width : (1.0 / (float) ClientSize.x), currentCenterFreq,
|
||||||
|
currentBandwidth);
|
||||||
} else {
|
} else {
|
||||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0);
|
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||||
glContext->DrawFreqSelector(centerPos, 1, 1, 0, width ? width : (1.0 / (float) ClientSize.x));
|
glContext->DrawFreqSelector(centerPos, 1, 1, 0, width ? width : (1.0 / (float) ClientSize.x), currentCenterFreq,
|
||||||
|
currentBandwidth);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
glContext->DrawDemod(lastActiveDemodulator);
|
glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 1.0 / (float) ClientSize.x);
|
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 1.0 / (float) ClientSize.x, currentCenterFreq, currentBandwidth);
|
||||||
} else {
|
} else {
|
||||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0);
|
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 1.0 / (float) ClientSize.x);
|
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 1.0 / (float) ClientSize.x, currentCenterFreq, currentBandwidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (activeDemodulator == NULL) {
|
if (activeDemodulator == NULL) {
|
||||||
if (lastActiveDemodulator) {
|
if (lastActiveDemodulator) {
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
glContext->DrawDemod(lastActiveDemodulator);
|
glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0);
|
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||||
} else {
|
} else {
|
||||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0);
|
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0);
|
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0);
|
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (lastActiveDemodulator) {
|
if (lastActiveDemodulator) {
|
||||||
glContext->DrawDemod(lastActiveDemodulator);
|
glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||||
}
|
}
|
||||||
glContext->DrawDemod(activeDemodulator, 1, 1, 0);
|
glContext->DrawDemod(activeDemodulator, 1, 1, 0, currentCenterFreq, currentBandwidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (activeDemodulator) {
|
if (activeDemodulator) {
|
||||||
glContext->DrawDemod(activeDemodulator);
|
glContext->DrawDemod(activeDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||||
}
|
}
|
||||||
if (lastActiveDemodulator) {
|
if (lastActiveDemodulator) {
|
||||||
glContext->DrawDemod(lastActiveDemodulator);
|
glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +163,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
if (activeDemodulator == demods[i] || lastActiveDemodulator == demods[i]) {
|
if (activeDemodulator == demods[i] || lastActiveDemodulator == demods[i]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
glContext->DrawDemod(demods[i]);
|
glContext->DrawDemod(demods[i], 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
glContext->EndDraw();
|
glContext->EndDraw();
|
||||||
@ -144,43 +172,78 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
|
void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
|
||||||
|
InteractiveCanvas::OnKeyUp(event);
|
||||||
shiftDown = event.ShiftDown();
|
shiftDown = event.ShiftDown();
|
||||||
altDown = event.AltDown();
|
altDown = event.AltDown();
|
||||||
ctrlDown = event.ControlDown();
|
ctrlDown = event.ControlDown();
|
||||||
// switch (event.GetKeyCode()) {
|
switch (event.GetKeyCode()) {
|
||||||
// }
|
case 'A':
|
||||||
|
zoom = 0;
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
zoom = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
||||||
|
InteractiveCanvas::OnKeyDown(event);
|
||||||
float angle = 5.0;
|
float angle = 5.0;
|
||||||
|
|
||||||
shiftDown = event.ShiftDown();
|
|
||||||
altDown = event.AltDown();
|
|
||||||
ctrlDown = event.ControlDown();
|
|
||||||
|
|
||||||
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
|
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
|
||||||
|
|
||||||
unsigned int freq;
|
unsigned int freq;
|
||||||
|
unsigned int bw;
|
||||||
switch (event.GetKeyCode()) {
|
switch (event.GetKeyCode()) {
|
||||||
|
case 'A':
|
||||||
|
zoom = 1;
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
zoom = -1;
|
||||||
|
break;
|
||||||
case WXK_RIGHT:
|
case WXK_RIGHT:
|
||||||
freq = wxGetApp().getFrequency();
|
freq = wxGetApp().getFrequency();
|
||||||
if (shiftDown) {
|
if (shiftDown) {
|
||||||
freq += SRATE * 10;
|
freq += SRATE * 10;
|
||||||
|
if (isView) {
|
||||||
|
SetView(center_freq + SRATE * 10, GetBandwidth());
|
||||||
|
if (spectrumCanvas) {
|
||||||
|
spectrumCanvas->SetView(GetCenterFrequency(), GetBandwidth());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
freq += SRATE / 2;
|
freq += SRATE / 2;
|
||||||
|
if (isView) {
|
||||||
|
SetView(center_freq + SRATE / 2, GetBandwidth());
|
||||||
|
if (spectrumCanvas) {
|
||||||
|
spectrumCanvas->SetView(GetCenterFrequency(), GetBandwidth());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
wxGetApp().setFrequency(freq);
|
wxGetApp().setFrequency(freq);
|
||||||
((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq));
|
setStatusText("Set center frequency: %s", freq);
|
||||||
break;
|
break;
|
||||||
case WXK_LEFT:
|
case WXK_LEFT:
|
||||||
freq = wxGetApp().getFrequency();
|
freq = wxGetApp().getFrequency();
|
||||||
if (shiftDown) {
|
if (shiftDown) {
|
||||||
freq -= SRATE * 10;
|
freq -= SRATE * 10;
|
||||||
|
if (isView) {
|
||||||
|
SetView(center_freq - SRATE * 10, GetBandwidth());
|
||||||
|
if (spectrumCanvas) {
|
||||||
|
spectrumCanvas->SetView(GetCenterFrequency(), GetBandwidth());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
freq -= SRATE / 2;
|
freq -= SRATE / 2;
|
||||||
|
if (isView) {
|
||||||
|
SetView(center_freq - SRATE / 2, GetBandwidth());
|
||||||
|
if (spectrumCanvas) {
|
||||||
|
spectrumCanvas->SetView(GetCenterFrequency(), GetBandwidth());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
wxGetApp().setFrequency(freq);
|
wxGetApp().setFrequency(freq);
|
||||||
((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq));
|
setStatusText("Set center frequency: %s", freq);
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
case WXK_DELETE:
|
case WXK_DELETE:
|
||||||
@ -216,49 +279,207 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::setData(std::vector<liquid_float_complex> *data) {
|
void WaterfallCanvas::setData(DemodulatorThreadIQData *input) {
|
||||||
|
if (!input) {
|
||||||
if (data && data->size()) {
|
return;
|
||||||
if (spectrum_points.size() < FFT_SIZE * 2) {
|
|
||||||
spectrum_points.resize(FFT_SIZE * 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < FFT_SIZE; i++) {
|
unsigned int bw;
|
||||||
|
if (zoom) {
|
||||||
|
int freq = wxGetApp().getFrequency();
|
||||||
|
|
||||||
|
if (zoom > 0) {
|
||||||
|
center_freq = GetCenterFrequency();
|
||||||
|
bw = GetBandwidth();
|
||||||
|
bw = (unsigned int) ceil((float) bw * 0.95);
|
||||||
|
if (bw < 80000) {
|
||||||
|
bw = 80000;
|
||||||
|
}
|
||||||
|
if (mTracker.mouseInView()) {
|
||||||
|
int mfreqA = GetFrequencyAt(mTracker.getMouseX());
|
||||||
|
SetBandwidth(bw);
|
||||||
|
int mfreqB = GetFrequencyAt(mTracker.getMouseX());
|
||||||
|
center_freq += mfreqA - mfreqB;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetView(center_freq, bw);
|
||||||
|
if (spectrumCanvas) {
|
||||||
|
spectrumCanvas->SetView(center_freq, bw);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isView) {
|
||||||
|
bw = GetBandwidth();
|
||||||
|
bw = (unsigned int) ceil((float) bw * 1.05);
|
||||||
|
if ((int) bw >= SRATE) {
|
||||||
|
bw = (unsigned int) SRATE;
|
||||||
|
DisableView();
|
||||||
|
if (spectrumCanvas) {
|
||||||
|
spectrumCanvas->DisableView();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mTracker.mouseInView()) {
|
||||||
|
int freq = wxGetApp().getFrequency();
|
||||||
|
int mfreqA = GetFrequencyAt(mTracker.getMouseX());
|
||||||
|
SetBandwidth(bw);
|
||||||
|
int mfreqB = GetFrequencyAt(mTracker.getMouseX());
|
||||||
|
center_freq += mfreqA - mfreqB;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetView(GetCenterFrequency(), bw);
|
||||||
|
if (spectrumCanvas) {
|
||||||
|
spectrumCanvas->SetView(center_freq, bw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (center_freq < freq && (center_freq - bandwidth / 2) < (freq - SRATE / 2)) {
|
||||||
|
center_freq = (freq - SRATE / 2) + bandwidth / 2;
|
||||||
|
}
|
||||||
|
if (center_freq > freq && (center_freq + bandwidth / 2) > (freq + SRATE / 2)) {
|
||||||
|
center_freq = (freq + SRATE / 2) - bandwidth / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<liquid_float_complex> *data = &input->data;
|
||||||
|
|
||||||
|
if (data && data->size()) {
|
||||||
|
// if (fft_size != data->size() && !isView) {
|
||||||
|
// Setup(data->size(), waterfall_lines);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (last_bandwidth != bandwidth && !isView) {
|
||||||
|
// Setup(bandwidth, waterfall_lines);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (spectrum_points.size() < fft_size * 2) {
|
||||||
|
spectrum_points.resize(fft_size * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isView) {
|
||||||
|
if (!input->frequency || !input->bandwidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (center_freq != input->frequency) {
|
||||||
|
if (((int) center_freq - (int) input->frequency) != shift_freq || last_input_bandwidth != input->bandwidth) {
|
||||||
|
if ((int) input->frequency - abs((int) center_freq) < (int) ((float) ((float) SRATE / 2.0))) {
|
||||||
|
shift_freq = (int) center_freq - (int) input->frequency;
|
||||||
|
nco_crcf_reset(nco_shift);
|
||||||
|
nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((float) abs(shift_freq)) / ((float) input->bandwidth)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift_buffer.size() != input->data.size()) {
|
||||||
|
if (shift_buffer.capacity() < input->data.size()) {
|
||||||
|
shift_buffer.reserve(input->data.size());
|
||||||
|
}
|
||||||
|
shift_buffer.resize(input->data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift_freq < 0) {
|
||||||
|
nco_crcf_mix_block_up(nco_shift, &input->data[0], &shift_buffer[0], input->data.size());
|
||||||
|
} else {
|
||||||
|
nco_crcf_mix_block_down(nco_shift, &input->data[0], &shift_buffer[0], input->data.size());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shift_buffer.assign(input->data.begin(), input->data.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resampler || bandwidth != last_bandwidth || last_input_bandwidth != input->bandwidth) {
|
||||||
|
resample_ratio = (double) (bandwidth) / (double) input->bandwidth;
|
||||||
|
|
||||||
|
float As = 60.0f;
|
||||||
|
|
||||||
|
if (resampler) {
|
||||||
|
msresamp_crcf_destroy(resampler);
|
||||||
|
}
|
||||||
|
resampler = msresamp_crcf_create(resample_ratio, As);
|
||||||
|
|
||||||
|
last_bandwidth = bandwidth;
|
||||||
|
last_input_bandwidth = input->bandwidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int out_size = ceil((double) (input->data.size()) * resample_ratio) + 512;
|
||||||
|
|
||||||
|
if (resampler_buffer.size() != out_size) {
|
||||||
|
if (resampler_buffer.capacity() < out_size) {
|
||||||
|
resampler_buffer.reserve(out_size);
|
||||||
|
}
|
||||||
|
resampler_buffer.resize(out_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int num_written;
|
||||||
|
msresamp_crcf_execute(resampler, &shift_buffer[0], input->data.size(), &resampler_buffer[0], &num_written);
|
||||||
|
|
||||||
|
resampler_buffer.resize(fft_size);
|
||||||
|
|
||||||
|
if (num_written < fft_size) {
|
||||||
|
for (int i = 0; i < num_written; i++) {
|
||||||
|
in[i][0] = resampler_buffer[i].real;
|
||||||
|
in[i][1] = resampler_buffer[i].imag;
|
||||||
|
}
|
||||||
|
for (int i = num_written; i < fft_size; i++) {
|
||||||
|
in[i][0] = 0;
|
||||||
|
in[i][1] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < fft_size; i++) {
|
||||||
|
in[i][0] = resampler_buffer[i].real;
|
||||||
|
in[i][1] = resampler_buffer[i].imag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (data->size() < fft_size) {
|
||||||
|
for (int i = 0, iMax = data->size(); i < iMax; i++) {
|
||||||
in[i][0] = (*data)[i].real;
|
in[i][0] = (*data)[i].real;
|
||||||
in[i][1] = (*data)[i].imag;
|
in[i][1] = (*data)[i].imag;
|
||||||
}
|
}
|
||||||
|
for (int i = data->size(); i < fft_size; i++) {
|
||||||
|
in[i][0] = 0;
|
||||||
|
in[i][1] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < fft_size; i++) {
|
||||||
|
in[i][0] = (*data)[i].real;
|
||||||
|
in[i][1] = (*data)[i].imag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fftw_execute(plan);
|
fftw_execute(plan);
|
||||||
|
|
||||||
double fft_ceil = 0, fft_floor = 1;
|
double fft_ceil = 0, fft_floor = 1;
|
||||||
|
|
||||||
if (fft_result.size() < FFT_SIZE) {
|
if (fft_result.size() < fft_size) {
|
||||||
fft_result.resize(FFT_SIZE);
|
fft_result.resize(fft_size);
|
||||||
fft_result_ma.resize(FFT_SIZE);
|
fft_result_ma.resize(fft_size);
|
||||||
fft_result_maa.resize(FFT_SIZE);
|
fft_result_maa.resize(fft_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int n;
|
int n;
|
||||||
for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) {
|
for (int i = 0, iMax = fft_size / 2; i < iMax; i++) {
|
||||||
n = (i == 0) ? 1 : i;
|
n = (i == 0) ? 1 : i;
|
||||||
double a = out[n][0];
|
double a = out[n][0];
|
||||||
double b = out[n][1];
|
double b = out[n][1];
|
||||||
double c = sqrt(a * a + b * b);
|
double c = sqrt(a * a + b * b);
|
||||||
|
|
||||||
// n = (i == FFT_SIZE / 2) ? (FFT_SIZE / 2 + 1) : i;
|
double x = out[fft_size / 2 + n][0];
|
||||||
double x = out[FFT_SIZE / 2 + n][0];
|
double y = out[fft_size / 2 + n][1];
|
||||||
double y = out[FFT_SIZE / 2 + n][1];
|
|
||||||
double z = sqrt(x * x + y * y);
|
double z = sqrt(x * x + y * y);
|
||||||
|
|
||||||
fft_result[i] = (z);
|
fft_result[i] = (z);
|
||||||
fft_result[FFT_SIZE / 2 + i] = (c);
|
fft_result[fft_size / 2 + i] = (c);
|
||||||
}
|
}
|
||||||
|
|
||||||
float time_slice = (float) SRATE / (float) (BUF_SIZE / 2);
|
for (int i = 0, iMax = fft_size; i < iMax; i++) {
|
||||||
|
if (isView) {
|
||||||
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) {
|
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.85;
|
||||||
|
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.55;
|
||||||
|
} else {
|
||||||
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65;
|
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65;
|
||||||
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65;
|
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65;
|
||||||
|
}
|
||||||
|
|
||||||
if (fft_result_maa[i] > fft_ceil) {
|
if (fft_result_maa[i] > fft_ceil) {
|
||||||
fft_ceil = fft_result_maa[i];
|
fft_ceil = fft_result_maa[i];
|
||||||
@ -271,37 +492,30 @@ void WaterfallCanvas::setData(std::vector<liquid_float_complex> *data) {
|
|||||||
fft_ceil += 1;
|
fft_ceil += 1;
|
||||||
fft_floor -= 1;
|
fft_floor -= 1;
|
||||||
|
|
||||||
fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.01;
|
fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05;
|
||||||
fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.01;
|
fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05;
|
||||||
|
|
||||||
fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.01;
|
fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05;
|
||||||
fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.01;
|
fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05;
|
||||||
|
|
||||||
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) {
|
for (int i = 0, iMax = fft_size; i < iMax; i++) {
|
||||||
float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa));
|
double v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa));
|
||||||
spectrum_points[i * 2] = ((float) i / (float) iMax);
|
spectrum_points[i * 2] = ((float) i / (float) iMax);
|
||||||
spectrum_points[i * 2 + 1] = v;
|
spectrum_points[i * 2 + 1] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (spectrumCanvas) {
|
||||||
|
spectrumCanvas->spectrum_points.assign(spectrum_points.begin(), spectrum_points.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
|
void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
|
||||||
// timer.update();
|
|
||||||
// frameTimer += timer.lastUpdateSeconds();
|
|
||||||
// if (frameTimer > 1.0/30.0) {
|
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
// frameTimer = 0;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseMoved(event);
|
InteractiveCanvas::mouseMoved(event);
|
||||||
|
|
||||||
shiftDown = event.ShiftDown();
|
|
||||||
altDown = event.AltDown();
|
|
||||||
ctrlDown = event.ControlDown();
|
|
||||||
|
|
||||||
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getActiveDemodulator();
|
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getActiveDemodulator();
|
||||||
|
|
||||||
if (mTracker.mouseDown()) {
|
if (mTracker.mouseDown()) {
|
||||||
@ -310,7 +524,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
|||||||
}
|
}
|
||||||
if (dragState == WF_DRAG_BANDWIDTH_LEFT || dragState == WF_DRAG_BANDWIDTH_RIGHT) {
|
if (dragState == WF_DRAG_BANDWIDTH_LEFT || dragState == WF_DRAG_BANDWIDTH_RIGHT) {
|
||||||
|
|
||||||
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) SRATE) * 2;
|
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) GetBandwidth()) * 2;
|
||||||
|
|
||||||
if (dragState == WF_DRAG_BANDWIDTH_LEFT) {
|
if (dragState == WF_DRAG_BANDWIDTH_LEFT) {
|
||||||
bwDiff = -bwDiff;
|
bwDiff = -bwDiff;
|
||||||
@ -323,19 +537,20 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
|||||||
DemodulatorThreadCommand command;
|
DemodulatorThreadCommand command;
|
||||||
command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH;
|
command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH;
|
||||||
activeDemodulatorBandwidth = activeDemodulatorBandwidth + bwDiff;
|
activeDemodulatorBandwidth = activeDemodulatorBandwidth + bwDiff;
|
||||||
if (activeDemodulatorBandwidth < 2000) {
|
|
||||||
activeDemodulatorBandwidth = 2000;
|
|
||||||
}
|
|
||||||
if (activeDemodulatorBandwidth > SRATE) {
|
if (activeDemodulatorBandwidth > SRATE) {
|
||||||
activeDemodulatorBandwidth = SRATE;
|
activeDemodulatorBandwidth = SRATE;
|
||||||
}
|
}
|
||||||
|
if (activeDemodulatorBandwidth < MIN_BANDWIDTH) {
|
||||||
|
activeDemodulatorBandwidth = MIN_BANDWIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
command.int_value = activeDemodulatorBandwidth;
|
command.int_value = activeDemodulatorBandwidth;
|
||||||
demod->getCommandQueue()->push(command);
|
demod->getCommandQueue()->push(command);
|
||||||
|
setStatusText("Set demodulator bandwidth: %s", activeDemodulatorBandwidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dragState == WF_DRAG_FREQUENCY) {
|
if (dragState == WF_DRAG_FREQUENCY) {
|
||||||
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) SRATE);
|
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) GetBandwidth());
|
||||||
|
|
||||||
if (!activeDemodulatorFrequency) {
|
if (!activeDemodulatorFrequency) {
|
||||||
activeDemodulatorFrequency = demod->getParams().frequency;
|
activeDemodulatorFrequency = demod->getParams().frequency;
|
||||||
@ -349,6 +564,8 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
|||||||
demod->getCommandQueue()->push(command);
|
demod->getCommandQueue()->push(command);
|
||||||
|
|
||||||
demod->updateLabel(activeDemodulatorFrequency);
|
demod->updateLabel(activeDemodulatorFrequency);
|
||||||
|
|
||||||
|
setStatusText("Set demodulator frequency: %s", activeDemodulatorFrequency);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int freqPos = GetFrequencyAt(mTracker.getMouseX());
|
int freqPos = GetFrequencyAt(mTracker.getMouseX());
|
||||||
@ -361,9 +578,14 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
|||||||
nextDragState = WF_DRAG_RANGE;
|
nextDragState = WF_DRAG_RANGE;
|
||||||
mTracker.setVertDragLock(true);
|
mTracker.setVertDragLock(true);
|
||||||
mTracker.setHorizDragLock(false);
|
mTracker.setHorizDragLock(false);
|
||||||
|
if (shiftDown) {
|
||||||
|
setStatusText("Click and drag to create a new demodulator by range.");
|
||||||
|
} else {
|
||||||
|
setStatusText("Click and drag to set the current demodulator range.");
|
||||||
|
}
|
||||||
} else if (demodsHover->size()) {
|
} else if (demodsHover->size()) {
|
||||||
int hovered = -1;
|
int hovered = -1;
|
||||||
int near_dist = SRATE;
|
int near_dist = GetBandwidth();
|
||||||
|
|
||||||
DemodulatorInstance *activeDemodulator = NULL;
|
DemodulatorInstance *activeDemodulator = NULL;
|
||||||
|
|
||||||
@ -407,16 +629,23 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
|||||||
|
|
||||||
mTracker.setVertDragLock(true);
|
mTracker.setVertDragLock(true);
|
||||||
mTracker.setHorizDragLock(false);
|
mTracker.setHorizDragLock(false);
|
||||||
|
setStatusText("Click and drag to change demodulator bandwidth. D to delete, SPACE for stereo.");
|
||||||
} else {
|
} else {
|
||||||
SetCursor(wxCURSOR_SIZING);
|
SetCursor(wxCURSOR_SIZING);
|
||||||
nextDragState = WF_DRAG_FREQUENCY;
|
nextDragState = WF_DRAG_FREQUENCY;
|
||||||
|
|
||||||
mTracker.setVertDragLock(true);
|
mTracker.setVertDragLock(true);
|
||||||
mTracker.setHorizDragLock(false);
|
mTracker.setHorizDragLock(false);
|
||||||
|
setStatusText("Click and drag to change demodulator frequency. D to delete, SPACE for stereo.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SetCursor(wxCURSOR_CROSS);
|
SetCursor(wxCURSOR_CROSS);
|
||||||
nextDragState = WF_DRAG_NONE;
|
nextDragState = WF_DRAG_NONE;
|
||||||
|
if (shiftDown) {
|
||||||
|
setStatusText("Click to create a new demodulator or hold ALT to drag range.");
|
||||||
|
} else {
|
||||||
|
setStatusText("Click to move active demodulator frequency or hold ALT to drag range; hold SHIFT to create new. A / Z to Zoom. Arrow keys (+SHIFT) to move center frequency.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete demodsHover;
|
delete demodsHover;
|
||||||
@ -424,12 +653,9 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::mouseDown(wxMouseEvent& event) {
|
void WaterfallCanvas::mouseDown(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseDown(event);
|
InteractiveCanvas::mouseDown(event);
|
||||||
dragState = nextDragState;
|
|
||||||
|
|
||||||
shiftDown = event.ShiftDown();
|
dragState = nextDragState;
|
||||||
altDown = event.AltDown();
|
|
||||||
ctrlDown = event.ControlDown();
|
|
||||||
|
|
||||||
if (dragState && dragState != WF_DRAG_RANGE) {
|
if (dragState && dragState != WF_DRAG_RANGE) {
|
||||||
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false);
|
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false);
|
||||||
@ -440,15 +666,11 @@ void WaterfallCanvas::mouseDown(wxMouseEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::mouseWheelMoved(wxMouseEvent& event) {
|
void WaterfallCanvas::mouseWheelMoved(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseWheelMoved(event);
|
InteractiveCanvas::mouseWheelMoved(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseReleased(event);
|
InteractiveCanvas::mouseReleased(event);
|
||||||
|
|
||||||
shiftDown = event.ShiftDown();
|
|
||||||
altDown = event.AltDown();
|
|
||||||
ctrlDown = event.ControlDown();
|
|
||||||
|
|
||||||
bool isNew = shiftDown
|
bool isNew = shiftDown
|
||||||
|| (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive());
|
|| (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive());
|
||||||
@ -460,8 +682,8 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
|
|
||||||
if (mTracker.getOriginDeltaMouseX() == 0 && mTracker.getOriginDeltaMouseY() == 0) {
|
if (mTracker.getOriginDeltaMouseX() == 0 && mTracker.getOriginDeltaMouseY() == 0) {
|
||||||
float pos = mTracker.getMouseX();
|
float pos = mTracker.getMouseX();
|
||||||
int center_freq = wxGetApp().getFrequency();
|
int input_center_freq = GetCenterFrequency();
|
||||||
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE);
|
int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth());
|
||||||
|
|
||||||
if (dragState == WF_DRAG_NONE) {
|
if (dragState == WF_DRAG_NONE) {
|
||||||
if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) {
|
if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) {
|
||||||
@ -472,6 +694,10 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
|
|
||||||
if (DemodulatorInstance *last = wxGetApp().getDemodMgr().getLastActiveDemodulator()) {
|
if (DemodulatorInstance *last = wxGetApp().getDemodMgr().getLastActiveDemodulator()) {
|
||||||
demod->getParams().bandwidth = last->getParams().bandwidth;
|
demod->getParams().bandwidth = last->getParams().bandwidth;
|
||||||
|
demod->setDemodulatorType(last->getDemodulatorType());
|
||||||
|
demod->setSquelchLevel(last->getSquelchLevel());
|
||||||
|
demod->setSquelchEnabled(last->isSquelchEnabled());
|
||||||
|
demod->setStereo(last->isStereo());
|
||||||
}
|
}
|
||||||
|
|
||||||
demod->run();
|
demod->run();
|
||||||
@ -492,9 +718,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
command.int_value = freq;
|
command.int_value = freq;
|
||||||
demod->getCommandQueue()->push(command);
|
demod->getCommandQueue()->push(command);
|
||||||
|
|
||||||
((wxFrame*) parent)->GetStatusBar()->SetStatusText(
|
setStatusText("New demodulator at frequency: %s", freq);
|
||||||
wxString::Format(wxT("Set demodulator frequency: %s"),
|
|
||||||
wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep)));
|
|
||||||
|
|
||||||
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false);
|
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false);
|
||||||
SetCursor(wxCURSOR_SIZING);
|
SetCursor(wxCURSOR_SIZING);
|
||||||
@ -502,10 +726,6 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
mTracker.setVertDragLock(true);
|
mTracker.setVertDragLock(true);
|
||||||
mTracker.setHorizDragLock(false);
|
mTracker.setHorizDragLock(false);
|
||||||
} else {
|
} else {
|
||||||
float pos = mTracker.getMouseX();
|
|
||||||
int center_freq = wxGetApp().getFrequency();
|
|
||||||
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE);
|
|
||||||
|
|
||||||
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false);
|
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false);
|
||||||
nextDragState = WF_DRAG_FREQUENCY;
|
nextDragState = WF_DRAG_FREQUENCY;
|
||||||
}
|
}
|
||||||
@ -513,15 +733,15 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
float width = mTracker.getOriginDeltaMouseX();
|
float width = mTracker.getOriginDeltaMouseX();
|
||||||
float pos = mTracker.getOriginMouseX() + width / 2.0;
|
float pos = mTracker.getOriginMouseX() + width / 2.0;
|
||||||
|
|
||||||
int center_freq = wxGetApp().getFrequency();
|
int input_center_freq = GetCenterFrequency();
|
||||||
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE);
|
unsigned int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth());
|
||||||
int bandwidth = (int) (fabs(width) * (float) SRATE);
|
unsigned int bw = (unsigned int) (fabs(width) * (float) GetBandwidth());
|
||||||
|
|
||||||
if (bandwidth < 2000) {
|
if (bw < MIN_BANDWIDTH) {
|
||||||
bandwidth = 2000;
|
bw = MIN_BANDWIDTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bandwidth) {
|
if (!bw) {
|
||||||
dragState = WF_DRAG_NONE;
|
dragState = WF_DRAG_NONE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -531,8 +751,13 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
} else {
|
} else {
|
||||||
demod = wxGetApp().getDemodMgr().newThread();
|
demod = wxGetApp().getDemodMgr().newThread();
|
||||||
demod->getParams().frequency = freq;
|
demod->getParams().frequency = freq;
|
||||||
demod->getParams().bandwidth = bandwidth;
|
demod->getParams().bandwidth = bw;
|
||||||
|
if (DemodulatorInstance *last = wxGetApp().getDemodMgr().getLastActiveDemodulator()) {
|
||||||
|
demod->setDemodulatorType(last->getDemodulatorType());
|
||||||
|
demod->setSquelchLevel(last->getSquelchLevel());
|
||||||
|
demod->setSquelchEnabled(last->isSquelchEnabled());
|
||||||
|
demod->setStereo(last->isStereo());
|
||||||
|
}
|
||||||
demod->run();
|
demod->run();
|
||||||
|
|
||||||
wxGetApp().bindDemodulator(demod);
|
wxGetApp().bindDemodulator(demod);
|
||||||
@ -544,9 +769,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
((wxFrame*) parent)->GetStatusBar()->SetStatusText(
|
setStatusText("New demodulator at frequency: %s", freq);
|
||||||
wxString::Format(wxT("Set demodulator frequency: %s"),
|
|
||||||
wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep)));
|
|
||||||
|
|
||||||
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false);
|
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false);
|
||||||
demod->updateLabel(freq);
|
demod->updateLabel(freq);
|
||||||
@ -556,7 +779,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
command.int_value = freq;
|
command.int_value = freq;
|
||||||
demod->getCommandQueue()->push(command);
|
demod->getCommandQueue()->push(command);
|
||||||
command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH;
|
command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH;
|
||||||
command.int_value = bandwidth;
|
command.int_value = bw;
|
||||||
demod->getCommandQueue()->push(command);
|
demod->getCommandQueue()->push(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,12 +787,13 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) {
|
void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseLeftWindow(event);
|
InteractiveCanvas::mouseLeftWindow(event);
|
||||||
SetCursor(wxCURSOR_CROSS);
|
SetCursor(wxCURSOR_CROSS);
|
||||||
wxGetApp().getDemodMgr().setActiveDemodulator(NULL);
|
wxGetApp().getDemodMgr().setActiveDemodulator(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) {
|
void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) {
|
||||||
mTracker.OnMouseEnterWindow(event);
|
InteractiveCanvas::mouseEnterWindow(event);
|
||||||
SetCursor(wxCURSOR_CROSS);
|
SetCursor(wxCURSOR_CROSS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,23 +6,29 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
#include "InteractiveCanvas.h"
|
||||||
#include "WaterfallContext.h"
|
#include "WaterfallContext.h"
|
||||||
#include "MouseTracker.h"
|
#include "MouseTracker.h"
|
||||||
|
#include "SpectrumCanvas.h"
|
||||||
|
|
||||||
#include "fftw3.h"
|
#include "fftw3.h"
|
||||||
#include "Timer.h"
|
|
||||||
|
|
||||||
class WaterfallCanvas: public wxGLCanvas {
|
class WaterfallCanvas: public InteractiveCanvas {
|
||||||
public:
|
public:
|
||||||
enum DragState {
|
enum DragState {
|
||||||
WF_DRAG_NONE, WF_DRAG_BANDWIDTH_LEFT, WF_DRAG_BANDWIDTH_RIGHT, WF_DRAG_FREQUENCY, WF_DRAG_RANGE
|
WF_DRAG_NONE, WF_DRAG_BANDWIDTH_LEFT, WF_DRAG_BANDWIDTH_RIGHT, WF_DRAG_FREQUENCY, WF_DRAG_RANGE
|
||||||
};
|
};
|
||||||
|
|
||||||
WaterfallCanvas(wxWindow *parent, int *attribList = NULL);
|
WaterfallCanvas(wxWindow *parent, int *attribList = NULL);
|
||||||
|
void Setup(int fft_size_in, int waterfall_lines_in);
|
||||||
~WaterfallCanvas();
|
~WaterfallCanvas();
|
||||||
|
|
||||||
void setData(std::vector<liquid_float_complex> *data);
|
void setData(DemodulatorThreadIQData *input);
|
||||||
int GetFrequencyAt(float x);
|
|
||||||
|
DragState getDragState();
|
||||||
|
DragState getNextDragState();
|
||||||
|
|
||||||
|
void attachSpectrumCanvas(SpectrumCanvas *canvas_in);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnPaint(wxPaintEvent& event);
|
void OnPaint(wxPaintEvent& event);
|
||||||
@ -38,23 +44,20 @@ private:
|
|||||||
void mouseEnterWindow(wxMouseEvent& event);
|
void mouseEnterWindow(wxMouseEvent& event);
|
||||||
void mouseLeftWindow(wxMouseEvent& event);
|
void mouseLeftWindow(wxMouseEvent& event);
|
||||||
|
|
||||||
wxWindow *parent;
|
SpectrumCanvas *spectrumCanvas;
|
||||||
std::vector<float> spectrum_points;
|
std::vector<float> spectrum_points;
|
||||||
|
|
||||||
fftw_complex *in, *out;
|
fftw_complex *in, *out;
|
||||||
fftw_plan plan;
|
fftw_plan plan;
|
||||||
|
|
||||||
float fft_ceil_ma, fft_ceil_maa;
|
double fft_ceil_ma, fft_ceil_maa;
|
||||||
float fft_floor_ma, fft_floor_maa;
|
double fft_floor_ma, fft_floor_maa;
|
||||||
|
|
||||||
std::vector<float> fft_result;
|
std::vector<double> fft_result;
|
||||||
std::vector<float> fft_result_ma;
|
std::vector<double> fft_result_ma;
|
||||||
std::vector<float> fft_result_maa;
|
std::vector<double> fft_result_maa;
|
||||||
|
|
||||||
WaterfallContext *glContext;
|
WaterfallContext *glContext;
|
||||||
Timer timer;
|
|
||||||
float frameTimer;
|
|
||||||
MouseTracker mTracker;
|
|
||||||
|
|
||||||
int activeDemodulatorBandwidth;
|
int activeDemodulatorBandwidth;
|
||||||
int activeDemodulatorFrequency;
|
int activeDemodulatorFrequency;
|
||||||
@ -62,7 +65,20 @@ private:
|
|||||||
DragState dragState;
|
DragState dragState;
|
||||||
DragState nextDragState;
|
DragState nextDragState;
|
||||||
|
|
||||||
bool shiftDown;bool altDown;bool ctrlDown;
|
int fft_size;
|
||||||
|
int waterfall_lines;
|
||||||
|
|
||||||
|
msresamp_crcf resampler;
|
||||||
|
double resample_ratio;
|
||||||
|
nco_crcf nco_shift;
|
||||||
|
int shift_freq;
|
||||||
|
|
||||||
|
int last_input_bandwidth;
|
||||||
|
int zoom;
|
||||||
|
|
||||||
|
std::vector<liquid_float_complex> shift_buffer;
|
||||||
|
std::vector<liquid_float_complex> resampler_buffer;
|
||||||
|
|
||||||
// event table
|
// event table
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,31 @@
|
|||||||
#include "CubicSDR.h"
|
#include "CubicSDR.h"
|
||||||
|
|
||||||
WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext) :
|
WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext) :
|
||||||
PrimaryGLContext(canvas, sharedContext) {
|
PrimaryGLContext(canvas, sharedContext), waterfall(0), waterfall_tex(NULL) {
|
||||||
|
grad.addColor(GradientColor(0, 0, 0));
|
||||||
|
grad.addColor(GradientColor(0, 0, 1.0));
|
||||||
|
grad.addColor(GradientColor(0, 1.0, 0));
|
||||||
|
grad.addColor(GradientColor(1.0, 1.0, 0));
|
||||||
|
grad.addColor(GradientColor(1.0, 0.2, 0.0));
|
||||||
|
|
||||||
|
grad.generate(256);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaterfallContext::Setup(int fft_size_in, int num_waterfall_lines_in) {
|
||||||
|
if (waterfall) {
|
||||||
|
glDeleteTextures(1, &waterfall);
|
||||||
|
waterfall = 0;
|
||||||
|
}
|
||||||
|
if (waterfall_tex) {
|
||||||
|
delete waterfall_tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
waterfall_lines = num_waterfall_lines_in;
|
||||||
|
fft_size = fft_size_in;
|
||||||
|
|
||||||
|
waterfall_tex = new unsigned char[fft_size * waterfall_lines];
|
||||||
|
memset(waterfall_tex,0,fft_size * waterfall_lines);
|
||||||
|
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
@ -23,26 +47,19 @@ WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedC
|
|||||||
|
|
||||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
||||||
|
|
||||||
grad.addColor(GradientColor(0, 0, 0));
|
|
||||||
grad.addColor(GradientColor(0, 0, 1.0));
|
|
||||||
grad.addColor(GradientColor(0, 1.0, 0));
|
|
||||||
grad.addColor(GradientColor(1.0, 1.0, 0));
|
|
||||||
grad.addColor(GradientColor(1.0, 0.2, 0.0));
|
|
||||||
|
|
||||||
grad.generate(256);
|
|
||||||
|
|
||||||
glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
|
glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
|
||||||
glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, &(grad.getRed())[0]);
|
glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, &(grad.getRed())[0]);
|
||||||
glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 256, &(grad.getGreen())[0]);
|
glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 256, &(grad.getGreen())[0]);
|
||||||
glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, &(grad.getBlue())[0]);
|
glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, &(grad.getBlue())[0]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallContext::Draw(std::vector<float> &points) {
|
void WaterfallContext::Draw(std::vector<float> &points) {
|
||||||
|
|
||||||
if (points.size()) {
|
if (points.size()) {
|
||||||
memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE);
|
memmove(waterfall_tex + fft_size, waterfall_tex, (waterfall_lines - 1) * fft_size);
|
||||||
|
|
||||||
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) {
|
for (int i = 0, iMax = fft_size; i < iMax; i++) {
|
||||||
float v = points[i * 2 + 1];
|
float v = points[i * 2 + 1];
|
||||||
|
|
||||||
float wv = v;
|
float wv = v;
|
||||||
@ -57,7 +74,7 @@ void WaterfallContext::Draw(std::vector<float> &points) {
|
|||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, waterfall);
|
glBindTexture(GL_TEXTURE_2D, waterfall);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex);
|
||||||
|
|
||||||
glColor3f(1.0, 1.0, 1.0);
|
glColor3f(1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
#include "PrimaryGLContext.h"
|
#include "PrimaryGLContext.h"
|
||||||
#include "Gradient.h"
|
#include "Gradient.h"
|
||||||
|
|
||||||
#define NUM_WATERFALL_LINES 512
|
|
||||||
|
|
||||||
class WaterfallCanvas;
|
class WaterfallCanvas;
|
||||||
|
|
||||||
class WaterfallContext: public PrimaryGLContext {
|
class WaterfallContext: public PrimaryGLContext {
|
||||||
@ -12,9 +10,12 @@ public:
|
|||||||
WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext);
|
WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext);
|
||||||
|
|
||||||
void Draw(std::vector<float> &points);
|
void Draw(std::vector<float> &points);
|
||||||
|
void Setup(int fft_size_in, int num_waterfall_lines_in);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Gradient grad;
|
Gradient grad;
|
||||||
GLuint waterfall;
|
GLuint waterfall;
|
||||||
unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES];
|
unsigned char *waterfall_tex;
|
||||||
|
int fft_size;
|
||||||
|
int waterfall_lines;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user