mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-11-10 04:37:37 +01:00
Fixed bugs + new radio
This commit is contained in:
parent
9b8c1a3072
commit
f4f8c77ffa
138
core/src/dsp/convertion.h
Normal file
138
core/src/dsp/convertion.h
Normal file
@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
#include <dsp/block.h>
|
||||
|
||||
namespace dsp {
|
||||
class ComplexToStereo : public generic_block<ComplexToStereo> {
|
||||
public:
|
||||
ComplexToStereo() {}
|
||||
|
||||
ComplexToStereo(stream<complex_t>* in) { init(in); }
|
||||
|
||||
~ComplexToStereo() { generic_block<ComplexToStereo>::stop(); }
|
||||
|
||||
static_assert(sizeof(complex_t) == sizeof(stereo_t));
|
||||
|
||||
void init(stream<complex_t>* in) {
|
||||
_in = in;
|
||||
generic_block<ComplexToStereo>::registerInput(_in);
|
||||
generic_block<ComplexToStereo>::registerOutput(&out);
|
||||
}
|
||||
|
||||
void setInput(stream<complex_t>* in) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<ComplexToStereo>::ctrlMtx);
|
||||
generic_block<ComplexToStereo>::tempStop();
|
||||
generic_block<ComplexToStereo>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<ComplexToStereo>::registerInput(_in);
|
||||
generic_block<ComplexToStereo>::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
memcpy(out.data, _in->data, count * sizeof(complex_t));
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
stream<stereo_t> out;
|
||||
|
||||
private:
|
||||
float avg;
|
||||
int count;
|
||||
stream<complex_t>* _in;
|
||||
|
||||
};
|
||||
|
||||
class ComplexToReal : public generic_block<ComplexToReal> {
|
||||
public:
|
||||
ComplexToReal() {}
|
||||
|
||||
ComplexToReal(stream<complex_t>* in) { init(in); }
|
||||
|
||||
~ComplexToReal() { generic_block<ComplexToReal>::stop(); }
|
||||
|
||||
void init(stream<complex_t>* in) {
|
||||
_in = in;
|
||||
generic_block<ComplexToReal>::registerInput(_in);
|
||||
generic_block<ComplexToReal>::registerOutput(&out);
|
||||
}
|
||||
|
||||
void setInput(stream<complex_t>* in) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<ComplexToReal>::ctrlMtx);
|
||||
generic_block<ComplexToReal>::tempStop();
|
||||
generic_block<ComplexToReal>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<ComplexToReal>::registerInput(_in);
|
||||
generic_block<ComplexToReal>::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
volk_32fc_deinterleave_real_32f(out.data, (lv_32fc_t*)_in->data, count);
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
stream<float> out;
|
||||
|
||||
private:
|
||||
float avg;
|
||||
int count;
|
||||
stream<complex_t>* _in;
|
||||
|
||||
};
|
||||
|
||||
class ComplexToImag : public generic_block<ComplexToImag> {
|
||||
public:
|
||||
ComplexToImag() {}
|
||||
|
||||
ComplexToImag(stream<complex_t>* in) { init(in); }
|
||||
|
||||
~ComplexToImag() { generic_block<ComplexToImag>::stop(); }
|
||||
|
||||
void init(stream<complex_t>* in) {
|
||||
_in = in;
|
||||
generic_block<ComplexToImag>::registerInput(_in);
|
||||
generic_block<ComplexToImag>::registerOutput(&out);
|
||||
}
|
||||
|
||||
void setInput(stream<complex_t>* in) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<ComplexToImag>::ctrlMtx);
|
||||
generic_block<ComplexToImag>::tempStop();
|
||||
generic_block<ComplexToImag>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<ComplexToImag>::registerInput(_in);
|
||||
generic_block<ComplexToImag>::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
volk_32fc_deinterleave_imag_32f(out.data, (lv_32fc_t*)_in->data, count);
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
stream<float> out;
|
||||
|
||||
private:
|
||||
float avg;
|
||||
int count;
|
||||
stream<complex_t>* _in;
|
||||
|
||||
};
|
||||
}
|
@ -5,13 +5,16 @@
|
||||
#include <string.h>
|
||||
|
||||
namespace dsp {
|
||||
class FrequencyXlator : public generic_block<FrequencyXlator> {
|
||||
template <class T>
|
||||
class FrequencyXlator : public generic_block<FrequencyXlator<T>> {
|
||||
public:
|
||||
FrequencyXlator() {}
|
||||
|
||||
FrequencyXlator(stream<complex_t>* in, float sampleRate, float freq) { init(in, sampleRate, freq); }
|
||||
|
||||
~FrequencyXlator() { generic_block<FrequencyXlator>::stop(); }
|
||||
~FrequencyXlator() {
|
||||
generic_block<FrequencyXlator<T>>::stop();
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, float sampleRate, float freq) {
|
||||
_in = in;
|
||||
@ -19,17 +22,17 @@ namespace dsp {
|
||||
_freq = freq;
|
||||
phase = lv_cmake(1.0f, 0.0f);
|
||||
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
||||
generic_block<FrequencyXlator>::registerInput(_in);
|
||||
generic_block<FrequencyXlator>::registerOutput(&out);
|
||||
generic_block<FrequencyXlator<T>>::registerInput(_in);
|
||||
generic_block<FrequencyXlator<T>>::registerOutput(&out);
|
||||
}
|
||||
|
||||
void setInputSize(stream<complex_t>* in) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<FrequencyXlator>::ctrlMtx);
|
||||
generic_block<FrequencyXlator>::tempStop();
|
||||
generic_block<FrequencyXlator>::unregisterInput(_in);
|
||||
std::lock_guard<std::mutex> lck(generic_block<FrequencyXlator<T>>::ctrlMtx);
|
||||
generic_block<FrequencyXlator<T>>::tempStop();
|
||||
generic_block<FrequencyXlator<T>>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<FrequencyXlator>::registerInput(_in);
|
||||
generic_block<FrequencyXlator>::tempStart();
|
||||
generic_block<FrequencyXlator<T>>::registerInput(_in);
|
||||
generic_block<FrequencyXlator<T>>::tempStart();
|
||||
}
|
||||
|
||||
void setSampleRate(float sampleRate) {
|
||||
@ -58,7 +61,13 @@ namespace dsp {
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.data, (lv_32fc_t*)_in->data, phaseDelta, &phase, count);
|
||||
// TODO: Do float xlation
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
spdlog::error("XLATOR NOT IMPLEMENTED FOR FLOAT");
|
||||
}
|
||||
if constexpr (std::is_same_v<T, complex_t>) {
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.data, (lv_32fc_t*)_in->data, phaseDelta, &phase, count);
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
|
@ -105,7 +105,7 @@ namespace dsp {
|
||||
float _offset, _inSampleRate, _outSampleRate, _bandWidth;
|
||||
filter_window::BlackmanWindow win;
|
||||
stream<complex_t>* _in;
|
||||
FrequencyXlator xlator;
|
||||
FrequencyXlator<complex_t> xlator;
|
||||
PolyphaseResampler<complex_t> resamp;
|
||||
|
||||
};
|
||||
|
@ -62,7 +62,9 @@
|
||||
#if !defined(DUK_CONFIG_H_INCLUDED)
|
||||
#define DUK_CONFIG_H_INCLUDED
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DUK_USE_DATE_NOW_WINDOWS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Intermediate helper defines
|
||||
|
@ -125,13 +125,14 @@ void windowInit() {
|
||||
// Add "select folder" option for the file source
|
||||
// Fix SSB demod
|
||||
// FIX AUDIO ISSUE ON BOTH LINUX AND SOMETIMES WINDOWS (probly the ring buffer, though double buffering could help)
|
||||
// Rewrite radio module with CW and RAW modes
|
||||
// Add CW mode to radio module
|
||||
// Add default main config to avoid having to ship one
|
||||
// Have a good directory system on both linux and windows
|
||||
// Switch to double buffering
|
||||
|
||||
// TODO for 0.2.6
|
||||
// And a module add/remove/change order menu
|
||||
// Add a module add/remove/change order menu
|
||||
// Change the way fft samples are stored to make it less CPU intensive
|
||||
|
||||
// Update UI settings
|
||||
LoadingScreen::show("Loading configuration");
|
||||
|
140
radio/src/am_demod.h
Normal file
140
radio/src/am_demod.h
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
#include <radio_demod.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
class AMDemodulator : public Demodulator {
|
||||
public:
|
||||
AMDemodulator() {}
|
||||
AMDemodulator(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
init(prefix, vfo, audioSampleRate, bandWidth);
|
||||
}
|
||||
|
||||
void init(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
uiPrefix = prefix;
|
||||
_vfo = vfo;
|
||||
audioSampRate = audioSampleRate;
|
||||
bw = bandWidth;
|
||||
|
||||
demod.init(_vfo->output);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
resamp.init(&agc.out, &win, bbSampRate, audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
resamp.updateWindow(&win);
|
||||
|
||||
m2s.init(&resamp.out);
|
||||
}
|
||||
|
||||
void start() {
|
||||
demod.start();
|
||||
agc.start();
|
||||
resamp.start();
|
||||
m2s.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
demod.stop();
|
||||
agc.stop();
|
||||
resamp.stop();
|
||||
m2s.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
void select() {
|
||||
_vfo->setSampleRate(bbSampRate, bw);
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
_vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
}
|
||||
|
||||
void setVFO(VFOManager::VFO* vfo) {
|
||||
_vfo = vfo;
|
||||
demod.setInput(_vfo->output);
|
||||
}
|
||||
|
||||
VFOManager::VFO* getVFO() {
|
||||
return _vfo;
|
||||
}
|
||||
|
||||
void setAudioSampleRate(float sampleRate) {
|
||||
if (running) {
|
||||
resamp.stop();
|
||||
}
|
||||
audioSampRate = sampleRate;
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
resamp.setOutSampleRate(audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
if (running) {
|
||||
resamp.start();
|
||||
}
|
||||
}
|
||||
|
||||
float getAudioSampleRate() {
|
||||
return audioSampRate;
|
||||
}
|
||||
|
||||
dsp::stream<dsp::stereo_t>* getOutput() {
|
||||
return &m2s.out;
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::InputFloat(("##_radio_am_bw_" + uiPrefix).c_str(), &bw, 1, 100, 0)) {
|
||||
bw = std::clamp<float>(bw, bwMin, bwMax);
|
||||
setBandwidth(bw);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("Snap Interval").x - 8);
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::InputFloat(("##_radio_am_snap_" + uiPrefix).c_str(), &snapInterval, 1, 100, 0)) {
|
||||
setSnapInterval(snapInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
snapInterval = snapInt;
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
const float bwMax = 15000;
|
||||
const float bwMin = 6000;
|
||||
const float bbSampRate = 15000;
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 1000;
|
||||
float audioSampRate = 48000;
|
||||
float bw = 12500;
|
||||
bool running = false;
|
||||
|
||||
VFOManager::VFO* _vfo;
|
||||
dsp::AMDemod demod;
|
||||
dsp::AGC agc;
|
||||
dsp::filter_window::BlackmanWindow win;
|
||||
dsp::PolyphaseResampler<float> resamp;
|
||||
dsp::MonoToStereo m2s;
|
||||
|
||||
};
|
151
radio/src/cw_demod.h
Normal file
151
radio/src/cw_demod.h
Normal file
@ -0,0 +1,151 @@
|
||||
#pragma once
|
||||
#include <radio_demod.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/convertion.h>
|
||||
#include <dsp/processing.h>
|
||||
#include <dsp/math.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
class CWDemodulator : public Demodulator {
|
||||
public:
|
||||
CWDemodulator() {}
|
||||
CWDemodulator(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
init(prefix, vfo, audioSampleRate, bandWidth);
|
||||
}
|
||||
|
||||
void init(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
uiPrefix = prefix;
|
||||
_vfo = vfo;
|
||||
audioSampRate = audioSampleRate;
|
||||
bw = bandWidth;
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
resamp.init(vfo->output, &win, bbSampRate, audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
resamp.updateWindow(&win);
|
||||
|
||||
xlator.init(&resamp.out, audioSampleRate, 1000);
|
||||
|
||||
c2r.init(&xlator.out);
|
||||
|
||||
agc.init(&c2r.out, 1.0f / 125.0f);
|
||||
|
||||
m2s.init(&agc.out);
|
||||
}
|
||||
|
||||
void start() {
|
||||
resamp.start();
|
||||
xlator.start();
|
||||
c2r.start();
|
||||
agc.start();
|
||||
m2s.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
resamp.stop();
|
||||
xlator.stop();
|
||||
c2r.stop();
|
||||
agc.stop();
|
||||
m2s.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
void select() {
|
||||
_vfo->setSampleRate(bbSampRate, bw);
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
_vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
}
|
||||
|
||||
void setVFO(VFOManager::VFO* vfo) {
|
||||
_vfo = vfo;
|
||||
resamp.setInput(_vfo->output);
|
||||
}
|
||||
|
||||
VFOManager::VFO* getVFO() {
|
||||
return _vfo;
|
||||
}
|
||||
|
||||
void setAudioSampleRate(float sampleRate) {
|
||||
if (running) {
|
||||
resamp.stop();
|
||||
xlator.stop();
|
||||
}
|
||||
audioSampRate = sampleRate;
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
resamp.setOutSampleRate(audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
xlator.setSampleRate(audioSampRate);
|
||||
if (running) {
|
||||
resamp.start();
|
||||
xlator.start();
|
||||
}
|
||||
}
|
||||
|
||||
float getAudioSampleRate() {
|
||||
return audioSampRate;
|
||||
}
|
||||
|
||||
dsp::stream<dsp::stereo_t>* getOutput() {
|
||||
return &m2s.out;
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::InputFloat(("##_radio_cw_bw_" + uiPrefix).c_str(), &bw, 1, 100, 0)) {
|
||||
bw = std::clamp<float>(bw, bwMin, bwMax);
|
||||
setBandwidth(bw);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("Snap Interval").x - 8);
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::InputFloat(("##_radio_cw_snap_" + uiPrefix).c_str(), &snapInterval, 1, 100, 0)) {
|
||||
setSnapInterval(snapInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
snapInterval = snapInt;
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
const float bwMax = 500;
|
||||
const float bwMin = 100;
|
||||
const float bbSampRate = 500;
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 10;
|
||||
float audioSampRate = 48000;
|
||||
float bw = 200;
|
||||
bool running = false;
|
||||
|
||||
VFOManager::VFO* _vfo;
|
||||
dsp::filter_window::BlackmanWindow win;
|
||||
dsp::PolyphaseResampler<dsp::complex_t> resamp;
|
||||
dsp::FrequencyXlator<dsp::complex_t> xlator;
|
||||
dsp::ComplexToReal c2r;
|
||||
dsp::AGC agc;
|
||||
dsp::MonoToStereo m2s;
|
||||
|
||||
};
|
140
radio/src/dsb_demod.h
Normal file
140
radio/src/dsb_demod.h
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
#include <radio_demod.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
class DSBDemodulator : public Demodulator {
|
||||
public:
|
||||
DSBDemodulator() {}
|
||||
DSBDemodulator(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
init(prefix, vfo, audioSampleRate, bandWidth);
|
||||
}
|
||||
|
||||
void init(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
uiPrefix = prefix;
|
||||
_vfo = vfo;
|
||||
audioSampRate = audioSampleRate;
|
||||
bw = bandWidth;
|
||||
|
||||
demod.init(_vfo->output, bbSampRate, bandWidth, dsp::SSBDemod::MODE_DSB);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
resamp.init(&agc.out, &win, bbSampRate, audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
resamp.updateWindow(&win);
|
||||
|
||||
m2s.init(&resamp.out);
|
||||
}
|
||||
|
||||
void start() {
|
||||
demod.start();
|
||||
agc.start();
|
||||
resamp.start();
|
||||
m2s.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
demod.stop();
|
||||
agc.stop();
|
||||
resamp.stop();
|
||||
m2s.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
void select() {
|
||||
_vfo->setSampleRate(bbSampRate, bw);
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
_vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
}
|
||||
|
||||
void setVFO(VFOManager::VFO* vfo) {
|
||||
_vfo = vfo;
|
||||
demod.setInput(_vfo->output);
|
||||
}
|
||||
|
||||
VFOManager::VFO* getVFO() {
|
||||
return _vfo;
|
||||
}
|
||||
|
||||
void setAudioSampleRate(float sampleRate) {
|
||||
if (running) {
|
||||
resamp.stop();
|
||||
}
|
||||
audioSampRate = sampleRate;
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
resamp.setOutSampleRate(audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
if (running) {
|
||||
resamp.start();
|
||||
}
|
||||
}
|
||||
|
||||
float getAudioSampleRate() {
|
||||
return audioSampRate;
|
||||
}
|
||||
|
||||
dsp::stream<dsp::stereo_t>* getOutput() {
|
||||
return &m2s.out;
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::InputFloat(("##_radio_dsb_bw_" + uiPrefix).c_str(), &bw, 1, 100, 0)) {
|
||||
bw = std::clamp<float>(bw, bwMin, bwMax);
|
||||
setBandwidth(bw);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("Snap Interval").x - 8);
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::InputFloat(("##_radio_dsb_snap_" + uiPrefix).c_str(), &snapInterval, 1, 100, 0)) {
|
||||
setSnapInterval(snapInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
snapInterval = snapInt;
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
const float bwMax = 12000;
|
||||
const float bwMin = 1000;
|
||||
const float bbSampRate = 12000;
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 100;
|
||||
float audioSampRate = 48000;
|
||||
float bw = 6000;
|
||||
bool running = false;
|
||||
|
||||
VFOManager::VFO* _vfo;
|
||||
dsp::SSBDemod demod;
|
||||
dsp::AGC agc;
|
||||
dsp::filter_window::BlackmanWindow win;
|
||||
dsp::PolyphaseResampler<float> resamp;
|
||||
dsp::MonoToStereo m2s;
|
||||
|
||||
};
|
136
radio/src/fm_demod.h
Normal file
136
radio/src/fm_demod.h
Normal file
@ -0,0 +1,136 @@
|
||||
#pragma once
|
||||
#include <radio_demod.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
class FMDemodulator : public Demodulator {
|
||||
public:
|
||||
FMDemodulator() {}
|
||||
FMDemodulator(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
init(prefix, vfo, audioSampleRate, bandWidth);
|
||||
}
|
||||
|
||||
void init(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
uiPrefix = prefix;
|
||||
_vfo = vfo;
|
||||
audioSampRate = audioSampleRate;
|
||||
bw = bandWidth;
|
||||
|
||||
demod.init(_vfo->output, bbSampRate, bandWidth / 2.0f);
|
||||
|
||||
float audioBW = std::min<float>(audioSampleRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
resamp.init(&demod.out, &win, bbSampRate, audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
resamp.updateWindow(&win);
|
||||
|
||||
m2s.init(&resamp.out);
|
||||
}
|
||||
|
||||
void start() {
|
||||
demod.start();
|
||||
resamp.start();
|
||||
m2s.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
demod.stop();
|
||||
resamp.stop();
|
||||
m2s.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
void select() {
|
||||
_vfo->setSampleRate(bbSampRate, bw);
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
_vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
}
|
||||
|
||||
void setVFO(VFOManager::VFO* vfo) {
|
||||
_vfo = vfo;
|
||||
demod.setInput(_vfo->output);
|
||||
}
|
||||
|
||||
VFOManager::VFO* getVFO() {
|
||||
return _vfo;
|
||||
}
|
||||
|
||||
void setAudioSampleRate(float sampleRate) {
|
||||
if (running) {
|
||||
resamp.stop();
|
||||
}
|
||||
audioSampRate = sampleRate;
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, 16000.0f);
|
||||
resamp.setOutSampleRate(audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
if (running) {
|
||||
resamp.start();
|
||||
}
|
||||
}
|
||||
|
||||
float getAudioSampleRate() {
|
||||
return audioSampRate;
|
||||
}
|
||||
|
||||
dsp::stream<dsp::stereo_t>* getOutput() {
|
||||
return &m2s.out;
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::InputFloat(("##_radio_fm_bw_" + uiPrefix).c_str(), &bw, 1, 100, 0)) {
|
||||
bw = std::clamp<float>(bw, bwMin, bwMax);
|
||||
setBandwidth(bw);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("Snap Interval").x - 8);
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::InputFloat(("##_radio_fm_snap_" + uiPrefix).c_str(), &snapInterval, 1, 100, 0)) {
|
||||
setSnapInterval(snapInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
demod.setDeviation(bw / 2.0f);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
snapInterval = snapInt;
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
const float bwMax = 15000;
|
||||
const float bwMin = 6000;
|
||||
const float bbSampRate = 12500;
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 10000;
|
||||
float audioSampRate = 48000;
|
||||
float bw = 12500;
|
||||
bool running = false;
|
||||
|
||||
VFOManager::VFO* _vfo;
|
||||
dsp::FMDemod demod;
|
||||
dsp::filter_window::BlackmanWindow win;
|
||||
dsp::PolyphaseResampler<float> resamp;
|
||||
dsp::MonoToStereo m2s;
|
||||
|
||||
};
|
140
radio/src/lsb_demod.h
Normal file
140
radio/src/lsb_demod.h
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
#include <radio_demod.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
class LSBDemodulator : public Demodulator {
|
||||
public:
|
||||
LSBDemodulator() {}
|
||||
LSBDemodulator(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
init(prefix, vfo, audioSampleRate, bandWidth);
|
||||
}
|
||||
|
||||
void init(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
uiPrefix = prefix;
|
||||
_vfo = vfo;
|
||||
audioSampRate = audioSampleRate;
|
||||
bw = bandWidth;
|
||||
|
||||
demod.init(_vfo->output, bbSampRate, bandWidth, dsp::SSBDemod::MODE_LSB);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
resamp.init(&agc.out, &win, bbSampRate, audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
resamp.updateWindow(&win);
|
||||
|
||||
m2s.init(&resamp.out);
|
||||
}
|
||||
|
||||
void start() {
|
||||
demod.start();
|
||||
agc.start();
|
||||
resamp.start();
|
||||
m2s.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
demod.stop();
|
||||
agc.stop();
|
||||
resamp.stop();
|
||||
m2s.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
void select() {
|
||||
_vfo->setSampleRate(bbSampRate, bw);
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
_vfo->setReference(ImGui::WaterfallVFO::REF_UPPER);
|
||||
}
|
||||
|
||||
void setVFO(VFOManager::VFO* vfo) {
|
||||
_vfo = vfo;
|
||||
demod.setInput(_vfo->output);
|
||||
}
|
||||
|
||||
VFOManager::VFO* getVFO() {
|
||||
return _vfo;
|
||||
}
|
||||
|
||||
void setAudioSampleRate(float sampleRate) {
|
||||
if (running) {
|
||||
resamp.stop();
|
||||
}
|
||||
audioSampRate = sampleRate;
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
resamp.setOutSampleRate(audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
if (running) {
|
||||
resamp.start();
|
||||
}
|
||||
}
|
||||
|
||||
float getAudioSampleRate() {
|
||||
return audioSampRate;
|
||||
}
|
||||
|
||||
dsp::stream<dsp::stereo_t>* getOutput() {
|
||||
return &m2s.out;
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::InputFloat(("##_radio_lsb_bw_" + uiPrefix).c_str(), &bw, 1, 100, 0)) {
|
||||
bw = std::clamp<float>(bw, bwMin, bwMax);
|
||||
setBandwidth(bw);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("Snap Interval").x - 8);
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::InputFloat(("##_radio_lsb_snap_" + uiPrefix).c_str(), &snapInterval, 1, 100, 0)) {
|
||||
setSnapInterval(snapInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
snapInterval = snapInt;
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
const float bwMax = 3000;
|
||||
const float bwMin = 500;
|
||||
const float bbSampRate = 6000;
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 100;
|
||||
float audioSampRate = 48000;
|
||||
float bw = 3000;
|
||||
bool running = false;
|
||||
|
||||
VFOManager::VFO* _vfo;
|
||||
dsp::SSBDemod demod;
|
||||
dsp::AGC agc;
|
||||
dsp::filter_window::BlackmanWindow win;
|
||||
dsp::PolyphaseResampler<float> resamp;
|
||||
dsp::MonoToStereo m2s;
|
||||
|
||||
};
|
@ -1,336 +1,149 @@
|
||||
#include <imgui.h>
|
||||
#include <module.h>
|
||||
#include <path.h>
|
||||
#include <watcher.h>
|
||||
#include <config.h>
|
||||
#include <core.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <radio_demod.h>
|
||||
#include <wfm_demod.h>
|
||||
#include <fm_demod.h>
|
||||
#include <am_demod.h>
|
||||
#include <usb_demod.h>
|
||||
#include <lsb_demod.h>
|
||||
#include <dsb_demod.h>
|
||||
#include <raw_demod.h>
|
||||
#include <cw_demod.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
#define DEEMP_LIST "50µS\00075µS\000none\000"
|
||||
|
||||
MOD_INFO {
|
||||
/* Name: */ "radio",
|
||||
/* Description: */ "Radio module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ "0.2.5"
|
||||
/* Version: */ "0.3.0"
|
||||
};
|
||||
|
||||
class RadioModule {
|
||||
public:
|
||||
RadioModule(std::string name) {
|
||||
this->name = name;
|
||||
demod = 1;
|
||||
bandWidth = 200000;
|
||||
bandWidthMin = 100000;
|
||||
bandWidthMax = 200000;
|
||||
sigPath.init(name);
|
||||
sigPath.start();
|
||||
sigPath.setDemodulator(SigPath::DEMOD_FM, bandWidth);
|
||||
sigPath.vfo->setSnapInterval(100000.0);
|
||||
gui::menu.registerEntry(name, menuHandler, this);
|
||||
|
||||
ScriptManager::ScriptRunHandler_t handler;
|
||||
handler.ctx = this;
|
||||
handler.handler = scriptCreateHandler;
|
||||
core::scriptManager.bindScriptRunHandler(name, handler);
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 200000, 200000, 1);
|
||||
|
||||
wfmDemod.init(name, vfo, audioSampRate, 200000);
|
||||
fmDemod.init(name, vfo, audioSampRate, 12500);
|
||||
amDemod.init(name, vfo, audioSampRate, 12500);
|
||||
usbDemod.init(name, vfo, audioSampRate, 3000);
|
||||
lsbDemod.init(name, vfo, audioSampRate, 3000);
|
||||
dsbDemod.init(name, vfo, audioSampRate, 6000);
|
||||
rawDemod.init(name, vfo, audioSampRate, audioSampRate);
|
||||
cwDemod.init(name, vfo, audioSampRate, 200);
|
||||
|
||||
srChangeHandler.ctx = this;
|
||||
srChangeHandler.handler = sampleRateChangeHandler;
|
||||
stream.init(wfmDemod.getOutput(), srChangeHandler, audioSampRate);
|
||||
sigpath::sinkManager.registerStream(name, &stream);
|
||||
|
||||
// TODO: Replace with config load
|
||||
demodId = 1;
|
||||
selectDemod(&wfmDemod);
|
||||
|
||||
stream.start();
|
||||
|
||||
gui::menu.registerEntry(name, menuHandler, this);
|
||||
}
|
||||
|
||||
~RadioModule() {
|
||||
// TODO: Implement destructor
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
static void menuHandler(void* ctx) {
|
||||
RadioModule* _this = (RadioModule*)ctx;
|
||||
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// TODO: Change VFO ref in signal path
|
||||
|
||||
ImGui::Columns(4, CONCAT("RadioModeColumns##_", _this->name), false);
|
||||
if (ImGui::RadioButton(CONCAT("NFM##_", _this->name), _this->demod == 0) && _this->demod != 0) {
|
||||
_this->demod = 0;
|
||||
_this->bandWidth = 16000;
|
||||
_this->bandWidthMin = 8000;
|
||||
_this->bandWidthMax = 16000;
|
||||
_this->snapInterval = 10000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_NFM, _this->bandWidth);
|
||||
if (ImGui::RadioButton(CONCAT("NFM##_", _this->name), _this->demodId == 0) && _this->demodId != 0) {
|
||||
_this->demodId = 0;
|
||||
_this->selectDemod(&_this->fmDemod);
|
||||
}
|
||||
if (ImGui::RadioButton(CONCAT("WFM##_", _this->name), _this->demod == 1) && _this->demod != 1) {
|
||||
_this->demod = 1;
|
||||
_this->bandWidth = 200000;
|
||||
_this->bandWidthMin = 100000;
|
||||
_this->bandWidthMax = 200000;
|
||||
_this->snapInterval = 100000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_FM, _this->bandWidth);
|
||||
if (ImGui::RadioButton(CONCAT("WFM##_", _this->name), _this->demodId == 1) && _this->demodId != 1) {
|
||||
_this->demodId = 1;
|
||||
_this->selectDemod(&_this->wfmDemod);
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
if (ImGui::RadioButton(CONCAT("AM##_", _this->name), _this->demod == 2) && _this->demod != 2) {
|
||||
_this->demod = 2;
|
||||
_this->bandWidth = 12500;
|
||||
_this->bandWidthMin = 1500;
|
||||
_this->bandWidthMax = 12500;
|
||||
_this->snapInterval = 1000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_AM, _this->bandWidth);
|
||||
if (ImGui::RadioButton(CONCAT("AM##_", _this->name), _this->demodId == 2) && _this->demodId != 2) {
|
||||
_this->demodId = 2;
|
||||
_this->selectDemod(&_this->amDemod);
|
||||
}
|
||||
if (ImGui::RadioButton(CONCAT("DSB##_", _this->name), _this->demod == 3) && _this->demod != 3) {
|
||||
_this->demod = 3;
|
||||
_this->bandWidth = 6000;
|
||||
_this->bandWidthMin = 3000;
|
||||
_this->bandWidthMax = 6000;
|
||||
_this->snapInterval = 1000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_DSB, _this->bandWidth);
|
||||
if (ImGui::RadioButton(CONCAT("DSB##_", _this->name), _this->demodId == 3) && _this->demodId != 3) {
|
||||
_this->demodId = 3;
|
||||
_this->selectDemod(&_this->dsbDemod);
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
if (ImGui::RadioButton(CONCAT("USB##_", _this->name), _this->demod == 4) && _this->demod != 4) {
|
||||
_this->demod = 4;
|
||||
_this->bandWidth = 3000;
|
||||
_this->bandWidthMin = 1500;
|
||||
_this->bandWidthMax = 3000;
|
||||
_this->snapInterval = 1000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_USB, _this->bandWidth);
|
||||
if (ImGui::RadioButton(CONCAT("USB##_", _this->name), _this->demodId == 4) && _this->demodId != 4) {
|
||||
_this->demodId = 4;
|
||||
_this->selectDemod(&_this->usbDemod);
|
||||
}
|
||||
if (ImGui::RadioButton(CONCAT("CW##_", _this->name), _this->demod == 5) && _this->demod != 5) { _this->demod = 5; };
|
||||
if (ImGui::RadioButton(CONCAT("CW##_", _this->name), _this->demodId == 5) && _this->demodId != 5) {
|
||||
_this->demodId = 5;
|
||||
_this->selectDemod(&_this->cwDemod);
|
||||
};
|
||||
ImGui::NextColumn();
|
||||
if (ImGui::RadioButton(CONCAT("LSB##_", _this->name), _this->demod == 6) && _this->demod != 6) {
|
||||
_this->demod = 6;
|
||||
_this->bandWidth = 3000;
|
||||
_this->bandWidthMin = 1500;
|
||||
_this->bandWidthMax = 3000;
|
||||
_this->snapInterval = 1000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_LSB, _this->bandWidth);
|
||||
if (ImGui::RadioButton(CONCAT("LSB##_", _this->name), _this->demodId == 6) && _this->demodId != 6) {
|
||||
_this->demodId = 6;
|
||||
_this->selectDemod(&_this->lsbDemod);
|
||||
}
|
||||
if (ImGui::RadioButton(CONCAT("RAW##_", _this->name), _this->demod == 7) && _this->demod != 7) {
|
||||
_this->demod = 7;
|
||||
_this->bandWidth = 10000;
|
||||
_this->bandWidthMin = 3000;
|
||||
_this->bandWidthMax = 10000;
|
||||
_this->snapInterval = 1;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_RAW, _this->bandWidth);
|
||||
if (ImGui::RadioButton(CONCAT("RAW##_", _this->name), _this->demodId == 7) && _this->demodId != 7) {
|
||||
_this->demodId = 7;
|
||||
_this->selectDemod(&_this->rawDemod);
|
||||
};
|
||||
ImGui::Columns(1, CONCAT("EndRadioModeColumns##_", _this->name), false);
|
||||
_this->sigPath.vfo->setSnapInterval(_this->snapInterval);
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
if (_this->demod == 1) {
|
||||
ImGui::Text("WFM Deemphasis");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(menuColumnWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::Combo(CONCAT("##_deemp_select_", _this->name), &_this->deemp, DEEMP_LIST)) {
|
||||
_this->sigPath.setDeemphasis(_this->deemp);
|
||||
}
|
||||
}
|
||||
|
||||
_this->currentDemod->showMenu();
|
||||
}
|
||||
|
||||
ImGui::Text("Bandwidth");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(menuColumnWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::InputInt(CONCAT("##_bw_select_", _this->name), &_this->bandWidth, 100, 1000)) {
|
||||
_this->bandWidth = std::clamp<int>(_this->bandWidth, _this->bandWidthMin, _this->bandWidthMax);
|
||||
_this->sigPath.setBandwidth(_this->bandWidth);
|
||||
}
|
||||
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(menuColumnWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::InputDouble(CONCAT("##_snap_select_", _this->name), &_this->snapInterval)) {
|
||||
_this->sigPath.vfo->setSnapInterval(_this->snapInterval);
|
||||
static void sampleRateChangeHandler(float sampleRate, void* ctx) {
|
||||
RadioModule* _this = (RadioModule*)ctx;
|
||||
// TODO: If too slow, change all demods here and not when setting
|
||||
_this->audioSampRate = sampleRate;
|
||||
if (_this->currentDemod != NULL) {
|
||||
_this->currentDemod->setAudioSampleRate(_this->audioSampRate);
|
||||
}
|
||||
}
|
||||
|
||||
static void scriptCreateHandler(void* ctx, duk_context* dukCtx, duk_idx_t objId) {
|
||||
duk_push_string(dukCtx, "Hello from modules ;)");
|
||||
duk_put_prop_string(dukCtx, objId, "test");
|
||||
|
||||
duk_push_c_function(dukCtx, duk_setDemodulator, 1);
|
||||
duk_put_prop_string(dukCtx, objId, "setDemodulator");
|
||||
|
||||
duk_push_c_function(dukCtx, duk_getDemodulator, 0);
|
||||
duk_put_prop_string(dukCtx, objId, "getDemodulator");
|
||||
|
||||
duk_push_c_function(dukCtx, duk_setBandwidth, 1);
|
||||
duk_put_prop_string(dukCtx, objId, "setBandwidth");
|
||||
|
||||
duk_push_c_function(dukCtx, duk_getBandwidth, 0);
|
||||
duk_put_prop_string(dukCtx, objId, "getBandwidth");
|
||||
|
||||
duk_push_c_function(dukCtx, duk_getMaxBandwidth, 0);
|
||||
duk_put_prop_string(dukCtx, objId, "getMaxBandwidth");
|
||||
|
||||
duk_push_c_function(dukCtx, duk_getMinBandwidth, 0);
|
||||
duk_put_prop_string(dukCtx, objId, "getMinBandwidth");
|
||||
|
||||
duk_push_pointer(dukCtx, ctx);
|
||||
duk_put_prop_string(dukCtx, objId, DUK_HIDDEN_SYMBOL("radio_ctx"));
|
||||
}
|
||||
|
||||
static duk_ret_t duk_setDemodulator(duk_context* dukCtx) {
|
||||
const char* str = duk_require_string(dukCtx, -1);
|
||||
std::string modName = str;
|
||||
|
||||
duk_push_this(dukCtx);
|
||||
if (!duk_get_prop_literal(dukCtx, -1, DUK_HIDDEN_SYMBOL("radio_ctx"))) {
|
||||
printf("COULD NOT RETRIEVE POINTER\n");
|
||||
}
|
||||
|
||||
RadioModule* _this = (RadioModule*)duk_require_pointer(dukCtx, -1);
|
||||
|
||||
duk_pop_n(dukCtx, 3); // Pop demod name, this and context
|
||||
|
||||
if (modName == "NFM") {
|
||||
_this->demod = 0;
|
||||
_this->bandWidth = 16000;
|
||||
_this->bandWidthMin = 8000;
|
||||
_this->bandWidthMax = 16000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_NFM, _this->bandWidth);
|
||||
}
|
||||
else if (modName == "WFM") {
|
||||
_this->demod = 1;
|
||||
_this->bandWidth = 200000;
|
||||
_this->bandWidthMin = 100000;
|
||||
_this->bandWidthMax = 200000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_FM, _this->bandWidth);
|
||||
}
|
||||
else if (modName == "AM") {
|
||||
_this->demod = 2;
|
||||
_this->bandWidth = 12500;
|
||||
_this->bandWidthMin = 6250;
|
||||
_this->bandWidthMax = 12500;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_AM, _this->bandWidth);
|
||||
}
|
||||
else if (modName == "DSB") {
|
||||
_this->demod = 3;
|
||||
_this->bandWidth = 6000;
|
||||
_this->bandWidthMin = 3000;
|
||||
_this->bandWidthMax = 6000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_DSB, _this->bandWidth);
|
||||
}
|
||||
else if (modName == "USB") {
|
||||
_this->demod = 4;
|
||||
_this->bandWidth = 3000;
|
||||
_this->bandWidthMin = 1500;
|
||||
_this->bandWidthMax = 3000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_USB, _this->bandWidth);
|
||||
}
|
||||
else if (modName == "CW") { _this->demod = 5; }
|
||||
else if (modName == "LSB") {
|
||||
_this->demod = 6;
|
||||
_this->bandWidth = 3000;
|
||||
_this->bandWidthMin = 1500;
|
||||
_this->bandWidthMax = 3000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_LSB, _this->bandWidth);
|
||||
}
|
||||
else if (modName == "RAW") {
|
||||
_this->demod = 7;
|
||||
_this->bandWidth = 10000;
|
||||
_this->bandWidthMin = 3000;
|
||||
_this->bandWidthMax = 10000;
|
||||
_this->sigPath.setDemodulator(SigPath::DEMOD_RAW, _this->bandWidth);
|
||||
}
|
||||
else {
|
||||
duk_push_string(dukCtx, "Invalid demodulator name");
|
||||
duk_throw(dukCtx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_getDemodulator(duk_context* dukCtx) {
|
||||
duk_push_this(dukCtx);
|
||||
if (!duk_get_prop_literal(dukCtx, -1, DUK_HIDDEN_SYMBOL("radio_ctx"))) {
|
||||
printf("COULD NOT RETRIEVE POINTER\n");
|
||||
}
|
||||
|
||||
RadioModule* _this = (RadioModule*)duk_require_pointer(dukCtx, -1);
|
||||
|
||||
duk_pop_n(dukCtx, 2); // Pop demod name, this and context
|
||||
|
||||
const char* demodNames[] = {"NFM", "WFM", "AM", "DSB", "USB", "CW", "LSB", "RAW"};
|
||||
|
||||
duk_push_string(dukCtx, demodNames[_this->demod]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_setBandwidth(duk_context* dukCtx) {
|
||||
double bandwidth = duk_require_number(dukCtx, -1);
|
||||
|
||||
duk_push_this(dukCtx);
|
||||
if (!duk_get_prop_literal(dukCtx, -1, DUK_HIDDEN_SYMBOL("radio_ctx"))) {
|
||||
printf("COULD NOT RETRIEVE POINTER\n");
|
||||
}
|
||||
|
||||
RadioModule* _this = (RadioModule*)duk_require_pointer(dukCtx, -1);
|
||||
|
||||
duk_pop_n(dukCtx, 3); // Pop demod name, this and context
|
||||
|
||||
if (bandwidth > _this->bandWidthMax || bandwidth < _this->bandWidthMin) {
|
||||
duk_push_string(dukCtx, "Bandwidth out of range");
|
||||
duk_throw(dukCtx);
|
||||
}
|
||||
|
||||
_this->bandWidth = bandwidth;
|
||||
_this->bandWidth = std::clamp<int>(_this->bandWidth, _this->bandWidthMin, _this->bandWidthMax);
|
||||
_this->sigPath.setBandwidth(_this->bandWidth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_getBandwidth(duk_context* dukCtx) {
|
||||
duk_push_this(dukCtx);
|
||||
if (!duk_get_prop_literal(dukCtx, -1, DUK_HIDDEN_SYMBOL("radio_ctx"))) {
|
||||
printf("COULD NOT RETRIEVE POINTER\n");
|
||||
}
|
||||
|
||||
RadioModule* _this = (RadioModule*)duk_require_pointer(dukCtx, -1);
|
||||
|
||||
duk_pop_n(dukCtx, 2); // Pop demod name, this and context
|
||||
|
||||
duk_push_number(dukCtx, _this->bandWidth);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_getMaxBandwidth(duk_context* dukCtx) {
|
||||
duk_push_this(dukCtx);
|
||||
if (!duk_get_prop_literal(dukCtx, -1, DUK_HIDDEN_SYMBOL("radio_ctx"))) {
|
||||
printf("COULD NOT RETRIEVE POINTER\n");
|
||||
}
|
||||
|
||||
RadioModule* _this = (RadioModule*)duk_require_pointer(dukCtx, -1);
|
||||
|
||||
duk_pop_n(dukCtx, 2); // Pop demod name, this and context
|
||||
|
||||
duk_push_number(dukCtx, _this->bandWidthMax);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_getMinBandwidth(duk_context* dukCtx) {
|
||||
duk_push_this(dukCtx);
|
||||
if (!duk_get_prop_literal(dukCtx, -1, DUK_HIDDEN_SYMBOL("radio_ctx"))) {
|
||||
printf("COULD NOT RETRIEVE POINTER\n");
|
||||
}
|
||||
|
||||
RadioModule* _this = (RadioModule*)duk_require_pointer(dukCtx, -1);
|
||||
|
||||
duk_pop_n(dukCtx, 2); // Pop demod name, this and context
|
||||
|
||||
duk_push_number(dukCtx, _this->bandWidthMin);
|
||||
|
||||
return 1;
|
||||
void selectDemod(Demodulator* demod) {
|
||||
if (currentDemod != NULL) { currentDemod->stop(); }
|
||||
currentDemod = demod;
|
||||
currentDemod->setAudioSampleRate(audioSampRate);
|
||||
stream.setInput(currentDemod->getOutput());
|
||||
currentDemod->select();
|
||||
currentDemod->start();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
int demod = 1;
|
||||
int deemp = 0;
|
||||
int bandWidth;
|
||||
int bandWidthMin;
|
||||
int bandWidthMax;
|
||||
double snapInterval = 100000.0;
|
||||
SigPath sigPath;
|
||||
int demodId = 0;
|
||||
float audioSampRate = 48000;
|
||||
Demodulator* currentDemod = NULL;
|
||||
|
||||
VFOManager::VFO* vfo;
|
||||
|
||||
WFMDemodulator wfmDemod;
|
||||
FMDemodulator fmDemod;
|
||||
AMDemodulator amDemod;
|
||||
USBDemodulator usbDemod;
|
||||
LSBDemodulator lsbDemod;
|
||||
DSBDemodulator dsbDemod;
|
||||
RAWDemodulator rawDemod;
|
||||
CWDemodulator cwDemod;
|
||||
|
||||
Event<float>::EventHandler srChangeHandler;
|
||||
SinkManager::Stream stream;
|
||||
|
||||
};
|
||||
|
||||
|
@ -1,298 +0,0 @@
|
||||
#include <path.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
SigPath::SigPath() {
|
||||
|
||||
}
|
||||
|
||||
void SigPath::sampleRateChangeHandler(float _sampleRate, void* ctx) {
|
||||
SigPath* _this = (SigPath*)ctx;
|
||||
_this->outputSampleRate = _sampleRate;
|
||||
_this->audioResamp.stop();
|
||||
_this->deemp.stop();
|
||||
float bw = std::min<float>(_this->bandwidth, _sampleRate / 2.0f);
|
||||
|
||||
|
||||
_this->audioResamp.setOutSampleRate(_sampleRate);
|
||||
_this->audioWin.setSampleRate(_this->demodOutputSamplerate * _this->audioResamp.getInterpolation());
|
||||
_this->audioResamp.updateWindow(&_this->audioWin);
|
||||
|
||||
_this->deemp.setSampleRate(_sampleRate);
|
||||
_this->audioResamp.start();
|
||||
_this->deemp.start();
|
||||
}
|
||||
|
||||
void SigPath::init(std::string vfoName) {
|
||||
this->vfoName = vfoName;
|
||||
|
||||
vfo = sigpath::vfoManager.createVFO(vfoName, ImGui::WaterfallVFO::REF_CENTER, 0, 200000, 200000, 1000);
|
||||
|
||||
_demod = DEMOD_FM;
|
||||
_deemp = DEEMP_50US;
|
||||
bandwidth = 200000;
|
||||
demodOutputSamplerate = 200000;
|
||||
outputSampleRate = 48000;
|
||||
|
||||
// TODO: Set default VFO options
|
||||
// TODO: ajust deemphasis for different output sample rates
|
||||
// TODO: Add a mono to stereo for different modes
|
||||
|
||||
demod.init(vfo->output, 200000, 100000);
|
||||
amDemod.init(vfo->output);
|
||||
ssbDemod.init(vfo->output, 6000, 3000, dsp::SSBDemod::MODE_USB);
|
||||
|
||||
agc.init(&amDemod.out, 1.0f / 125.0f);
|
||||
|
||||
audioWin.init(24000, 24000, 200000);
|
||||
audioResamp.init(&demod.out, &audioWin, 200000, 48000);
|
||||
audioWin.setSampleRate(audioResamp.getInterpolation() * 200000);
|
||||
audioResamp.updateWindow(&audioWin);
|
||||
|
||||
deemp.init(&audioResamp.out, 48000, 50e-6);
|
||||
|
||||
m2s.setInput(&deemp.out);
|
||||
|
||||
Event<float>::EventHandler evHandler;
|
||||
evHandler.handler = sampleRateChangeHandler;
|
||||
evHandler.ctx = this;
|
||||
stream.init(&m2s.out, evHandler, outputSampleRate);
|
||||
|
||||
sigpath::sinkManager.registerStream(vfoName, &stream);
|
||||
|
||||
setDemodulator(_demod, bandwidth);
|
||||
}
|
||||
|
||||
void SigPath::setDemodulator(int demId, float bandWidth) {
|
||||
if (demId < 0 || demId >= _DEMOD_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
audioResamp.stop();
|
||||
deemp.stop();
|
||||
|
||||
bandwidth = bandWidth;
|
||||
|
||||
// Stop current demodulator
|
||||
if (_demod == DEMOD_FM) {
|
||||
demod.stop();
|
||||
}
|
||||
else if (_demod == DEMOD_NFM) {
|
||||
demod.stop();
|
||||
}
|
||||
else if (_demod == DEMOD_AM) {
|
||||
agc.stop();
|
||||
amDemod.stop();
|
||||
}
|
||||
else if (_demod == DEMOD_USB) {
|
||||
agc.stop();
|
||||
ssbDemod.stop();
|
||||
}
|
||||
else if (_demod == DEMOD_LSB) {
|
||||
agc.stop();
|
||||
ssbDemod.stop();
|
||||
}
|
||||
else if (_demod == DEMOD_DSB) {
|
||||
agc.stop();
|
||||
ssbDemod.stop();
|
||||
}
|
||||
else {
|
||||
spdlog::error("UNIMPLEMENTED DEMODULATOR IN SigPath::setDemodulator (stop)");
|
||||
}
|
||||
_demod = demId;
|
||||
|
||||
// Set input of the audio resampler
|
||||
// TODO: Set bandwidth from argument
|
||||
if (demId == DEMOD_FM) {
|
||||
demodOutputSamplerate = 200000;
|
||||
vfo->setSampleRate(200000, bandwidth);
|
||||
demod.setSampleRate(200000);
|
||||
demod.setDeviation(bandwidth / 2.0f);
|
||||
audioResamp.setInput(&demod.out);
|
||||
audioBw = std::min<float>(bandwidth, outputSampleRate / 2.0f);
|
||||
audioBw = std::min<float>(audioBw, 16000.0f);
|
||||
|
||||
audioResamp.setInSampleRate(200000);
|
||||
audioWin.setSampleRate(200000 * audioResamp.getInterpolation());
|
||||
audioWin.setCutoff(audioBw);
|
||||
audioWin.setTransWidth(audioBw);
|
||||
audioResamp.updateWindow(&audioWin);
|
||||
|
||||
deemp.bypass = (_deemp == DEEMP_NONE);
|
||||
vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
demod.start();
|
||||
}
|
||||
else if (demId == DEMOD_NFM) {
|
||||
demodOutputSamplerate = 16000;
|
||||
vfo->setSampleRate(16000, bandwidth);
|
||||
demod.setSampleRate(16000);
|
||||
demod.setDeviation(bandwidth / 2.0f);
|
||||
audioResamp.setInput(&demod.out);
|
||||
audioBw = std::min<float>(bandwidth, outputSampleRate / 2.0f);
|
||||
|
||||
audioResamp.setInSampleRate(16000);
|
||||
audioWin.setSampleRate(16000 * audioResamp.getInterpolation());
|
||||
audioWin.setCutoff(audioBw);
|
||||
audioWin.setTransWidth(audioBw);
|
||||
audioResamp.updateWindow(&audioWin);
|
||||
|
||||
deemp.bypass = true;
|
||||
vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
demod.start();
|
||||
}
|
||||
else if (demId == DEMOD_AM) {
|
||||
demodOutputSamplerate = 125000;
|
||||
vfo->setSampleRate(12500, bandwidth);
|
||||
agc.setInput(&amDemod.out);
|
||||
audioResamp.setInput(&agc.out);
|
||||
audioBw = std::min<float>(bandwidth, outputSampleRate / 2.0f);
|
||||
|
||||
audioResamp.setInSampleRate(12500);
|
||||
audioWin.setSampleRate(12500 * audioResamp.getInterpolation());
|
||||
audioWin.setCutoff(audioBw);
|
||||
audioWin.setTransWidth(audioBw);
|
||||
audioResamp.updateWindow(&audioWin);
|
||||
|
||||
deemp.bypass = true;
|
||||
vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
agc.start();
|
||||
amDemod.start();
|
||||
}
|
||||
else if (demId == DEMOD_USB) {
|
||||
demodOutputSamplerate = 6000;
|
||||
vfo->setSampleRate(6000, bandwidth);
|
||||
ssbDemod.setMode(dsp::SSBDemod::MODE_USB);
|
||||
agc.setInput(&ssbDemod.out);
|
||||
audioResamp.setInput(&agc.out);
|
||||
audioBw = std::min<float>(bandwidth, outputSampleRate / 2.0f);
|
||||
|
||||
audioResamp.setInSampleRate(6000);
|
||||
audioWin.setSampleRate(6000 * audioResamp.getInterpolation());
|
||||
audioWin.setCutoff(audioBw);
|
||||
audioWin.setTransWidth(audioBw);
|
||||
audioResamp.updateWindow(&audioWin);
|
||||
|
||||
deemp.bypass = true;
|
||||
vfo->setReference(ImGui::WaterfallVFO::REF_LOWER);
|
||||
agc.start();
|
||||
ssbDemod.start();
|
||||
}
|
||||
else if (demId == DEMOD_LSB) {
|
||||
demodOutputSamplerate = 6000;
|
||||
vfo->setSampleRate(6000, bandwidth);
|
||||
ssbDemod.setMode(dsp::SSBDemod::MODE_LSB);
|
||||
agc.setInput(&ssbDemod.out);
|
||||
audioResamp.setInput(&agc.out);
|
||||
audioBw = std::min<float>(bandwidth, outputSampleRate / 2.0f);
|
||||
|
||||
audioResamp.setInSampleRate(6000);
|
||||
audioWin.setSampleRate(6000 * audioResamp.getInterpolation());
|
||||
audioWin.setCutoff(audioBw);
|
||||
audioWin.setTransWidth(audioBw);
|
||||
audioResamp.updateWindow(&audioWin);
|
||||
|
||||
deemp.bypass = true;
|
||||
vfo->setReference(ImGui::WaterfallVFO::REF_UPPER);
|
||||
agc.start();
|
||||
ssbDemod.start();
|
||||
}
|
||||
else if (demId == DEMOD_DSB) {
|
||||
demodOutputSamplerate = 6000;
|
||||
vfo->setSampleRate(6000, bandwidth);
|
||||
ssbDemod.setMode(dsp::SSBDemod::MODE_DSB);
|
||||
agc.setInput(&ssbDemod.out);
|
||||
audioResamp.setInput(&agc.out);
|
||||
audioBw = std::min<float>(bandwidth, outputSampleRate / 2.0f);
|
||||
|
||||
audioResamp.setInSampleRate(6000);
|
||||
audioWin.setSampleRate(6000 * audioResamp.getInterpolation());
|
||||
audioWin.setCutoff(audioBw);
|
||||
audioWin.setTransWidth(audioBw);
|
||||
audioResamp.updateWindow(&audioWin);
|
||||
|
||||
deemp.bypass = true;
|
||||
vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
agc.start();
|
||||
ssbDemod.start();
|
||||
}
|
||||
else {
|
||||
spdlog::error("UNIMPLEMENTED DEMODULATOR IN SigPath::setDemodulator (start): {0}", demId);
|
||||
}
|
||||
|
||||
audioResamp.start();
|
||||
deemp.start();
|
||||
}
|
||||
|
||||
void SigPath::setDeemphasis(int deemph) {
|
||||
_deemp = deemph;
|
||||
deemp.stop();
|
||||
if (_deemp == DEEMP_NONE) {
|
||||
deemp.bypass = true;
|
||||
}
|
||||
else if (_deemp == DEEMP_50US) {
|
||||
deemp.bypass = false;
|
||||
deemp.setTau(50e-6);
|
||||
}
|
||||
else if (_deemp == DEEMP_75US) {
|
||||
deemp.bypass = false;
|
||||
deemp.setTau(75e-6);
|
||||
}
|
||||
deemp.start();
|
||||
}
|
||||
|
||||
void SigPath::setBandwidth(float bandWidth) {
|
||||
bandwidth = bandWidth;
|
||||
vfo->setBandwidth(bandwidth);
|
||||
if (_demod == DEMOD_FM) {
|
||||
demod.stop();
|
||||
demod.setDeviation(bandwidth / 2.0f);
|
||||
demod.start();
|
||||
}
|
||||
else if (_demod == DEMOD_NFM) {
|
||||
demod.stop();
|
||||
demod.setDeviation(bandwidth / 2.0f);
|
||||
demod.start();
|
||||
}
|
||||
else if (_demod == DEMOD_AM) {
|
||||
// Notbing to change
|
||||
}
|
||||
else if (_demod == DEMOD_USB) {
|
||||
ssbDemod.stop();
|
||||
ssbDemod.setBandWidth(bandwidth);
|
||||
ssbDemod.start();
|
||||
}
|
||||
else if (_demod == DEMOD_LSB) {
|
||||
ssbDemod.stop();
|
||||
ssbDemod.setBandWidth(bandwidth);
|
||||
ssbDemod.start();
|
||||
}
|
||||
else if (_demod == DEMOD_DSB) {
|
||||
ssbDemod.stop();
|
||||
ssbDemod.setBandWidth(bandwidth);
|
||||
ssbDemod.start();
|
||||
}
|
||||
else if (_demod == DEMOD_RAW) {
|
||||
// Notbing to change
|
||||
}
|
||||
else {
|
||||
spdlog::error("UNIMPLEMENTED DEMODULATOR IN SigPath::setBandwidth");
|
||||
}
|
||||
float _audioBw = std::min<float>(bandwidth, outputSampleRate / 2.0f);
|
||||
if (audioBw != _audioBw) {
|
||||
audioBw = _audioBw;
|
||||
audioResamp.stop();
|
||||
|
||||
audioWin.setCutoff(audioBw);
|
||||
audioWin.setTransWidth(audioBw);
|
||||
audioResamp.updateWindow(&audioWin);
|
||||
|
||||
audioResamp.start();
|
||||
}
|
||||
}
|
||||
|
||||
void SigPath::start() {
|
||||
demod.start();
|
||||
audioResamp.start();
|
||||
deemp.start();
|
||||
m2s.start();
|
||||
stream.start();
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/window.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <module.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <dsp/processing.h>
|
||||
#include <signal_path/sink.h>
|
||||
|
||||
class SigPath {
|
||||
public:
|
||||
SigPath();
|
||||
void init(std::string vfoName);
|
||||
void start();
|
||||
void setDemodulator(int demod, float bandWidth);
|
||||
void setDeemphasis(int deemph);
|
||||
void setBandwidth(float bandWidth);
|
||||
|
||||
enum {
|
||||
DEMOD_FM,
|
||||
DEMOD_NFM,
|
||||
DEMOD_AM,
|
||||
DEMOD_USB,
|
||||
DEMOD_LSB,
|
||||
DEMOD_DSB,
|
||||
DEMOD_RAW,
|
||||
_DEMOD_COUNT
|
||||
};
|
||||
|
||||
enum {
|
||||
DEEMP_50US,
|
||||
DEEMP_75US,
|
||||
DEEMP_NONE,
|
||||
_DEEMP_COUNT
|
||||
};
|
||||
|
||||
|
||||
dsp::BFMDeemp deemp;
|
||||
VFOManager::VFO* vfo;
|
||||
|
||||
private:
|
||||
static void sampleRateChangeHandler(float _sampleRate, void* ctx);
|
||||
|
||||
dsp::stream<dsp::complex_t> input;
|
||||
|
||||
// Demodulators
|
||||
dsp::FMDemod demod;
|
||||
dsp::AMDemod amDemod;
|
||||
dsp::SSBDemod ssbDemod;
|
||||
|
||||
// Gain control
|
||||
dsp::AGC agc;
|
||||
|
||||
// Audio output
|
||||
dsp::filter_window::BlackmanWindow audioWin;
|
||||
dsp::PolyphaseResampler<float> audioResamp;
|
||||
dsp::MonoToStereo m2s;
|
||||
SinkManager::Stream stream;
|
||||
|
||||
std::string vfoName;
|
||||
|
||||
// TODO: FIx all this sample rate BS (multiple names for same thing)
|
||||
float bandwidth;
|
||||
float demodOutputSamplerate;
|
||||
float outputSampleRate;
|
||||
int _demod;
|
||||
int _deemp;
|
||||
float audioBw;
|
||||
};
|
17
radio/src/radio_demod.h
Normal file
17
radio/src/radio_demod.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <dsp/stream.h>
|
||||
#include <signal_path/vfo_manager.h>
|
||||
|
||||
class Demodulator {
|
||||
public:
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual bool isRunning() = 0;
|
||||
virtual void select() = 0;
|
||||
virtual void setVFO(VFOManager::VFO* vfo) = 0;
|
||||
virtual VFOManager::VFO* getVFO() = 0;
|
||||
virtual void setAudioSampleRate(float sampleRate) = 0;
|
||||
virtual float getAudioSampleRate() = 0;
|
||||
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
||||
virtual void showMenu() = 0;
|
||||
};
|
98
radio/src/raw_demod.h
Normal file
98
radio/src/raw_demod.h
Normal file
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
#include <radio_demod.h>
|
||||
#include <dsp/convertion.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
class RAWDemodulator : public Demodulator {
|
||||
public:
|
||||
RAWDemodulator() {}
|
||||
RAWDemodulator(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
init(prefix, vfo, audioSampleRate, bandWidth);
|
||||
}
|
||||
|
||||
void init(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
uiPrefix = prefix;
|
||||
_vfo = vfo;
|
||||
audioSampRate = audioSampleRate;
|
||||
bw = bandWidth;
|
||||
|
||||
c2s.init(_vfo->output);
|
||||
}
|
||||
|
||||
void start() {
|
||||
c2s.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
c2s.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
void select() {
|
||||
_vfo->setSampleRate(audioSampRate, audioSampRate);
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
_vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
}
|
||||
|
||||
void setVFO(VFOManager::VFO* vfo) {
|
||||
_vfo = vfo;
|
||||
c2s.setInput(_vfo->output);
|
||||
}
|
||||
|
||||
VFOManager::VFO* getVFO() {
|
||||
return _vfo;
|
||||
}
|
||||
|
||||
void setAudioSampleRate(float sampleRate) {
|
||||
audioSampRate = sampleRate;
|
||||
if (running) {
|
||||
_vfo->setSampleRate(audioSampRate, audioSampRate);
|
||||
}
|
||||
}
|
||||
|
||||
float getAudioSampleRate() {
|
||||
return audioSampRate;
|
||||
}
|
||||
|
||||
dsp::stream<dsp::stereo_t>* getOutput() {
|
||||
return &c2s.out;
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("Snap Interval").x - 8);
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::InputFloat(("##_radio_raw_snap_" + uiPrefix).c_str(), &snapInterval, 1, 100, 0)) {
|
||||
setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
// TODO: Allow selection of the bandwidth
|
||||
}
|
||||
|
||||
private:
|
||||
void setSnapInterval(float snapInt) {
|
||||
snapInterval = snapInt;
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 10000;
|
||||
float audioSampRate = 48000;
|
||||
float bw = 12500;
|
||||
bool running = false;
|
||||
|
||||
VFOManager::VFO* _vfo;
|
||||
dsp::ComplexToStereo c2s;
|
||||
|
||||
};
|
140
radio/src/usb_demod.h
Normal file
140
radio/src/usb_demod.h
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
#include <radio_demod.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
class USBDemodulator : public Demodulator {
|
||||
public:
|
||||
USBDemodulator() {}
|
||||
USBDemodulator(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
init(prefix, vfo, audioSampleRate, bandWidth);
|
||||
}
|
||||
|
||||
void init(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
uiPrefix = prefix;
|
||||
_vfo = vfo;
|
||||
audioSampRate = audioSampleRate;
|
||||
bw = bandWidth;
|
||||
|
||||
demod.init(_vfo->output, bbSampRate, bandWidth, dsp::SSBDemod::MODE_USB);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
resamp.init(&agc.out, &win, bbSampRate, audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
resamp.updateWindow(&win);
|
||||
|
||||
m2s.init(&resamp.out);
|
||||
}
|
||||
|
||||
void start() {
|
||||
demod.start();
|
||||
agc.start();
|
||||
resamp.start();
|
||||
m2s.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
demod.stop();
|
||||
agc.stop();
|
||||
resamp.stop();
|
||||
m2s.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
void select() {
|
||||
_vfo->setSampleRate(bbSampRate, bw);
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
_vfo->setReference(ImGui::WaterfallVFO::REF_LOWER);
|
||||
}
|
||||
|
||||
void setVFO(VFOManager::VFO* vfo) {
|
||||
_vfo = vfo;
|
||||
demod.setInput(_vfo->output);
|
||||
}
|
||||
|
||||
VFOManager::VFO* getVFO() {
|
||||
return _vfo;
|
||||
}
|
||||
|
||||
void setAudioSampleRate(float sampleRate) {
|
||||
if (running) {
|
||||
resamp.stop();
|
||||
}
|
||||
audioSampRate = sampleRate;
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
resamp.setOutSampleRate(audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
if (running) {
|
||||
resamp.start();
|
||||
}
|
||||
}
|
||||
|
||||
float getAudioSampleRate() {
|
||||
return audioSampRate;
|
||||
}
|
||||
|
||||
dsp::stream<dsp::stereo_t>* getOutput() {
|
||||
return &m2s.out;
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::InputFloat(("##_radio_usb_bw_" + uiPrefix).c_str(), &bw, 1, 100, 0)) {
|
||||
bw = std::clamp<float>(bw, bwMin, bwMax);
|
||||
setBandwidth(bw);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("Snap Interval").x - 8);
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::InputFloat(("##_radio_usb_snap_" + uiPrefix).c_str(), &snapInterval, 1, 100, 0)) {
|
||||
setSnapInterval(snapInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
snapInterval = snapInt;
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
const float bwMax = 3000;
|
||||
const float bwMin = 500;
|
||||
const float bbSampRate = 6000;
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 100;
|
||||
float audioSampRate = 48000;
|
||||
float bw = 3000;
|
||||
bool running = false;
|
||||
|
||||
VFOManager::VFO* _vfo;
|
||||
dsp::SSBDemod demod;
|
||||
dsp::AGC agc;
|
||||
dsp::filter_window::BlackmanWindow win;
|
||||
dsp::PolyphaseResampler<float> resamp;
|
||||
dsp::MonoToStereo m2s;
|
||||
|
||||
};
|
164
radio/src/wfm_demod.h
Normal file
164
radio/src/wfm_demod.h
Normal file
@ -0,0 +1,164 @@
|
||||
#pragma once
|
||||
#include <radio_demod.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/audio.h>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
class WFMDemodulator : public Demodulator {
|
||||
public:
|
||||
WFMDemodulator() {}
|
||||
WFMDemodulator(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
init(prefix, vfo, audioSampleRate, bandWidth);
|
||||
}
|
||||
|
||||
void init(std::string prefix, VFOManager::VFO* vfo, float audioSampleRate, float bandWidth) {
|
||||
uiPrefix = prefix;
|
||||
_vfo = vfo;
|
||||
audioSampRate = audioSampleRate;
|
||||
bw = bandWidth;
|
||||
|
||||
demod.init(_vfo->output, bbSampRate, bandWidth / 2.0f);
|
||||
|
||||
float audioBW = std::min<float>(audioSampleRate / 2.0f, 16000.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
resamp.init(&demod.out, &win, bbSampRate, audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
resamp.updateWindow(&win);
|
||||
|
||||
deemp.init(&resamp.out, audioSampRate, tau);
|
||||
|
||||
m2s.init(&deemp.out);
|
||||
}
|
||||
|
||||
void start() {
|
||||
demod.start();
|
||||
resamp.start();
|
||||
deemp.start();
|
||||
m2s.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
demod.stop();
|
||||
resamp.stop();
|
||||
deemp.stop();
|
||||
m2s.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
void select() {
|
||||
_vfo->setSampleRate(bbSampRate, bw);
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
_vfo->setReference(ImGui::WaterfallVFO::REF_CENTER);
|
||||
}
|
||||
|
||||
void setVFO(VFOManager::VFO* vfo) {
|
||||
_vfo = vfo;
|
||||
demod.setInput(_vfo->output);
|
||||
}
|
||||
|
||||
VFOManager::VFO* getVFO() {
|
||||
return _vfo;
|
||||
}
|
||||
|
||||
void setAudioSampleRate(float sampleRate) {
|
||||
if (running) {
|
||||
resamp.stop();
|
||||
deemp.stop();
|
||||
}
|
||||
audioSampRate = sampleRate;
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, 16000.0f);
|
||||
resamp.setOutSampleRate(audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
deemp.setSampleRate(audioSampRate);
|
||||
if (running) {
|
||||
resamp.start();
|
||||
deemp.start();
|
||||
}
|
||||
}
|
||||
|
||||
float getAudioSampleRate() {
|
||||
return audioSampRate;
|
||||
}
|
||||
|
||||
dsp::stream<dsp::stereo_t>* getOutput() {
|
||||
return &m2s.out;
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::InputFloat(("##_radio_wfm_bw_" + uiPrefix).c_str(), &bw, 1, 100, 0)) {
|
||||
bw = std::clamp<float>(bw, bwMin, bwMax);
|
||||
setBandwidth(bw);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("Snap Interval").x - 8);
|
||||
ImGui::Text("Snap Interval");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::InputFloat(("##_radio_wfm_snap_" + uiPrefix).c_str(), &snapInterval, 1, 100, 0)) {
|
||||
setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::CalcTextSize("De-emphasis").x - 8);
|
||||
ImGui::Text("De-emphasis");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Combo(("##_radio_wfm_deemp_" + uiPrefix).c_str(), &deempId, deempModes)) {
|
||||
setDeempIndex(deempId);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
demod.setDeviation(bw / 2.0f);
|
||||
}
|
||||
|
||||
void setDeempIndex(int id) {
|
||||
if (id >= 2 || id < 0) {
|
||||
deemp.bypass = true;
|
||||
return;
|
||||
}
|
||||
deemp.setTau(deempVals[id]);
|
||||
deemp.bypass = false;
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
snapInterval = snapInt;
|
||||
_vfo->setSnapInterval(snapInterval);
|
||||
}
|
||||
|
||||
const float bwMax = 200000;
|
||||
const float bwMin = 100000;
|
||||
const float bbSampRate = 200000;
|
||||
const char* deempModes = "50µS\00075µS\000none\000";
|
||||
const float deempVals[2] = { 50e-6, 75e-6 };
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 100000;
|
||||
float audioSampRate = 48000;
|
||||
float bw = 200000;
|
||||
int deempId = 0;
|
||||
float tau = 50e-6;
|
||||
bool running = false;
|
||||
|
||||
VFOManager::VFO* _vfo;
|
||||
dsp::FMDemod demod;
|
||||
dsp::filter_window::BlackmanWindow win;
|
||||
dsp::PolyphaseResampler<float> resamp;
|
||||
dsp::BFMDeemp deemp;
|
||||
dsp::MonoToStereo m2s;
|
||||
|
||||
};
|
@ -19,7 +19,7 @@
|
||||
"bandPlan": "General",
|
||||
"bandPlanEnabled": true,
|
||||
"fftHeight": 296,
|
||||
"frequency": 99000000,
|
||||
"frequency": 98930000,
|
||||
"max": 0.0,
|
||||
"maximized": false,
|
||||
"menuOrder": [
|
||||
@ -36,13 +36,13 @@
|
||||
"min": -72.05882263183594,
|
||||
"offset": 0.0,
|
||||
"showWaterfall": true,
|
||||
"source": "PlutoSDR",
|
||||
"source": "SoapySDR",
|
||||
"sourceSettings": {},
|
||||
"streams": {
|
||||
"Radio": {
|
||||
"muted": false,
|
||||
"sink": "Audio",
|
||||
"volume": 0.5306122303009033
|
||||
"volume": 0.4183673560619354
|
||||
},
|
||||
"Radio 1": {
|
||||
"muted": true,
|
||||
|
@ -2,5 +2,5 @@
|
||||
"IP": "192.168.2.1",
|
||||
"gain": 0.0,
|
||||
"gainMode": 2,
|
||||
"sampleRate": 2000000.0
|
||||
"sampleRate": 4000000.0
|
||||
}
|
@ -32,7 +32,7 @@
|
||||
"LNA": 23.415000915527344,
|
||||
"VGA": 16.332000732421875
|
||||
},
|
||||
"sampleRate": 8000000.0
|
||||
"sampleRate": 2000000.0
|
||||
},
|
||||
"Microphone (Realtek High Definition Audio)": {
|
||||
"sampleRate": 96000.0
|
||||
|
Loading…
Reference in New Issue
Block a user