mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2026-04-18 22:32:44 +00:00
bugfix, new squelch system, dragon labs source
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,7 +17,7 @@ m17_decoder/libcorrect
|
|||||||
SDR++.app
|
SDR++.app
|
||||||
android/deps
|
android/deps
|
||||||
android/app/assets
|
android/app/assets
|
||||||
source_modules/dragonlabs_source
|
|
||||||
source_modules/badgesdr_source
|
source_modules/badgesdr_source
|
||||||
decoder_modules/mystery_decoder
|
decoder_modules/mystery_decoder
|
||||||
decoder_modules/tetra_decoder
|
decoder_modules/tetra_decoder
|
||||||
|
misc_modules/modulation_monitor
|
||||||
@@ -14,6 +14,7 @@ option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Dependencies:
|
|||||||
option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)" ON)
|
option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)" ON)
|
||||||
option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF)
|
option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF)
|
||||||
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF)
|
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF)
|
||||||
|
option(OPT_BUILD_DRAGONLABS_SOURCE "Build Dragon Labs Source Module (Dependencies: libdlcr)" OFF)
|
||||||
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
|
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
|
||||||
option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF)
|
option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF)
|
||||||
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
||||||
@@ -143,6 +144,10 @@ if (OPT_BUILD_BLADERF_SOURCE)
|
|||||||
add_subdirectory("source_modules/bladerf_source")
|
add_subdirectory("source_modules/bladerf_source")
|
||||||
endif (OPT_BUILD_BLADERF_SOURCE)
|
endif (OPT_BUILD_BLADERF_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_DRAGONLABS_SOURCE)
|
||||||
|
add_subdirectory("source_modules/dragonlabs_source")
|
||||||
|
endif (OPT_BUILD_DRAGONLABS_SOURCE)
|
||||||
|
|
||||||
if (OPT_BUILD_FILE_SOURCE)
|
if (OPT_BUILD_FILE_SOURCE)
|
||||||
add_subdirectory("source_modules/file_source")
|
add_subdirectory("source_modules/file_source")
|
||||||
endif (OPT_BUILD_FILE_SOURCE)
|
endif (OPT_BUILD_FILE_SOURCE)
|
||||||
|
|||||||
@@ -163,10 +163,10 @@ namespace dsp {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Processor<T, T>* blockBefore(Processor<T, T>* block) {
|
Processor<T, T>* blockBefore(Processor<T, T>* block) {
|
||||||
// TODO: This is wrong and must be fixed when I get more time
|
Processor<T, T>* prev = NULL;
|
||||||
for (auto& ln : links) {
|
for (auto& ln : links) {
|
||||||
if (ln == block) { return NULL; }
|
if (ln == block) { return prev; }
|
||||||
if (states[ln]) { return ln; }
|
if (states[ln]) { prev = ln; }
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,17 @@ namespace dsp::demod {
|
|||||||
dsp::taps::free(filterTaps);
|
dsp::taps::free(filterTaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass, bool highPass) {
|
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass) {
|
||||||
_samplerate = samplerate;
|
_samplerate = samplerate;
|
||||||
_bandwidth = bandwidth;
|
_bandwidth = bandwidth;
|
||||||
_lowPass = lowPass;
|
_lowPass = lowPass;
|
||||||
_highPass = highPass;
|
|
||||||
|
|
||||||
demod.init(NULL, bandwidth / 2.0, _samplerate);
|
demod.init(NULL, bandwidth / 2.0, _samplerate);
|
||||||
loadDummyTaps();
|
loadDummyTaps();
|
||||||
fir.init(NULL, filterTaps);
|
fir.init(NULL, filterTaps);
|
||||||
|
|
||||||
// Initialize taps
|
// Initialize taps
|
||||||
updateFilter(lowPass, highPass);
|
updateFilter(lowPass);
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, float>) {
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
demod.out.free();
|
demod.out.free();
|
||||||
@@ -59,19 +58,13 @@ namespace dsp::demod {
|
|||||||
if (bandwidth == _bandwidth) { return; }
|
if (bandwidth == _bandwidth) { return; }
|
||||||
_bandwidth = bandwidth;
|
_bandwidth = bandwidth;
|
||||||
demod.setDeviation(_bandwidth / 2.0, _samplerate);
|
demod.setDeviation(_bandwidth / 2.0, _samplerate);
|
||||||
updateFilter(_lowPass, _highPass);
|
updateFilter(_lowPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLowPass(bool lowPass) {
|
void setLowPass(bool lowPass) {
|
||||||
assert(base_type::_block_init);
|
assert(base_type::_block_init);
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
updateFilter(lowPass, _highPass);
|
updateFilter(lowPass);
|
||||||
}
|
|
||||||
|
|
||||||
void setHighPass(bool highPass) {
|
|
||||||
assert(base_type::_block_init);
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
|
||||||
updateFilter(_lowPass, highPass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
@@ -86,14 +79,14 @@ namespace dsp::demod {
|
|||||||
inline int process(int count, dsp::complex_t* in, T* out) {
|
inline int process(int count, dsp::complex_t* in, T* out) {
|
||||||
if constexpr (std::is_same_v<T, float>) {
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
demod.process(count, in, out);
|
demod.process(count, in, out);
|
||||||
if (filtering) {
|
if (_lowPass) {
|
||||||
std::lock_guard<std::mutex> lck(filterMtx);
|
std::lock_guard<std::mutex> lck(filterMtx);
|
||||||
fir.process(count, out, out);
|
fir.process(count, out, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<T, stereo_t>) {
|
if constexpr (std::is_same_v<T, stereo_t>) {
|
||||||
demod.process(count, in, demod.out.writeBuf);
|
demod.process(count, in, demod.out.writeBuf);
|
||||||
if (filtering) {
|
if (_lowPass) {
|
||||||
std::lock_guard<std::mutex> lck(filterMtx);
|
std::lock_guard<std::mutex> lck(filterMtx);
|
||||||
fir.process(count, demod.out.writeBuf, demod.out.writeBuf);
|
fir.process(count, demod.out.writeBuf, demod.out.writeBuf);
|
||||||
}
|
}
|
||||||
@@ -114,25 +107,17 @@ namespace dsp::demod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateFilter(bool lowPass, bool highPass) {
|
void updateFilter(bool lowPass) {
|
||||||
std::lock_guard<std::mutex> lck(filterMtx);
|
std::lock_guard<std::mutex> lck(filterMtx);
|
||||||
|
|
||||||
// Update values
|
// Update values
|
||||||
_lowPass = lowPass;
|
_lowPass = lowPass;
|
||||||
_highPass = highPass;
|
|
||||||
filtering = (lowPass || highPass);
|
|
||||||
|
|
||||||
// Free filter taps
|
// Free filter taps
|
||||||
dsp::taps::free(filterTaps);
|
dsp::taps::free(filterTaps);
|
||||||
|
|
||||||
// Generate filter depending on low and high pass settings
|
// Generate filter depending on the low pass settings
|
||||||
if (_lowPass && _highPass) {
|
if (_lowPass) {
|
||||||
filterTaps = dsp::taps::bandPass<float>(300.0, _bandwidth / 2.0, 100.0, _samplerate);
|
|
||||||
}
|
|
||||||
else if (_highPass) {
|
|
||||||
filterTaps = dsp::taps::highPass(300.0, 100.0, _samplerate);
|
|
||||||
}
|
|
||||||
else if (_lowPass) {
|
|
||||||
filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
|
filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -152,7 +137,6 @@ namespace dsp::demod {
|
|||||||
double _samplerate;
|
double _samplerate;
|
||||||
double _bandwidth;
|
double _bandwidth;
|
||||||
bool _lowPass;
|
bool _lowPass;
|
||||||
bool _highPass;
|
|
||||||
bool filtering;
|
bool filtering;
|
||||||
|
|
||||||
Quadrature demod;
|
Quadrature demod;
|
||||||
|
|||||||
303
core/src/dsp/noise_reduction/ctcss_squelch.h
Normal file
303
core/src/dsp/noise_reduction/ctcss_squelch.h
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../channel/rx_vfo.h"
|
||||||
|
#include "../demod/quadrature.h"
|
||||||
|
#include "../filter/fir.h"
|
||||||
|
#include "../taps/high_pass.h"
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include <map>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#define CTCSS_DECODE_SAMPLERATE 500//250.0
|
||||||
|
#define CTCSS_DECODE_BANDWIDTH 200.0
|
||||||
|
#define CTCSS_DECODE_OFFSET 160.55
|
||||||
|
|
||||||
|
namespace dsp::noise_reduction {
|
||||||
|
enum CTCSSTone {
|
||||||
|
/**
|
||||||
|
* Indicates that any valid tone will let audio through.
|
||||||
|
*/
|
||||||
|
CTCSS_TONE_ANY = -2,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that no tone is being received, or to act as a decoder only, letting audio through continuously.
|
||||||
|
*/
|
||||||
|
CTCSS_TONE_NONE = -1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CTCSS Tone Frequency.
|
||||||
|
*/
|
||||||
|
CTCSS_TONE_67Hz,
|
||||||
|
CTCSS_TONE_69_3Hz,
|
||||||
|
CTCSS_TONE_71_9Hz,
|
||||||
|
CTCSS_TONE_74_4Hz,
|
||||||
|
CTCSS_TONE_77Hz,
|
||||||
|
CTCSS_TONE_79_7Hz,
|
||||||
|
CTCSS_TONE_82_5Hz,
|
||||||
|
CTCSS_TONE_85_4Hz,
|
||||||
|
CTCSS_TONE_88_5Hz,
|
||||||
|
CTCSS_TONE_91_5Hz,
|
||||||
|
CTCSS_TONE_94_8Hz,
|
||||||
|
CTCSS_TONE_97_4Hz,
|
||||||
|
CTCSS_TONE_100Hz,
|
||||||
|
CTCSS_TONE_103_5Hz,
|
||||||
|
CTCSS_TONE_107_2Hz,
|
||||||
|
CTCSS_TONE_110_9Hz,
|
||||||
|
CTCSS_TONE_114_8Hz,
|
||||||
|
CTCSS_TONE_118_8Hz,
|
||||||
|
CTCSS_TONE_123Hz,
|
||||||
|
CTCSS_TONE_127_3Hz,
|
||||||
|
CTCSS_TONE_131_8Hz,
|
||||||
|
CTCSS_TONE_136_5Hz,
|
||||||
|
CTCSS_TONE_141_3Hz,
|
||||||
|
CTCSS_TONE_146_2Hz,
|
||||||
|
CTCSS_TONE_150Hz,
|
||||||
|
CTCSS_TONE_151_4Hz,
|
||||||
|
CTCSS_TONE_156_7Hz,
|
||||||
|
CTCSS_TONE_159_8Hz,
|
||||||
|
CTCSS_TONE_162_2Hz,
|
||||||
|
CTCSS_TONE_165_5Hz,
|
||||||
|
CTCSS_TONE_167_9Hz,
|
||||||
|
CTCSS_TONE_171_3Hz,
|
||||||
|
CTCSS_TONE_173_8Hz,
|
||||||
|
CTCSS_TONE_177_3Hz,
|
||||||
|
CTCSS_TONE_179_9Hz,
|
||||||
|
CTCSS_TONE_183_5Hz,
|
||||||
|
CTCSS_TONE_186_2Hz,
|
||||||
|
CTCSS_TONE_189_9Hz,
|
||||||
|
CTCSS_TONE_192_8Hz,
|
||||||
|
CTCSS_TONE_196_6Hz,
|
||||||
|
CTCSS_TONE_199_5Hz,
|
||||||
|
CTCSS_TONE_203_5Hz,
|
||||||
|
CTCSS_TONE_206_5Hz,
|
||||||
|
CTCSS_TONE_210_7Hz,
|
||||||
|
CTCSS_TONE_218_1Hz,
|
||||||
|
CTCSS_TONE_225_7Hz,
|
||||||
|
CTCSS_TONE_229_1Hz,
|
||||||
|
CTCSS_TONE_233_6Hz,
|
||||||
|
CTCSS_TONE_241_8Hz,
|
||||||
|
CTCSS_TONE_250_3Hz,
|
||||||
|
CTCSS_TONE_254_1Hz,
|
||||||
|
_CTCSS_TONE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
const float CTCSS_TONES[_CTCSS_TONE_COUNT] = {
|
||||||
|
67.0f,
|
||||||
|
69.3f,
|
||||||
|
71.9f,
|
||||||
|
74.4f,
|
||||||
|
77.0f,
|
||||||
|
79.7f,
|
||||||
|
82.5f,
|
||||||
|
85.4f,
|
||||||
|
88.5f,
|
||||||
|
91.5f,
|
||||||
|
94.8f,
|
||||||
|
97.4f,
|
||||||
|
100.0f,
|
||||||
|
103.5f,
|
||||||
|
107.2f,
|
||||||
|
110.9f,
|
||||||
|
114.8f,
|
||||||
|
118.8f,
|
||||||
|
123.0f,
|
||||||
|
127.3f,
|
||||||
|
131.8f,
|
||||||
|
136.5f,
|
||||||
|
141.3f,
|
||||||
|
146.2f,
|
||||||
|
150.0f,
|
||||||
|
151.4f,
|
||||||
|
156.7f,
|
||||||
|
159.8f,
|
||||||
|
162.2f,
|
||||||
|
165.5f,
|
||||||
|
167.9f,
|
||||||
|
171.3f,
|
||||||
|
173.8f,
|
||||||
|
177.3f,
|
||||||
|
179.9f,
|
||||||
|
183.5f,
|
||||||
|
186.2f,
|
||||||
|
189.9f,
|
||||||
|
192.8f,
|
||||||
|
196.6f,
|
||||||
|
199.5f,
|
||||||
|
203.5f,
|
||||||
|
206.5f,
|
||||||
|
210.7f,
|
||||||
|
218.1f,
|
||||||
|
225.7f,
|
||||||
|
229.1f,
|
||||||
|
233.6f,
|
||||||
|
241.8f,
|
||||||
|
250.3f,
|
||||||
|
254.1f
|
||||||
|
};
|
||||||
|
|
||||||
|
class CTCSSSquelch : public Processor<stereo_t, stereo_t> {
|
||||||
|
using base_type = Processor<stereo_t, stereo_t>;
|
||||||
|
public:
|
||||||
|
CTCSSSquelch() {}
|
||||||
|
|
||||||
|
CTCSSSquelch(stream<stereo_t>* in, double samplerate) { init(in, samplerate); }
|
||||||
|
|
||||||
|
~CTCSSSquelch() {
|
||||||
|
// If not initialized, do nothing
|
||||||
|
if (!base_type::_block_init) { return; }
|
||||||
|
|
||||||
|
// Stop the DSP thread
|
||||||
|
base_type::stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<stereo_t>* in, double samplerate) {
|
||||||
|
// Save settings
|
||||||
|
_samplerate = samplerate;
|
||||||
|
|
||||||
|
// Create dummy taps just for initialization
|
||||||
|
float dummy[1] = { 1.0f };
|
||||||
|
auto dummyTaps = dsp::taps::fromArray(1, dummy);
|
||||||
|
|
||||||
|
// Initialize the DDC and FM demod
|
||||||
|
ddc.init(NULL, samplerate, CTCSS_DECODE_SAMPLERATE, CTCSS_DECODE_BANDWIDTH, CTCSS_DECODE_OFFSET);
|
||||||
|
fm.init(NULL, 1.0, CTCSS_DECODE_SAMPLERATE);
|
||||||
|
|
||||||
|
// Initilize the base block class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate(double samplerate) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
base_type::tempStop();
|
||||||
|
_samplerate = samplerate;
|
||||||
|
ddc.setInSamplerate(samplerate);
|
||||||
|
base_type::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRequiredTone(CTCSSTone tone) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
requiredTone = tone;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTCSSTone getCurrentTone() {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
return currentTone;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int process(int count, const stereo_t* in, stereo_t* out) {
|
||||||
|
// Shift and resample to the correct samplerate
|
||||||
|
int ddcOutCount = ddc.process(count, (complex_t*)in, ddc.out.writeBuf);
|
||||||
|
|
||||||
|
// FM Demod the CTCSS tone
|
||||||
|
fm.process(ddcOutCount, ddc.out.writeBuf, fm.out.writeBuf);
|
||||||
|
|
||||||
|
// Get the required tone
|
||||||
|
const CTCSSTone rtone = requiredTone;
|
||||||
|
|
||||||
|
// Detect the tone frequency
|
||||||
|
for (int i = 0; i < ddcOutCount; i++) {
|
||||||
|
// Compute the running mean
|
||||||
|
const float val = fm.out.writeBuf[i];
|
||||||
|
mean = 0.95f*mean + 0.05f*val;
|
||||||
|
|
||||||
|
// Compute the running variance
|
||||||
|
const float err = val - mean;
|
||||||
|
var = 0.95f*var + 0.05f*err*err;
|
||||||
|
|
||||||
|
// Run a schmitt trigger on the variance
|
||||||
|
bool nvarOk = varOk ? (var < 1100.0f) : (var < 1000.0f);
|
||||||
|
|
||||||
|
// Check if the tone has to be rematched
|
||||||
|
if (nvarOk && (!varOk || mean < minFreq || mean > maxFreq)) {
|
||||||
|
// Compute the absolute frequency
|
||||||
|
float freq = mean + CTCSS_DECODE_OFFSET;
|
||||||
|
|
||||||
|
// Check it against the known tones
|
||||||
|
if (freq < CTCSS_TONES[0] - 2.5) {
|
||||||
|
currentTone = CTCSS_TONE_NONE;
|
||||||
|
}
|
||||||
|
else if (freq > CTCSS_TONES[_CTCSS_TONE_COUNT-1] + 2.5) {
|
||||||
|
currentTone = CTCSS_TONE_NONE;
|
||||||
|
}
|
||||||
|
else if (freq < CTCSS_TONES[0]) {
|
||||||
|
currentTone = (CTCSSTone)0;
|
||||||
|
}
|
||||||
|
else if (freq > CTCSS_TONES[_CTCSS_TONE_COUNT-1]) {
|
||||||
|
currentTone = (CTCSSTone)(_CTCSS_TONE_COUNT-1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int a = 0;
|
||||||
|
int b = _CTCSS_TONE_COUNT-1;
|
||||||
|
while (b - a > 1) {
|
||||||
|
int c = (a + b) >> 1;
|
||||||
|
((CTCSS_TONES[c] < freq) ? a : b) = c;
|
||||||
|
}
|
||||||
|
currentTone = (CTCSSTone)((freq - CTCSS_TONES[a] < CTCSS_TONES[b] - freq) ? a : b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the mute status
|
||||||
|
mute = !(currentTone == rtone || (currentTone != CTCSS_TONE_NONE && rtone == CTCSS_TONE_ANY));
|
||||||
|
|
||||||
|
// Unmuted the audio if needed
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Recompute min and max freq if a valid tone is detected
|
||||||
|
if (currentTone != CTCSS_TONE_NONE) {
|
||||||
|
float c = CTCSS_TONES[currentTone];
|
||||||
|
float l = (currentTone > CTCSS_TONE_67Hz) ? CTCSS_TONES[currentTone - 1] : c - 2.5f;
|
||||||
|
float r = (currentTone < CTCSS_TONE_254_1Hz) ? CTCSS_TONES[currentTone + 1] : c + 2.5f;
|
||||||
|
minFreq = (l+c) / 2.0f;
|
||||||
|
maxFreq = (r+c) / 2.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a rising edge on the variance
|
||||||
|
if (!nvarOk && varOk) {
|
||||||
|
// Mute the audio
|
||||||
|
// TODO
|
||||||
|
mute = true;
|
||||||
|
currentTone = CTCSS_TONE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the new variance state
|
||||||
|
varOk = nvarOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEBUG ONLY
|
||||||
|
if ((rtone != CTCSS_TONE_NONE) && mute) {
|
||||||
|
memset(out, 0, count * sizeof(stereo_t));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(out, in, count * sizeof(stereo_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
process(count, base_type::_in->readBuf, base_type::out.writeBuf);
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!base_type::out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
double _samplerate;
|
||||||
|
std::atomic<CTCSSTone> requiredTone = CTCSS_TONE_ANY;
|
||||||
|
|
||||||
|
float mean = 0.0f;
|
||||||
|
float var = 0.0f;
|
||||||
|
bool varOk = false;
|
||||||
|
float minFreq = 0.0f;
|
||||||
|
float maxFreq = 0.0f;
|
||||||
|
bool mute = true;
|
||||||
|
std::atomic<CTCSSTone> currentTone = CTCSS_TONE_NONE;
|
||||||
|
|
||||||
|
channel::RxVFO ddc;
|
||||||
|
demod::Quadrature fm;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
// TODO: Rewrite better!!!!!
|
// TODO: Rewrite better!!!!!
|
||||||
namespace dsp::noise_reduction {
|
namespace dsp::noise_reduction {
|
||||||
class Squelch : public Processor<complex_t, complex_t> {
|
class PowerSquelch : public Processor<complex_t, complex_t> {
|
||||||
using base_type = Processor<complex_t, complex_t>;
|
using base_type = Processor<complex_t, complex_t>;
|
||||||
public:
|
public:
|
||||||
Squelch() {}
|
PowerSquelch() {}
|
||||||
|
|
||||||
Squelch(stream<complex_t>* in, double level) {}
|
PowerSquelch(stream<complex_t>* in, double level) {}
|
||||||
|
|
||||||
~Squelch() {
|
~PowerSquelch() {
|
||||||
if (!base_type::_block_init) { return; }
|
if (!base_type::_block_init) { return; }
|
||||||
base_type::stop();
|
base_type::stop();
|
||||||
buffer::free(normBuffer);
|
buffer::free(normBuffer);
|
||||||
@@ -31,8 +31,11 @@ namespace dsp::noise_reduction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline int process(int count, const complex_t* in, complex_t* out) {
|
inline int process(int count, const complex_t* in, complex_t* out) {
|
||||||
float sum;
|
// Compute the amplitude of each sample
|
||||||
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)in, count);
|
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)in, count);
|
||||||
|
|
||||||
|
// Compute the mean amplitude
|
||||||
|
float sum = 0.0f;
|
||||||
volk_32f_accumulator_s32f(&sum, normBuffer, count);
|
volk_32f_accumulator_s32f(&sum, normBuffer, count);
|
||||||
sum /= (float)count;
|
sum /= (float)count;
|
||||||
|
|
||||||
@@ -46,8 +49,6 @@ namespace dsp::noise_reduction {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
//DEFAULT_PROC_RUN();
|
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
int count = base_type::_in->read();
|
int count = base_type::_in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
@@ -85,8 +85,8 @@ void SourceManager::tune(double freq) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: No need to always retune the hardware in Panadapter mode
|
// TODO: No need to always retune the hardware in Panadapter mode
|
||||||
selectedHandler->tuneHandler(abs(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset), selectedHandler->ctx);
|
selectedHandler->tuneHandler(abs(((tuneMode == TuningMode::NORMAL) ? (freq + tuneOffset) : ifFreq)), selectedHandler->ctx);
|
||||||
onRetune.emit(freq);
|
onRetune.emit(freq + tuneOffset);
|
||||||
currentFreq = freq;
|
currentFreq = freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION_STR "1.2.1"
|
#define VERSION_STR "1.3.0"
|
||||||
@@ -20,6 +20,16 @@ enum IFNRPreset {
|
|||||||
IFNR_PRESET_BROADCAST
|
IFNR_PRESET_BROADCAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum SquelchMode {
|
||||||
|
SQUELCH_MODE_OFF,
|
||||||
|
SQUELCH_MODE_POWER,
|
||||||
|
SQUELCH_MODE_SNR,
|
||||||
|
SQUELCH_MODE_CTCSS_MUTE,
|
||||||
|
SQUELCH_MODE_CTCSS_DECODE,
|
||||||
|
SQUELCH_MODE_DCS_MUTE,
|
||||||
|
SQUELCH_MODE_DCS_DECODE,
|
||||||
|
};
|
||||||
|
|
||||||
namespace demod {
|
namespace demod {
|
||||||
class Demodulator {
|
class Demodulator {
|
||||||
public:
|
public:
|
||||||
@@ -45,6 +55,8 @@ namespace demod {
|
|||||||
virtual int getDefaultDeemphasisMode() = 0;
|
virtual int getDefaultDeemphasisMode() = 0;
|
||||||
virtual bool getFMIFNRAllowed() = 0;
|
virtual bool getFMIFNRAllowed() = 0;
|
||||||
virtual bool getNBAllowed() = 0;
|
virtual bool getNBAllowed() = 0;
|
||||||
|
virtual bool getHighPassAllowed() = 0;
|
||||||
|
virtual bool getSquelchAllowed() = 0;
|
||||||
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ namespace demod {
|
|||||||
|
|
||||||
void showMenu() {
|
void showMenu() {
|
||||||
float menuWidth = ImGui::GetContentRegionAvail().x;
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
if (ImGui::Checkbox(("Carrier AGC##_radio_am_carrier_agc_" + name).c_str(), &carrierAgc)) {
|
||||||
|
demod.setAGCMode(carrierAgc ? dsp::demod::AM<dsp::stereo_t>::AGCMode::CARRIER : dsp::demod::AM<dsp::stereo_t>::AGCMode::AUDIO);
|
||||||
|
_config->acquire();
|
||||||
|
_config->conf[name][getName()]["carrierAgc"] = carrierAgc;
|
||||||
|
_config->release(true);
|
||||||
|
}
|
||||||
ImGui::LeftLabel("AGC Attack");
|
ImGui::LeftLabel("AGC Attack");
|
||||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
if (ImGui::SliderFloat(("##_radio_am_agc_attack_" + name).c_str(), &agcAttack, 1.0f, 200.0f)) {
|
if (ImGui::SliderFloat(("##_radio_am_agc_attack_" + name).c_str(), &agcAttack, 1.0f, 200.0f)) {
|
||||||
@@ -56,12 +62,6 @@ namespace demod {
|
|||||||
_config->conf[name][getName()]["agcDecay"] = agcDecay;
|
_config->conf[name][getName()]["agcDecay"] = agcDecay;
|
||||||
_config->release(true);
|
_config->release(true);
|
||||||
}
|
}
|
||||||
if (ImGui::Checkbox(("Carrier AGC##_radio_am_carrier_agc_" + name).c_str(), &carrierAgc)) {
|
|
||||||
demod.setAGCMode(carrierAgc ? dsp::demod::AM<dsp::stereo_t>::AGCMode::CARRIER : dsp::demod::AM<dsp::stereo_t>::AGCMode::AUDIO);
|
|
||||||
_config->acquire();
|
|
||||||
_config->conf[name][getName()]["carrierAgc"] = carrierAgc;
|
|
||||||
_config->release(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBandwidth(double bandwidth) { demod.setBandwidth(bandwidth); }
|
void setBandwidth(double bandwidth) { demod.setBandwidth(bandwidth); }
|
||||||
@@ -86,6 +86,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return false; }
|
bool getNBAllowed() { return false; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return false; }
|
bool getNBAllowed() { return false; }
|
||||||
|
bool getHighPassAllowed() { return false; }
|
||||||
|
bool getSquelchAllowed() { return false; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return true; }
|
bool getNBAllowed() { return true; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return true; }
|
bool getNBAllowed() { return true; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -22,14 +22,11 @@ namespace demod {
|
|||||||
if (config->conf[name][getName()].contains("lowPass")) {
|
if (config->conf[name][getName()].contains("lowPass")) {
|
||||||
_lowPass = config->conf[name][getName()]["lowPass"];
|
_lowPass = config->conf[name][getName()]["lowPass"];
|
||||||
}
|
}
|
||||||
if (config->conf[name][getName()].contains("highPass")) {
|
|
||||||
_highPass = config->conf[name][getName()]["highPass"];
|
|
||||||
}
|
|
||||||
_config->release();
|
_config->release();
|
||||||
|
|
||||||
|
|
||||||
// Define structure
|
// Define structure
|
||||||
demod.init(input, getIFSampleRate(), bandwidth, _lowPass, _highPass);
|
demod.init(input, getIFSampleRate(), bandwidth, _lowPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() { demod.start(); }
|
void start() { demod.start(); }
|
||||||
@@ -43,12 +40,6 @@ namespace demod {
|
|||||||
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
||||||
_config->release(true);
|
_config->release(true);
|
||||||
}
|
}
|
||||||
if (ImGui::Checkbox(("High Pass##_radio_wfm_highpass_" + name).c_str(), &_highPass)) {
|
|
||||||
demod.setHighPass(_highPass);
|
|
||||||
_config->acquire();
|
|
||||||
_config->conf[name][getName()]["highPass"] = _highPass;
|
|
||||||
_config->release(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBandwidth(double bandwidth) {
|
void setBandwidth(double bandwidth) {
|
||||||
@@ -75,6 +66,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return true; }
|
bool getFMIFNRAllowed() { return true; }
|
||||||
bool getNBAllowed() { return false; }
|
bool getNBAllowed() { return false; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -83,7 +76,6 @@ namespace demod {
|
|||||||
ConfigManager* _config = NULL;
|
ConfigManager* _config = NULL;
|
||||||
|
|
||||||
bool _lowPass = true;
|
bool _lowPass = true;
|
||||||
bool _highPass = false;
|
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return true; }
|
bool getNBAllowed() { return true; }
|
||||||
|
bool getHighPassAllowed() { return false; }
|
||||||
|
bool getSquelchAllowed() { return false; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return true; }
|
bool getNBAllowed() { return true; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -100,18 +100,18 @@ namespace demod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void showMenu() {
|
void showMenu() {
|
||||||
if (ImGui::Checkbox(("Stereo##_radio_wfm_stereo_" + name).c_str(), &_stereo)) {
|
|
||||||
setStereo(_stereo);
|
|
||||||
_config->acquire();
|
|
||||||
_config->conf[name][getName()]["stereo"] = _stereo;
|
|
||||||
_config->release(true);
|
|
||||||
}
|
|
||||||
if (ImGui::Checkbox(("Low Pass##_radio_wfm_lowpass_" + name).c_str(), &_lowPass)) {
|
if (ImGui::Checkbox(("Low Pass##_radio_wfm_lowpass_" + name).c_str(), &_lowPass)) {
|
||||||
demod.setLowPass(_lowPass);
|
demod.setLowPass(_lowPass);
|
||||||
_config->acquire();
|
_config->acquire();
|
||||||
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
||||||
_config->release(true);
|
_config->release(true);
|
||||||
}
|
}
|
||||||
|
if (ImGui::Checkbox(("Stereo##_radio_wfm_stereo_" + name).c_str(), &_stereo)) {
|
||||||
|
setStereo(_stereo);
|
||||||
|
_config->acquire();
|
||||||
|
_config->conf[name][getName()]["stereo"] = _stereo;
|
||||||
|
_config->release(true);
|
||||||
|
}
|
||||||
if (ImGui::Checkbox(("Decode RDS##_radio_wfm_rds_" + name).c_str(), &_rds)) {
|
if (ImGui::Checkbox(("Decode RDS##_radio_wfm_rds_" + name).c_str(), &_rds)) {
|
||||||
demod.setRDSOut(_rds);
|
demod.setRDSOut(_rds);
|
||||||
_config->acquire();
|
_config->acquire();
|
||||||
@@ -270,6 +270,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; }
|
||||||
bool getFMIFNRAllowed() { return true; }
|
bool getFMIFNRAllowed() { return true; }
|
||||||
bool getNBAllowed() { return false; }
|
bool getNBAllowed() { return false; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
// ============= DEDICATED FUNCTIONS =============
|
// ============= DEDICATED FUNCTIONS =============
|
||||||
|
|||||||
@@ -5,10 +5,14 @@ enum {
|
|||||||
RADIO_IFACE_CMD_SET_MODE,
|
RADIO_IFACE_CMD_SET_MODE,
|
||||||
RADIO_IFACE_CMD_GET_BANDWIDTH,
|
RADIO_IFACE_CMD_GET_BANDWIDTH,
|
||||||
RADIO_IFACE_CMD_SET_BANDWIDTH,
|
RADIO_IFACE_CMD_SET_BANDWIDTH,
|
||||||
RADIO_IFACE_CMD_GET_SQUELCH_ENABLED,
|
RADIO_IFACE_CMD_GET_SQUELCH_MODE,
|
||||||
RADIO_IFACE_CMD_SET_SQUELCH_ENABLED,
|
RADIO_IFACE_CMD_SET_SQUELCH_MODE,
|
||||||
RADIO_IFACE_CMD_GET_SQUELCH_LEVEL,
|
RADIO_IFACE_CMD_GET_SQUELCH_LEVEL,
|
||||||
RADIO_IFACE_CMD_SET_SQUELCH_LEVEL,
|
RADIO_IFACE_CMD_SET_SQUELCH_LEVEL,
|
||||||
|
RADIO_IFACE_CMD_GET_CTCSS_TONE,
|
||||||
|
RADIO_IFACE_CMD_SET_CTCSS_TONE,
|
||||||
|
RADIO_IFACE_CMD_GET_HIGHPASS,
|
||||||
|
RADIO_IFACE_CMD_SET_HIGHPASS
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
#include <dsp/chain.h>
|
#include <dsp/chain.h>
|
||||||
#include <dsp/noise_reduction/noise_blanker.h>
|
#include <dsp/noise_reduction/noise_blanker.h>
|
||||||
#include <dsp/noise_reduction/fm_if.h>
|
#include <dsp/noise_reduction/fm_if.h>
|
||||||
#include <dsp/noise_reduction/squelch.h>
|
#include <dsp/noise_reduction/power_squelch.h>
|
||||||
|
#include <dsp/noise_reduction/ctcss_squelch.h>
|
||||||
#include <dsp/multirate/rational_resampler.h>
|
#include <dsp/multirate/rational_resampler.h>
|
||||||
#include <dsp/filter/deephasis.h>
|
#include <dsp/filter/deephasis.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
@@ -49,6 +50,22 @@ public:
|
|||||||
ifnrPresets.define("Voice", IFNR_PRESET_VOICE);
|
ifnrPresets.define("Voice", IFNR_PRESET_VOICE);
|
||||||
ifnrPresets.define("Narrow Band", IFNR_PRESET_NARROW_BAND);
|
ifnrPresets.define("Narrow Band", IFNR_PRESET_NARROW_BAND);
|
||||||
|
|
||||||
|
squelchModes.define("off", "Off", SQUELCH_MODE_OFF);
|
||||||
|
squelchModes.define("power", "Power", SQUELCH_MODE_POWER);
|
||||||
|
//squelchModes.define("snr", "SNR", SQUELCH_MODE_SNR);
|
||||||
|
squelchModes.define("ctcss_mute", "CTCSS (Mute)", SQUELCH_MODE_CTCSS_MUTE);
|
||||||
|
squelchModes.define("ctcss_decode", "CTCSS (Decode Only)", SQUELCH_MODE_CTCSS_DECODE);
|
||||||
|
//squelchModes.define("dcs_mute", "DCS (Mute)", SQUELCH_MODE_DCS_MUTE);
|
||||||
|
//squelchModes.define("dcs_decode", "DCS (Decode Only)", SQUELCH_MODE_DCS_DECODE);
|
||||||
|
|
||||||
|
for (int i = 0; i < dsp::noise_reduction::_CTCSS_TONE_COUNT; i++) {
|
||||||
|
float tone = dsp::noise_reduction::CTCSS_TONES[i];
|
||||||
|
char buf[64];
|
||||||
|
sprintf(buf, "%.1fHz", tone);
|
||||||
|
ctcssTones.define((int)round(tone) * 10, buf, (dsp::noise_reduction::CTCSSTone)i);
|
||||||
|
}
|
||||||
|
ctcssTones.define(-1, "Any", dsp::noise_reduction::CTCSS_TONE_ANY);
|
||||||
|
|
||||||
// Initialize the config if it doesn't exist
|
// Initialize the config if it doesn't exist
|
||||||
bool created = false;
|
bool created = false;
|
||||||
config.acquire();
|
config.acquire();
|
||||||
@@ -72,19 +89,24 @@ public:
|
|||||||
|
|
||||||
nb.init(NULL, 500.0 / 24000.0, 10.0);
|
nb.init(NULL, 500.0 / 24000.0, 10.0);
|
||||||
fmnr.init(NULL, 32);
|
fmnr.init(NULL, 32);
|
||||||
squelch.init(NULL, MIN_SQUELCH);
|
powerSquelch.init(NULL, MIN_SQUELCH);
|
||||||
|
|
||||||
ifChain.addBlock(&nb, false);
|
ifChain.addBlock(&nb, false);
|
||||||
ifChain.addBlock(&squelch, false);
|
ifChain.addBlock(&powerSquelch, false);
|
||||||
ifChain.addBlock(&fmnr, false);
|
ifChain.addBlock(&fmnr, false);
|
||||||
|
|
||||||
// Initialize audio DSP chain
|
// Initialize audio DSP chain
|
||||||
afChain.init(&dummyAudioStream);
|
afChain.init(&dummyAudioStream);
|
||||||
|
|
||||||
|
ctcss.init(NULL, 50000.0);
|
||||||
resamp.init(NULL, 250000.0, 48000.0);
|
resamp.init(NULL, 250000.0, 48000.0);
|
||||||
|
hpTaps = dsp::taps::highPass(300.0, 100.0, 48000.0);
|
||||||
|
hpf.init(NULL, hpTaps);
|
||||||
deemp.init(NULL, 50e-6, 48000.0);
|
deemp.init(NULL, 50e-6, 48000.0);
|
||||||
|
|
||||||
|
afChain.addBlock(&ctcss, false);
|
||||||
afChain.addBlock(&resamp, true);
|
afChain.addBlock(&resamp, true);
|
||||||
|
afChain.addBlock(&hpf, false);
|
||||||
afChain.addBlock(&deemp, false);
|
afChain.addBlock(&deemp, false);
|
||||||
|
|
||||||
// Initialize the sink
|
// Initialize the sink
|
||||||
@@ -233,6 +255,33 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Squelch
|
||||||
|
if (_this->squelchAllowed) {
|
||||||
|
ImGui::LeftLabel("Squelch Mode");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Combo(("##_radio_sqelch_mode_" + _this->name).c_str(), &_this->squelchModeId, _this->squelchModes.txt)) {
|
||||||
|
_this->setSquelchMode(_this->squelchModes[_this->squelchModeId]);
|
||||||
|
}
|
||||||
|
switch (_this->squelchModes[_this->squelchModeId]) {
|
||||||
|
case SQUELCH_MODE_POWER:
|
||||||
|
ImGui::LeftLabel("Squelch Level");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::SliderFloat(("##_radio_sqelch_lvl_" + _this->name).c_str(), &_this->squelchLevel, _this->MIN_SQUELCH, _this->MAX_SQUELCH, "%.3fdB")) {
|
||||||
|
_this->setSquelchLevel(_this->squelchLevel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_CTCSS_MUTE:
|
||||||
|
if (_this->squelchModes[_this->squelchModeId] == SQUELCH_MODE_CTCSS_MUTE) {
|
||||||
|
ImGui::LeftLabel("CTCSS Tone");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Combo(("##_radio_ctcss_tone_" + _this->name).c_str(), &_this->ctcssToneId, _this->ctcssTones.txt)) {
|
||||||
|
_this->setCTCSSTone(_this->ctcssTones[_this->ctcssToneId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Noise blanker
|
// Noise blanker
|
||||||
if (_this->nbAllowed) {
|
if (_this->nbAllowed) {
|
||||||
if (ImGui::Checkbox(("Noise blanker (W.I.P.)##_radio_nb_ena_" + _this->name).c_str(), &_this->nbEnabled)) {
|
if (ImGui::Checkbox(("Noise blanker (W.I.P.)##_radio_nb_ena_" + _this->name).c_str(), &_this->nbEnabled)) {
|
||||||
@@ -247,19 +296,6 @@ private:
|
|||||||
if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); }
|
if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Squelch
|
|
||||||
if (ImGui::Checkbox(("Squelch##_radio_sqelch_ena_" + _this->name).c_str(), &_this->squelchEnabled)) {
|
|
||||||
_this->setSquelchEnabled(_this->squelchEnabled);
|
|
||||||
}
|
|
||||||
if (!_this->squelchEnabled && _this->enabled) { style::beginDisabled(); }
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
|
||||||
if (ImGui::SliderFloat(("##_radio_sqelch_lvl_" + _this->name).c_str(), &_this->squelchLevel, _this->MIN_SQUELCH, _this->MAX_SQUELCH, "%.3fdB")) {
|
|
||||||
_this->setSquelchLevel(_this->squelchLevel);
|
|
||||||
}
|
|
||||||
if (!_this->squelchEnabled && _this->enabled) { style::endDisabled(); }
|
|
||||||
|
|
||||||
// FM IF Noise Reduction
|
// FM IF Noise Reduction
|
||||||
if (_this->FMIFNRAllowed) {
|
if (_this->FMIFNRAllowed) {
|
||||||
if (ImGui::Checkbox(("IF Noise Reduction##_radio_fmifnr_ena_" + _this->name).c_str(), &_this->FMIFNREnabled)) {
|
if (ImGui::Checkbox(("IF Noise Reduction##_radio_fmifnr_ena_" + _this->name).c_str(), &_this->FMIFNREnabled)) {
|
||||||
@@ -276,9 +312,53 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// High pass
|
||||||
|
if (_this->highPassAllowed) {
|
||||||
|
if (ImGui::Checkbox(("High Pass##_radio_hpf_" + _this->name).c_str(), &_this->highPass)) {
|
||||||
|
_this->setHighPass(_this->highPass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Demodulator specific menu
|
// Demodulator specific menu
|
||||||
_this->selectedDemod->showMenu();
|
_this->selectedDemod->showMenu();
|
||||||
|
|
||||||
|
// Display the squelch diagnostics
|
||||||
|
switch (_this->squelchModes[_this->squelchModeId]) {
|
||||||
|
case SQUELCH_MODE_CTCSS_MUTE:
|
||||||
|
ImGui::TextUnformatted("Received Tone:");
|
||||||
|
ImGui::SameLine();
|
||||||
|
{
|
||||||
|
auto ctone = _this->ctcss.getCurrentTone();
|
||||||
|
auto dtone = _this->ctcssTones[_this->ctcssToneId];
|
||||||
|
if (ctone != dsp::noise_reduction::CTCSS_TONE_NONE) {
|
||||||
|
if (dtone == dsp::noise_reduction::CTCSS_TONE_ANY || ctone == dtone) {
|
||||||
|
ImGui::TextColored(ImVec4(0, 1, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::TextColored(ImVec4(1, 0, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::TextUnformatted("None");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_CTCSS_DECODE:
|
||||||
|
ImGui::TextUnformatted("Received Tone:");
|
||||||
|
ImGui::SameLine();
|
||||||
|
{
|
||||||
|
auto ctone = _this->ctcss.getCurrentTone();
|
||||||
|
if (ctone != dsp::noise_reduction::CTCSS_TONE_NONE) {
|
||||||
|
ImGui::TextColored(ImVec4(0, 1, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::TextUnformatted("None");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_this->enabled) { style::endDisabled(); }
|
if (!_this->enabled) { style::endDisabled(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,15 +440,20 @@ private:
|
|||||||
maxBandwidth = selectedDemod->getMaxBandwidth();
|
maxBandwidth = selectedDemod->getMaxBandwidth();
|
||||||
bandwidthLocked = selectedDemod->getBandwidthLocked();
|
bandwidthLocked = selectedDemod->getBandwidthLocked();
|
||||||
snapInterval = selectedDemod->getDefaultSnapInterval();
|
snapInterval = selectedDemod->getDefaultSnapInterval();
|
||||||
squelchLevel = MIN_SQUELCH;
|
|
||||||
deempAllowed = selectedDemod->getDeempAllowed();
|
deempAllowed = selectedDemod->getDeempAllowed();
|
||||||
deempId = deempModes.valueId((DeemphasisMode)selectedDemod->getDefaultDeemphasisMode());
|
deempId = deempModes.valueId((DeemphasisMode)selectedDemod->getDefaultDeemphasisMode());
|
||||||
squelchEnabled = false;
|
squelchModeId = squelchModes.valueId(SQUELCH_MODE_OFF);
|
||||||
|
squelchLevel = MIN_SQUELCH;
|
||||||
|
ctcssToneId = ctcssTones.valueId(dsp::noise_reduction::CTCSS_TONE_67Hz);
|
||||||
|
highPass = false;
|
||||||
|
|
||||||
postProcEnabled = selectedDemod->getPostProcEnabled();
|
postProcEnabled = selectedDemod->getPostProcEnabled();
|
||||||
FMIFNRAllowed = selectedDemod->getFMIFNRAllowed();
|
FMIFNRAllowed = selectedDemod->getFMIFNRAllowed();
|
||||||
FMIFNREnabled = false;
|
FMIFNREnabled = false;
|
||||||
fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE);
|
fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE);
|
||||||
nbAllowed = selectedDemod->getNBAllowed();
|
nbAllowed = selectedDemod->getNBAllowed();
|
||||||
|
squelchAllowed = selectedDemod->getSquelchAllowed();
|
||||||
|
highPassAllowed = selectedDemod->getHighPassAllowed();
|
||||||
nbEnabled = false;
|
nbEnabled = false;
|
||||||
nbLevel = 0.0f;
|
nbLevel = 0.0f;
|
||||||
double ifSamplerate = selectedDemod->getIFSampleRate();
|
double ifSamplerate = selectedDemod->getIFSampleRate();
|
||||||
@@ -380,11 +465,24 @@ private:
|
|||||||
if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) {
|
if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) {
|
||||||
snapInterval = config.conf[name][selectedDemod->getName()]["snapInterval"];
|
snapInterval = config.conf[name][selectedDemod->getName()]["snapInterval"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.conf[name][selectedDemod->getName()].contains("squelchMode")) {
|
||||||
|
std::string squelchModeStr = config.conf[name][selectedDemod->getName()]["squelchMode"];
|
||||||
|
if (squelchModes.keyExists(squelchModeStr)) {
|
||||||
|
squelchModeId = squelchModes.keyId(squelchModeStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (config.conf[name][selectedDemod->getName()].contains("squelchLevel")) {
|
if (config.conf[name][selectedDemod->getName()].contains("squelchLevel")) {
|
||||||
squelchLevel = config.conf[name][selectedDemod->getName()]["squelchLevel"];
|
squelchLevel = config.conf[name][selectedDemod->getName()]["squelchLevel"];
|
||||||
}
|
}
|
||||||
if (config.conf[name][selectedDemod->getName()].contains("squelchEnabled")) {
|
if (config.conf[name][selectedDemod->getName()].contains("ctcssTone")) {
|
||||||
squelchEnabled = config.conf[name][selectedDemod->getName()]["squelchEnabled"];
|
int ctcssToneX10 = config.conf[name][selectedDemod->getName()]["ctcssTone"];
|
||||||
|
if (ctcssTones.keyExists(ctcssToneX10)) {
|
||||||
|
ctcssToneId = ctcssTones.keyId(ctcssToneX10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.conf[name][selectedDemod->getName()].contains("highPass")) {
|
||||||
|
highPass = config.conf[name][selectedDemod->getName()]["highPass"];
|
||||||
}
|
}
|
||||||
if (config.conf[name][selectedDemod->getName()].contains("deempMode")) {
|
if (config.conf[name][selectedDemod->getName()].contains("deempMode")) {
|
||||||
if (!config.conf[name][selectedDemod->getName()]["deempMode"].is_string()) {
|
if (!config.conf[name][selectedDemod->getName()]["deempMode"].is_string()) {
|
||||||
@@ -434,16 +532,22 @@ private:
|
|||||||
setFMIFNREnabled(FMIFNRAllowed ? FMIFNREnabled : false);
|
setFMIFNREnabled(FMIFNRAllowed ? FMIFNREnabled : false);
|
||||||
|
|
||||||
// Configure squelch
|
// Configure squelch
|
||||||
|
setSquelchMode(squelchAllowed ? squelchModes[squelchModeId] : SQUELCH_MODE_OFF);
|
||||||
setSquelchLevel(squelchLevel);
|
setSquelchLevel(squelchLevel);
|
||||||
setSquelchEnabled(squelchEnabled);
|
setCTCSSTone(ctcssTones[ctcssToneId]);
|
||||||
|
|
||||||
// Configure AF chain
|
// Configure AF chain
|
||||||
if (postProcEnabled) {
|
if (postProcEnabled) {
|
||||||
// Configure resampler
|
// Configure resampler
|
||||||
afChain.stop();
|
afChain.stop();
|
||||||
resamp.setInSamplerate(selectedDemod->getAFSampleRate());
|
double afsr = selectedDemod->getAFSampleRate();
|
||||||
setAudioSampleRate(audioSampleRate);
|
ctcss.setSamplerate(afsr);
|
||||||
|
resamp.setInSamplerate(afsr);
|
||||||
afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
setAudioSampleRate(audioSampleRate);
|
||||||
|
|
||||||
|
// Configure the HPF
|
||||||
|
setHighPass(highPass && highPassAllowed);
|
||||||
|
|
||||||
// Configure deemphasis
|
// Configure deemphasis
|
||||||
setDeemphasisMode(deempModes[deempId]);
|
setDeemphasisMode(deempModes[deempId]);
|
||||||
@@ -489,12 +593,32 @@ private:
|
|||||||
// Configure resampler
|
// Configure resampler
|
||||||
resamp.setOutSamplerate(audioSampleRate);
|
resamp.setOutSamplerate(audioSampleRate);
|
||||||
|
|
||||||
|
// Configure the HPF sample rate
|
||||||
|
hpTaps = dsp::taps::highPass(300.0, 100.0, audioSampleRate);
|
||||||
|
hpf.setTaps(hpTaps);
|
||||||
|
|
||||||
// Configure deemphasis sample rate
|
// Configure deemphasis sample rate
|
||||||
deemp.setSamplerate(audioSampleRate);
|
deemp.setSamplerate(audioSampleRate);
|
||||||
|
|
||||||
afChain.start();
|
afChain.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setHighPass(bool enabled) {
|
||||||
|
// Update the state
|
||||||
|
highPass = enabled;
|
||||||
|
|
||||||
|
// Check if post-processing is enabled and that a demodulator is selected
|
||||||
|
if (!postProcEnabled || !selectedDemod) { return; }
|
||||||
|
|
||||||
|
// Set the state of the HPF in the AF chain
|
||||||
|
afChain.setBlockEnabled(&hpf, enabled, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
|
||||||
|
// Save config
|
||||||
|
config.acquire();
|
||||||
|
config.conf[name][selectedDemod->getName()]["highPass"] = enabled;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
void setDeemphasisMode(DeemphasisMode mode) {
|
void setDeemphasisMode(DeemphasisMode mode) {
|
||||||
deempId = deempModes.valueId(mode);
|
deempId = deempModes.valueId(mode);
|
||||||
if (!postProcEnabled || !selectedDemod) { return; }
|
if (!postProcEnabled || !selectedDemod) { return; }
|
||||||
@@ -522,6 +646,7 @@ private:
|
|||||||
void setNBLevel(float level) {
|
void setNBLevel(float level) {
|
||||||
nbLevel = std::clamp<float>(level, MIN_NB, MAX_NB);
|
nbLevel = std::clamp<float>(level, MIN_NB, MAX_NB);
|
||||||
nb.setLevel(nbLevel);
|
nb.setLevel(nbLevel);
|
||||||
|
if (!selectedDemod) { return; }
|
||||||
|
|
||||||
// Save config
|
// Save config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
@@ -529,20 +654,59 @@ private:
|
|||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSquelchEnabled(bool enable) {
|
void setSquelchMode(SquelchMode mode) {
|
||||||
squelchEnabled = enable;
|
squelchModeId = squelchModes.valueId(mode);
|
||||||
if (!selectedDemod) { return; }
|
if (!selectedDemod) { return; }
|
||||||
ifChain.setBlockEnabled(&squelch, squelchEnabled, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
|
|
||||||
|
// Disable all squelch blocks
|
||||||
|
ifChain.disableBlock(&powerSquelch, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
|
||||||
|
afChain.disableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
|
||||||
|
// Enable the block depending on the mode
|
||||||
|
switch (mode) {
|
||||||
|
case SQUELCH_MODE_OFF:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_POWER:
|
||||||
|
// Enable the power squelch block
|
||||||
|
ifChain.enableBlock(&powerSquelch, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_SNR:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_CTCSS_MUTE:
|
||||||
|
// Set the required tone and enable the CTCSS squelch block
|
||||||
|
ctcss.setRequiredTone(ctcssTones[ctcssToneId]);
|
||||||
|
afChain.enableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_CTCSS_DECODE:
|
||||||
|
// Set the required tone to none and enable the CTCSS squelch block
|
||||||
|
ctcss.setRequiredTone(dsp::noise_reduction::CTCSS_TONE_NONE);
|
||||||
|
afChain.enableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_DCS_MUTE:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_DCS_DECODE:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Save config
|
// Save config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[name][selectedDemod->getName()]["squelchEnabled"] = squelchEnabled;
|
config.conf[name][selectedDemod->getName()]["squelchMode"] = squelchModes.key(squelchModeId);
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSquelchLevel(float level) {
|
void setSquelchLevel(float level) {
|
||||||
squelchLevel = std::clamp<float>(level, MIN_SQUELCH, MAX_SQUELCH);
|
squelchLevel = std::clamp<float>(level, MIN_SQUELCH, MAX_SQUELCH);
|
||||||
squelch.setLevel(squelchLevel);
|
powerSquelch.setLevel(squelchLevel);
|
||||||
|
if (!selectedDemod) { return; }
|
||||||
|
|
||||||
// Save config
|
// Save config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
@@ -550,6 +714,24 @@ private:
|
|||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCTCSSTone(dsp::noise_reduction::CTCSSTone tone) {
|
||||||
|
// Check for an invalid value
|
||||||
|
if (tone == dsp::noise_reduction::CTCSS_TONE_NONE) { return; }
|
||||||
|
|
||||||
|
// If not in CTCSS mute mode, do nothing
|
||||||
|
if (squelchModes[squelchModeId] != SQUELCH_MODE_CTCSS_MUTE) { return; }
|
||||||
|
|
||||||
|
// Set the tone
|
||||||
|
ctcssToneId = ctcssTones.valueId(tone);
|
||||||
|
ctcss.setRequiredTone(tone);
|
||||||
|
if (!selectedDemod) { return; }
|
||||||
|
|
||||||
|
// Save config
|
||||||
|
config.acquire();
|
||||||
|
config.conf[name][selectedDemod->getName()]["ctcssTone"] = ctcssTones.key(ctcssToneId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
void setFMIFNREnabled(bool enabled) {
|
void setFMIFNREnabled(bool enabled) {
|
||||||
FMIFNREnabled = enabled;
|
FMIFNREnabled = enabled;
|
||||||
if (!selectedDemod) { return; }
|
if (!selectedDemod) { return; }
|
||||||
@@ -619,13 +801,13 @@ private:
|
|||||||
if (_this->bandwidthLocked) { return; }
|
if (_this->bandwidthLocked) { return; }
|
||||||
_this->setBandwidth(*_in);
|
_this->setBandwidth(*_in);
|
||||||
}
|
}
|
||||||
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_ENABLED && out) {
|
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_MODE && out) {
|
||||||
bool* _out = (bool*)out;
|
SquelchMode* _out = (SquelchMode*)out;
|
||||||
*_out = _this->squelchEnabled;
|
*_out = _this->squelchModes[_this->squelchModeId];
|
||||||
}
|
}
|
||||||
else if (code == RADIO_IFACE_CMD_SET_SQUELCH_ENABLED && in && _this->enabled) {
|
else if (code == RADIO_IFACE_CMD_SET_SQUELCH_MODE && in && _this->enabled) {
|
||||||
bool* _in = (bool*)in;
|
SquelchMode* _in = (SquelchMode*)in;
|
||||||
_this->setSquelchEnabled(*_in);
|
_this->setSquelchMode(*_in);
|
||||||
}
|
}
|
||||||
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_LEVEL && out) {
|
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_LEVEL && out) {
|
||||||
float* _out = (float*)out;
|
float* _out = (float*)out;
|
||||||
@@ -635,6 +817,22 @@ private:
|
|||||||
float* _in = (float*)in;
|
float* _in = (float*)in;
|
||||||
_this->setSquelchLevel(*_in);
|
_this->setSquelchLevel(*_in);
|
||||||
}
|
}
|
||||||
|
else if (code == RADIO_IFACE_CMD_GET_CTCSS_TONE && out) {
|
||||||
|
dsp::noise_reduction::CTCSSTone* _out = (dsp::noise_reduction::CTCSSTone*)out;
|
||||||
|
*_out = _this->ctcssTones[_this->ctcssToneId];
|
||||||
|
}
|
||||||
|
else if (code == RADIO_IFACE_CMD_SET_CTCSS_TONE && in && _this->enabled) {
|
||||||
|
dsp::noise_reduction::CTCSSTone* _in = (dsp::noise_reduction::CTCSSTone*)in;
|
||||||
|
_this->setCTCSSTone(*_in);
|
||||||
|
}
|
||||||
|
else if (code == RADIO_IFACE_CMD_GET_HIGHPASS && out) {
|
||||||
|
bool* _out = (bool*)out;
|
||||||
|
*_out = _this->highPass;
|
||||||
|
}
|
||||||
|
else if (code == RADIO_IFACE_CMD_SET_HIGHPASS && in && _this->enabled) {
|
||||||
|
bool* _in = (bool*)in;
|
||||||
|
_this->setHighPass(*_in);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -655,12 +853,15 @@ private:
|
|||||||
dsp::chain<dsp::complex_t> ifChain;
|
dsp::chain<dsp::complex_t> ifChain;
|
||||||
dsp::noise_reduction::NoiseBlanker nb;
|
dsp::noise_reduction::NoiseBlanker nb;
|
||||||
dsp::noise_reduction::FMIF fmnr;
|
dsp::noise_reduction::FMIF fmnr;
|
||||||
dsp::noise_reduction::Squelch squelch;
|
dsp::noise_reduction::PowerSquelch powerSquelch;
|
||||||
|
|
||||||
// Audio chain
|
// Audio chain
|
||||||
dsp::stream<dsp::stereo_t> dummyAudioStream;
|
dsp::stream<dsp::stereo_t> dummyAudioStream;
|
||||||
dsp::chain<dsp::stereo_t> afChain;
|
dsp::chain<dsp::stereo_t> afChain;
|
||||||
|
dsp::noise_reduction::CTCSSSquelch ctcss;
|
||||||
dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
|
dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
|
||||||
|
dsp::tap<float> hpTaps;
|
||||||
|
dsp::filter::FIR<dsp::stereo_t, float> hpf;
|
||||||
dsp::filter::Deemphasis<dsp::stereo_t> deemp;
|
dsp::filter::Deemphasis<dsp::stereo_t> deemp;
|
||||||
|
|
||||||
SinkManager::Stream stream;
|
SinkManager::Stream stream;
|
||||||
@@ -669,6 +870,8 @@ private:
|
|||||||
|
|
||||||
OptionList<std::string, DeemphasisMode> deempModes;
|
OptionList<std::string, DeemphasisMode> deempModes;
|
||||||
OptionList<std::string, IFNRPreset> ifnrPresets;
|
OptionList<std::string, IFNRPreset> ifnrPresets;
|
||||||
|
OptionList<std::string, SquelchMode> squelchModes;
|
||||||
|
OptionList<int, dsp::noise_reduction::CTCSSTone> ctcssTones;
|
||||||
|
|
||||||
double audioSampleRate = 48000.0;
|
double audioSampleRate = 48000.0;
|
||||||
float minBandwidth;
|
float minBandwidth;
|
||||||
@@ -679,8 +882,13 @@ private:
|
|||||||
int selectedDemodID = 1;
|
int selectedDemodID = 1;
|
||||||
bool postProcEnabled;
|
bool postProcEnabled;
|
||||||
|
|
||||||
bool squelchEnabled = false;
|
int squelchModeId = 0;
|
||||||
float squelchLevel;
|
float squelchLevel;
|
||||||
|
int ctcssToneId = 0;
|
||||||
|
bool squelchAllowed = false;
|
||||||
|
|
||||||
|
bool highPass = false;
|
||||||
|
bool highPassAllowed = false;
|
||||||
|
|
||||||
int deempId = 0;
|
int deempId = 0;
|
||||||
bool deempAllowed;
|
bool deempAllowed;
|
||||||
|
|||||||
28
source_modules/dragonlabs_source/CMakeLists.txt
Normal file
28
source_modules/dragonlabs_source/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(dragonlabs_source)
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Debugging only
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
|
||||||
|
target_link_libraries(dragonlabs_source PRIVATE PkgConfig::libusb)
|
||||||
|
target_include_directories(dragonlabs_source PRIVATE "C:/Users/ryzerth/Documents/DragonLabs/products/KrakenSlayer/host/src")
|
||||||
|
|
||||||
|
target_include_directories(dragonlabs_source PRIVATE "C:/Program Files/DragonLabs/CR/include")
|
||||||
|
target_link_directories(dragonlabs_source PRIVATE "C:/Program Files/DragonLabs/CR/lib")
|
||||||
|
target_link_libraries(dragonlabs_source PRIVATE dlcr)
|
||||||
|
elseif (ANDROID)
|
||||||
|
# Not supported yet...
|
||||||
|
else (MSVC)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBDLCR REQUIRED libdlcr)
|
||||||
|
|
||||||
|
target_include_directories(dragonlabs_source PRIVATE ${LIBDLCR_INCLUDE_DIRS})
|
||||||
|
target_link_directories(dragonlabs_source PRIVATE ${LIBDLCR_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(dragonlabs_source PRIVATE ${LIBDLCR_LIBRARIES})
|
||||||
|
endif ()
|
||||||
448
source_modules/dragonlabs_source/src/main.cpp
Normal file
448
source_modules/dragonlabs_source/src/main.cpp
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/smgui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <utils/optionlist.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <dlcr.h>
|
||||||
|
#include <dlcr_internal.h>
|
||||||
|
#include <lmx2572.h>
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "dragonlabs_source",
|
||||||
|
/* Description: */ "Dragon Labs Source Module",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
class DragonLabsSourceModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
DragonLabsSourceModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
// Load the register debugging values
|
||||||
|
strcpy(clkRegStr, "00");
|
||||||
|
strcpy(clkValStr, "--");
|
||||||
|
strcpy(synRegStr, "00");
|
||||||
|
strcpy(synValStr, "--");
|
||||||
|
strcpy(adcRegStr, "00");
|
||||||
|
strcpy(adcValStr, "--");
|
||||||
|
strcpy(tunRegStr, "00");
|
||||||
|
strcpy(tunValStr, "--");
|
||||||
|
|
||||||
|
// Define the clock sources
|
||||||
|
clockSources.define("internal", "Internal", DLCR_CLOCK_INTERNAL);
|
||||||
|
clockSources.define("external", "External", DLCR_CLOCK_EXTERNAL);
|
||||||
|
|
||||||
|
// Define the channels
|
||||||
|
channels.define("chan1", "Channel 1", 0);
|
||||||
|
channels.define("chan2", "Channel 2", 1);
|
||||||
|
channels.define("chan3", "Channel 3", 2);
|
||||||
|
channels.define("chan4", "Channel 4", 3);
|
||||||
|
channels.define("chan5", "Channel 5", 4);
|
||||||
|
channels.define("chan6", "Channel 6", 5);
|
||||||
|
channels.define("chan7", "Channel 7", 6);
|
||||||
|
channels.define("chan8", "Channel 8", 7);
|
||||||
|
|
||||||
|
// Hardcode the samplerate
|
||||||
|
sampleRate = 12.5e6;
|
||||||
|
|
||||||
|
// Fill out the source handler
|
||||||
|
handler.ctx = this;
|
||||||
|
handler.selectHandler = menuSelected;
|
||||||
|
handler.deselectHandler = menuDeselected;
|
||||||
|
handler.menuHandler = menuHandler;
|
||||||
|
handler.startHandler = start;
|
||||||
|
handler.stopHandler = stop;
|
||||||
|
handler.tuneHandler = tune;
|
||||||
|
handler.stream = &stream;
|
||||||
|
|
||||||
|
// Refresh devices
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
// Select first (TODO: Select from config)
|
||||||
|
select("");
|
||||||
|
|
||||||
|
sigpath::sourceManager.registerSource("Dragon Labs", &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
~DragonLabsSourceModule() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refresh() {
|
||||||
|
devices.clear();
|
||||||
|
|
||||||
|
// Get the list of devices
|
||||||
|
dlcr_info_t* list;
|
||||||
|
int count = dlcr_list_devices(&list);
|
||||||
|
if (count < 0) {
|
||||||
|
flog::error("Failed to list devices");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the menu list
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Format device name
|
||||||
|
std::string devName = "CR8-1725 ";
|
||||||
|
devName += " [";
|
||||||
|
devName += list[i].serial;
|
||||||
|
devName += ']';
|
||||||
|
|
||||||
|
// Save device
|
||||||
|
devices.define(list[i].serial, devName, list[i].serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the device list
|
||||||
|
dlcr_free_device_list(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void select(const std::string& serial) {
|
||||||
|
// If there are no devices, give up
|
||||||
|
if (devices.empty()) {
|
||||||
|
selectedSerial.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the serial was not found, select the first available serial
|
||||||
|
if (!devices.keyExists(serial)) {
|
||||||
|
select(devices.key(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save serial number
|
||||||
|
selectedSerial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuSelected(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Menu Select!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuDeselected(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Menu Deselect!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::high_resolution_clock::time_point last;
|
||||||
|
|
||||||
|
static void start(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
if (_this->running) { return; }
|
||||||
|
|
||||||
|
// Open the device
|
||||||
|
int err = dlcr_open(&_this->openDev, _this->selectedSerial.c_str());
|
||||||
|
if (err) {
|
||||||
|
flog::error("Failed to open device '{}': {}", _this->selectedSerial, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flog::debug("Device open");
|
||||||
|
|
||||||
|
// Configure the device
|
||||||
|
dlcr_set_freq(_this->openDev, DLCR_CHAN_ALL, _this->freq, true);
|
||||||
|
dlcr_set_lna_gain(_this->openDev, DLCR_CHAN_ALL, _this->lnaGain);
|
||||||
|
dlcr_set_mixer_gain(_this->openDev, DLCR_CHAN_ALL, _this->mixerGain);
|
||||||
|
dlcr_set_vga_gain(_this->openDev, DLCR_CHAN_ALL, _this->vgaGain);
|
||||||
|
|
||||||
|
// Start device
|
||||||
|
dlcr_start(_this->openDev, 0/*TODO*/, callback, _this);
|
||||||
|
|
||||||
|
_this->running = true;
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Start!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
if (!_this->running) { return; }
|
||||||
|
_this->running = false;
|
||||||
|
|
||||||
|
// Stop device
|
||||||
|
dlcr_stop(_this->openDev);
|
||||||
|
|
||||||
|
// Close device
|
||||||
|
dlcr_close(_this->openDev);
|
||||||
|
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Stop!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune(double freq, void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_freq(_this->openDev, DLCR_CHAN_ALL, freq, _this->docal);
|
||||||
|
}
|
||||||
|
_this->calibrated = false;
|
||||||
|
_this->freq = freq;
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
double lmxFreq = 100e6;
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::BeginDisabled(); }
|
||||||
|
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Combo(CONCAT("##_dlcr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
||||||
|
_this->select(_this->devices.key(_this->devId));
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Button(CONCAT("Refresh##_dlcr_refr_", _this->name))) {
|
||||||
|
_this->refresh();
|
||||||
|
_this->select(_this->selectedSerial);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Clock Source");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_dlcr_clock_", _this->name), &_this->clockSourceId, _this->clockSources.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_clock_source(_this->openDev, _this->clockSources[_this->clockSourceId]);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Channel");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::Combo(CONCAT("##_dlcr_chan_", _this->name), &_this->channelId, _this->channels.txt);
|
||||||
|
|
||||||
|
SmGui::LeftLabel("LNA Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_dlcr_lna_gain_", _this->name), &_this->lnaGain, 0, 14)) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_lna_gain(_this->openDev, DLCR_CHAN_ALL, _this->lnaGain);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Mixer Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_dlcr_mixer_gain_", _this->name), &_this->mixerGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_mixer_gain(_this->openDev, DLCR_CHAN_ALL, _this->mixerGain);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("VGA Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_dlcr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_vga_gain(_this->openDev, DLCR_CHAN_ALL, _this->vgaGain);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::Checkbox(CONCAT("Debug##_dlcr_debug_", _this->name), &_this->debug);
|
||||||
|
|
||||||
|
if (_this->debug) {
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Clockgen Reg");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_clk_reg_", _this->name), _this->clkRegStr, 256);
|
||||||
|
SmGui::LeftLabel("Clockgen Value");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_clk_val_", _this->name), _this->clkValStr, 256);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Read##_dlcr_clk_rd_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
uint8_t val;
|
||||||
|
dlcr_si5351c_read_reg(_this->openDev, std::stoi(_this->clkRegStr, NULL, 16), &val);
|
||||||
|
sprintf(_this->clkValStr, "%02X", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Write##_dlcr_clk_wr_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_si5351c_write_reg(_this->openDev, std::stoi(_this->clkRegStr, NULL, 16), std::stoi(_this->clkValStr, NULL, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Synth Reg");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_syn_reg_", _this->name), _this->synRegStr, 256);
|
||||||
|
SmGui::LeftLabel("Synth Value");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_syn_val_", _this->name), _this->synValStr, 256);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Read##_dlcr_syn_rd_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
uint16_t val;
|
||||||
|
dlcr_lmx2572_read_reg(_this->openDev, std::stoi(_this->synRegStr, NULL, 16), &val);
|
||||||
|
sprintf(_this->synValStr, "%02X", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Write##_dlcr_syn_wr_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_lmx2572_write_reg(_this->openDev, std::stoi(_this->synRegStr, NULL, 16), std::stoi(_this->synValStr, NULL, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("ADC Reg");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_adc_reg_", _this->name), _this->adcRegStr, 256);
|
||||||
|
SmGui::LeftLabel("ADC Value");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_adc_val_", _this->name), _this->adcValStr, 256);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Read##_dlcr_adc_rd_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
uint8_t val;
|
||||||
|
dlcr_mcp37211_read_reg(_this->openDev, std::stoi(_this->adcRegStr, NULL, 16), &val);
|
||||||
|
sprintf(_this->adcValStr, "%02X", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Write##_dlcr_adc_wr_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_mcp37211_write_reg(_this->openDev, std::stoi(_this->adcRegStr, NULL, 16), std::stoi(_this->adcValStr, NULL, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Tuner Reg");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_tun_reg_", _this->name), _this->tunRegStr, 256);
|
||||||
|
SmGui::LeftLabel("Tuner Value");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_tun_val_", _this->name), _this->tunValStr, 256);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Read##_dlcr_tun_rd_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
uint8_t val[8];
|
||||||
|
dlcr_r860_read_reg(_this->openDev, DLCR_TUNER_ALL, std::stoi(_this->tunRegStr, NULL, 16), val);
|
||||||
|
sprintf(_this->tunValStr, "%02X", val[0]);
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
printf("TUNER[%d][0x%02X] = 0x%02X\n", i, std::stoi(_this->tunRegStr, NULL, 16), val[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Write##_dlcr_tun_wr_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_r860_write_reg(_this->openDev, DLCR_TUNER_ALL, std::stoi(_this->tunRegStr, NULL, 16), std::stoi(_this->tunValStr, NULL, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Synth Freq");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
ImGui::InputDouble("##_dlcr_synth_freq", &_this->synthFreq);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Tune##_dlcr_synth_freq", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
lmx2572_tune(_this->openDev, _this->synthFreq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Shutdown##_dlcr_synth_freq", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
lmx2572_shutdown(_this->openDev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::Checkbox("Calibrate##_dlcr_cal", &_this->docal);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void callback(dlcr_complex_t* samples[DLCR_CHANNEL_COUNT], size_t count, size_t drops, void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
|
||||||
|
// Copy the data to the stream
|
||||||
|
memcpy(_this->stream.writeBuf, samples[_this->channelId], count * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
// Send the samples
|
||||||
|
_this->stream.swap(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
double sampleRate;
|
||||||
|
SourceManager::SourceHandler handler;
|
||||||
|
bool running = false;
|
||||||
|
double freq = 100e6;
|
||||||
|
|
||||||
|
OptionList<std::string, std::string> devices;
|
||||||
|
|
||||||
|
bool debug = false;
|
||||||
|
char clkRegStr[256];
|
||||||
|
char clkValStr[256];
|
||||||
|
char synRegStr[256];
|
||||||
|
char synValStr[256];
|
||||||
|
char adcRegStr[256];
|
||||||
|
char adcValStr[256];
|
||||||
|
char tunRegStr[256];
|
||||||
|
char tunValStr[256];
|
||||||
|
double synthFreq = 100e6;
|
||||||
|
bool docal = false;
|
||||||
|
int devId = 0;
|
||||||
|
int clockSourceId = 0;
|
||||||
|
int channelId = 0;
|
||||||
|
int lnaGain = 0;
|
||||||
|
int mixerGain = 0;
|
||||||
|
int vgaGain = 0;
|
||||||
|
std::string selectedSerial;
|
||||||
|
dlcr_t* openDev = NULL;
|
||||||
|
|
||||||
|
bool calibrated = false;
|
||||||
|
|
||||||
|
dsp::stream<dsp::complex_t> stream;
|
||||||
|
OptionList<std::string, dlcr_clock_t> clockSources;
|
||||||
|
OptionList<std::string, int> channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new DragonLabsSourceModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (DragonLabsSourceModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user