mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-03-26 17:45:28 +01:00
add flex decoder menu entry and fix pocsag decoding
This commit is contained in:
parent
3fc893568a
commit
ef42ea01d8
@ -1,8 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <signal_path/vfo_manager.h>
|
||||||
|
|
||||||
class Decoder {
|
class Decoder {
|
||||||
public:
|
public:
|
||||||
|
virtual ~Decoder() {}
|
||||||
virtual void showMenu();
|
virtual void showMenu() {};
|
||||||
|
virtual void setVFO(VFOManager::VFO* vfo) = 0;
|
||||||
|
virtual void start() = 0;
|
||||||
|
virtual void stop() = 0;
|
||||||
};
|
};
|
@ -5,24 +5,11 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
#include <module.h>
|
#include <module.h>
|
||||||
#include <filesystem>
|
|
||||||
#include <dsp/stream.h>
|
|
||||||
#include <dsp/buffer/reshaper.h>
|
|
||||||
#include <dsp/multirate/rational_resampler.h>
|
|
||||||
#include <dsp/sink/handler_sink.h>
|
|
||||||
#include <gui/widgets/folder_select.h>
|
#include <gui/widgets/folder_select.h>
|
||||||
#include <gui/widgets/symbol_diagram.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <chrono>
|
|
||||||
#include <dsp/demod/quadrature.h>
|
|
||||||
#include <dsp/clock_recovery/mm.h>
|
|
||||||
#include <dsp/taps/root_raised_cosine.h>
|
|
||||||
#include <dsp/correction/dc_blocker.h>
|
|
||||||
#include <dsp/loop/fast_agc.h>
|
|
||||||
#include <utils/optionlist.h>
|
#include <utils/optionlist.h>
|
||||||
#include <dsp/digital/binary_slicer.h>
|
#include "decoder.h"
|
||||||
#include <dsp/routing/doubler.h>
|
#include "pocsag/decoder.h"
|
||||||
#include "pocsag/pocsag.h"
|
#include "flex/decoder.h"
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
@ -34,77 +21,29 @@ SDRPP_MOD_INFO{
|
|||||||
/* Max instances */ -1
|
/* Max instances */ -1
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* msgTypes[] = {
|
|
||||||
"Numeric",
|
|
||||||
"Unknown (0b01)",
|
|
||||||
"Unknown (0b10)",
|
|
||||||
"Alphanumeric",
|
|
||||||
};
|
|
||||||
|
|
||||||
ConfigManager config;
|
ConfigManager config;
|
||||||
|
|
||||||
#define INPUT_SAMPLE_RATE 24000.0
|
|
||||||
#define INPUT_BANDWIDTH 12500.0
|
|
||||||
#define INPUT_BAUD_RATE 2400.0
|
|
||||||
|
|
||||||
enum Protocol {
|
enum Protocol {
|
||||||
|
PROTOCOL_INVALID = -1,
|
||||||
PROTOCOL_POCSAG,
|
PROTOCOL_POCSAG,
|
||||||
PROTOCOL_FLEX
|
PROTOCOL_FLEX
|
||||||
};
|
};
|
||||||
|
|
||||||
class PagerDecoderModule : public ModuleManager::Instance {
|
class PagerDecoderModule : public ModuleManager::Instance {
|
||||||
public:
|
public:
|
||||||
PagerDecoderModule(std::string name) : diag(0.6, 2400) {
|
PagerDecoderModule(std::string name) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
|
|
||||||
// Define protocols
|
// Define protocols
|
||||||
protocols.define("POCSAG", PROTOCOL_POCSAG);
|
protocols.define("POCSAG", PROTOCOL_POCSAG);
|
||||||
protocols.define("FLEX", PROTOCOL_FLEX);
|
protocols.define("FLEX", PROTOCOL_FLEX);
|
||||||
|
|
||||||
// Load config
|
// Initialize VFO with default values
|
||||||
config.acquire();
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 12500, 24000, 12500, 12500, true);
|
||||||
if (!config.conf.contains(name)) {
|
|
||||||
config.conf[name]["showLines"] = false;
|
|
||||||
}
|
|
||||||
showLines = config.conf[name]["showLines"];
|
|
||||||
if (showLines) {
|
|
||||||
diag.lines.push_back(-1.0);
|
|
||||||
diag.lines.push_back(-1.0/3.0);
|
|
||||||
diag.lines.push_back(1.0/3.0);
|
|
||||||
diag.lines.push_back(1.0);
|
|
||||||
}
|
|
||||||
config.release(true);
|
|
||||||
|
|
||||||
// Initialize VFO
|
|
||||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
|
|
||||||
vfo->setSnapInterval(1);
|
vfo->setSnapInterval(1);
|
||||||
|
|
||||||
// Initialize DSP here (negative dev to invert signal)
|
// Select the protocol
|
||||||
demod.init(vfo->output, -4500.0, INPUT_SAMPLE_RATE);
|
selectProtocol(PROTOCOL_POCSAG);
|
||||||
dcBlock.init(&demod.out, 0.001);
|
|
||||||
float taps[] = { 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f };
|
|
||||||
shape = dsp::taps::fromArray<float>(10, taps);
|
|
||||||
fir.init(&dcBlock.out, shape);
|
|
||||||
recov.init(&fir.out, INPUT_SAMPLE_RATE/INPUT_BAUD_RATE, 1e5, 0.1, 0.05);
|
|
||||||
doubler.init(&recov.out);
|
|
||||||
slicer.init(&doubler.outB);
|
|
||||||
dataHandler.init(&slicer.out, _dataHandler, this);
|
|
||||||
reshape.init(&doubler.outA, 2400.0, (INPUT_BAUD_RATE / 30.0) - 2400.0);
|
|
||||||
diagHandler.init(&reshape.out, _diagHandler, this);
|
|
||||||
|
|
||||||
// Initialize decode
|
|
||||||
decoder.onMessage.bind(&PagerDecoderModule::messageHandler, this);
|
|
||||||
|
|
||||||
// Start DSP Here
|
|
||||||
demod.start();
|
|
||||||
dcBlock.start();
|
|
||||||
fir.start();
|
|
||||||
recov.start();
|
|
||||||
doubler.start();
|
|
||||||
slicer.start();
|
|
||||||
dataHandler.start();
|
|
||||||
reshape.start();
|
|
||||||
diagHandler.start();
|
|
||||||
|
|
||||||
gui::menu.registerEntry(name, menuHandler, this, this);
|
gui::menu.registerEntry(name, menuHandler, this, this);
|
||||||
}
|
}
|
||||||
@ -113,15 +52,8 @@ public:
|
|||||||
gui::menu.removeEntry(name);
|
gui::menu.removeEntry(name);
|
||||||
// Stop DSP
|
// Stop DSP
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
demod.stop();
|
decoder->stop();
|
||||||
dcBlock.stop();
|
decoder.reset();
|
||||||
fir.stop();
|
|
||||||
recov.stop();
|
|
||||||
doubler.stop();
|
|
||||||
slicer.stop();
|
|
||||||
dataHandler.stop();
|
|
||||||
reshape.stop();
|
|
||||||
diagHandler.stop();
|
|
||||||
sigpath::vfoManager.deleteVFO(vfo);
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,36 +64,17 @@ public:
|
|||||||
|
|
||||||
void enable() {
|
void enable() {
|
||||||
double bw = gui::waterfall.getBandwidth();
|
double bw = gui::waterfall.getBandwidth();
|
||||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), 12500, 24000, 12500, 12500, true);
|
||||||
vfo->setSnapInterval(250);
|
vfo->setSnapInterval(1);
|
||||||
|
|
||||||
// Start DSP
|
decoder->setVFO(vfo);
|
||||||
demod.start();
|
decoder->start();
|
||||||
dcBlock.start();
|
|
||||||
fir.start();
|
|
||||||
recov.start();
|
|
||||||
doubler.start();
|
|
||||||
slicer.start();
|
|
||||||
dataHandler.start();
|
|
||||||
reshape.start();
|
|
||||||
diagHandler.start();
|
|
||||||
|
|
||||||
enabled = true;
|
enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disable() {
|
void disable() {
|
||||||
demod.stop();
|
decoder->stop();
|
||||||
dcBlock.stop();
|
|
||||||
fir.stop();
|
|
||||||
recov.stop();
|
|
||||||
doubler.stop();
|
|
||||||
slicer.stop();
|
|
||||||
dataHandler.stop();
|
|
||||||
reshape.stop();
|
|
||||||
diagHandler.stop();
|
|
||||||
reshape.stop();
|
|
||||||
diagHandler.stop();
|
|
||||||
|
|
||||||
sigpath::vfoManager.deleteVFO(vfo);
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
enabled = false;
|
enabled = false;
|
||||||
}
|
}
|
||||||
@ -170,6 +83,36 @@ public:
|
|||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void selectProtocol(Protocol newProto) {
|
||||||
|
// Cannot change while disabled
|
||||||
|
if (!enabled) { return; }
|
||||||
|
|
||||||
|
// If the protocol hasn't changed, no need to do anything
|
||||||
|
if (newProto == proto) { return; }
|
||||||
|
|
||||||
|
// Delete current decoder
|
||||||
|
decoder.reset();
|
||||||
|
|
||||||
|
// Create a new decoder
|
||||||
|
switch (newProto) {
|
||||||
|
case PROTOCOL_POCSAG:
|
||||||
|
decoder = std::make_unique<POCSAGDecoder>(name, vfo);
|
||||||
|
break;
|
||||||
|
case PROTOCOL_FLEX:
|
||||||
|
decoder = std::make_unique<FLEXDecoder>(name, vfo);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
flog::error("Tried to select unknown pager protocol");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the new decoder
|
||||||
|
decoder->start();
|
||||||
|
|
||||||
|
// Save selected protocol
|
||||||
|
proto = newProto;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void menuHandler(void* ctx) {
|
static void menuHandler(void* ctx) {
|
||||||
PagerDecoderModule* _this = (PagerDecoderModule*)ctx;
|
PagerDecoderModule* _this = (PagerDecoderModule*)ctx;
|
||||||
@ -181,54 +124,28 @@ private:
|
|||||||
ImGui::LeftLabel("Protocol");
|
ImGui::LeftLabel("Protocol");
|
||||||
ImGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
if (ImGui::Combo(("##pager_decoder_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
|
if (ImGui::Combo(("##pager_decoder_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
|
||||||
// TODO
|
_this->selectProtocol(_this->protocols.value(_this->protoId));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(menuWidth);
|
if (_this->decoder) { _this->decoder->showMenu(); }
|
||||||
_this->diag.draw();
|
|
||||||
|
ImGui::Button(("Record##pager_decoder_show_" + _this->name).c_str(), ImVec2(menuWidth, 0));
|
||||||
|
ImGui::Button(("Show Messages##pager_decoder_show_" + _this->name).c_str(), ImVec2(menuWidth, 0));
|
||||||
|
|
||||||
if (!_this->enabled) { style::endDisabled(); }
|
if (!_this->enabled) { style::endDisabled(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _dataHandler(uint8_t* data, int count, void* ctx) {
|
|
||||||
PagerDecoderModule* _this = (PagerDecoderModule*)ctx;
|
|
||||||
_this->decoder.process(data, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _diagHandler(float* data, int count, void* ctx) {
|
|
||||||
PagerDecoderModule* _this = (PagerDecoderModule*)ctx;
|
|
||||||
float* buf = _this->diag.acquireBuffer();
|
|
||||||
memcpy(buf, data, count * sizeof(float));
|
|
||||||
_this->diag.releaseBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void messageHandler(pocsag::Address addr, pocsag::MessageType type, const std::string& msg) {
|
|
||||||
flog::debug("[{}]: '{}'", (uint32_t)addr, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
|
|
||||||
|
Protocol proto = PROTOCOL_INVALID;
|
||||||
int protoId = 0;
|
int protoId = 0;
|
||||||
|
|
||||||
OptionList<std::string, Protocol> protocols;
|
OptionList<std::string, Protocol> protocols;
|
||||||
|
|
||||||
pocsag::Decoder decoder;
|
|
||||||
|
|
||||||
// DSP Chain
|
// DSP Chain
|
||||||
VFOManager::VFO* vfo;
|
VFOManager::VFO* vfo;
|
||||||
dsp::demod::Quadrature demod;
|
std::unique_ptr<Decoder> decoder;
|
||||||
dsp::correction::DCBlocker<float> dcBlock;
|
|
||||||
dsp::tap<float> shape;
|
|
||||||
dsp::filter::FIR<float, float> fir;
|
|
||||||
dsp::clock_recovery::MM<float> recov;
|
|
||||||
dsp::routing::Doubler<float> doubler;
|
|
||||||
dsp::digital::BinarySlicer slicer;
|
|
||||||
dsp::buffer::Reshaper<float> reshape;
|
|
||||||
dsp::sink::Handler<uint8_t> dataHandler;
|
|
||||||
dsp::sink::Handler<float> diagHandler;
|
|
||||||
|
|
||||||
ImGui::SymbolDiagram diag;
|
|
||||||
|
|
||||||
bool showLines = false;
|
bool showLines = false;
|
||||||
};
|
};
|
||||||
|
@ -1,28 +1,107 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "../decoder.h"
|
#include "../decoder.h"
|
||||||
|
#include <signal_path/vfo_manager.h>
|
||||||
#include <utils/optionlist.h>
|
#include <utils/optionlist.h>
|
||||||
#include <gui/widgets/symbol_diagram.h>
|
#include <gui/widgets/symbol_diagram.h>
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
|
#include <dsp/sink/handler_sink.h>
|
||||||
|
#include "dsp.h"
|
||||||
|
#include "pocsag.h"
|
||||||
|
|
||||||
|
const char* msgTypes[] = {
|
||||||
|
"Numeric",
|
||||||
|
"Unknown (0b01)",
|
||||||
|
"Unknown (0b10)",
|
||||||
|
"Alphanumeric",
|
||||||
|
};
|
||||||
|
|
||||||
class POCSAGDecoder : public Decoder {
|
class POCSAGDecoder : public Decoder {
|
||||||
public:
|
public:
|
||||||
POCSAGDecoder() : diag(0.6, 2400) {
|
POCSAGDecoder(const std::string& name, VFOManager::VFO* vfo) : diag(0.6, 2400) {
|
||||||
|
this->name = name;
|
||||||
|
this->vfo = vfo;
|
||||||
|
|
||||||
// Define baudrate options
|
// Define baudrate options
|
||||||
baudrates.define(512, "512 Baud", 512);
|
baudrates.define(512, "512 Baud", 512);
|
||||||
baudrates.define(1200, "1200 Baud", 1200);
|
baudrates.define(1200, "1200 Baud", 1200);
|
||||||
baudrates.define(2400, "2400 Baud", 2400);
|
baudrates.define(2400, "2400 Baud", 2400);
|
||||||
|
|
||||||
|
// Init DSP
|
||||||
|
vfo->setBandwidthLimits(12500, 12500, true);
|
||||||
|
vfo->setSampleRate(24000, 12500);
|
||||||
|
dsp.init(vfo->output, 24000, 2400);
|
||||||
|
reshape.init(&dsp.soft, 2400.0, (2400 / 30.0) - 2400.0);
|
||||||
|
dataHandler.init(&dsp.out, _dataHandler, this);
|
||||||
|
diagHandler.init(&reshape.out, _diagHandler, this);
|
||||||
|
|
||||||
|
// Init decoder
|
||||||
|
decoder.onMessage.bind(&POCSAGDecoder::messageHandler, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~POCSAGDecoder() {
|
||||||
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void showMenu() {
|
void showMenu() {
|
||||||
ImGui::LeftLabel("Baudrate");
|
ImGui::LeftLabel("Baudrate");
|
||||||
ImGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
if (ImGui::Combo(("##pager_decoder_proto_" + name).c_str(), &brId, baudrates.txt)) {
|
if (ImGui::Combo(("##pager_decoder_pocsag_br_" + name).c_str(), &brId, baudrates.txt)) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::FillWidth();
|
||||||
|
diag.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVFO(VFOManager::VFO* vfo) {
|
||||||
|
this->vfo = vfo;
|
||||||
|
vfo->setBandwidthLimits(12500, 12500, true);
|
||||||
|
vfo->setSampleRate(24000, 12500);
|
||||||
|
dsp.setInput(vfo->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
flog::debug("POCSAG start");
|
||||||
|
dsp.start();
|
||||||
|
reshape.start();
|
||||||
|
dataHandler.start();
|
||||||
|
diagHandler.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
flog::debug("POCSAG stop");
|
||||||
|
dsp.stop();
|
||||||
|
reshape.stop();
|
||||||
|
dataHandler.stop();
|
||||||
|
diagHandler.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void _dataHandler(uint8_t* data, int count, void* ctx) {
|
||||||
|
POCSAGDecoder* _this = (POCSAGDecoder*)ctx;
|
||||||
|
_this->decoder.process(data, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _diagHandler(float* data, int count, void* ctx) {
|
||||||
|
POCSAGDecoder* _this = (POCSAGDecoder*)ctx;
|
||||||
|
float* buf = _this->diag.acquireBuffer();
|
||||||
|
memcpy(buf, data, count * sizeof(float));
|
||||||
|
_this->diag.releaseBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void messageHandler(pocsag::Address addr, pocsag::MessageType type, const std::string& msg) {
|
||||||
|
flog::debug("[{}]: '{}'", (uint32_t)addr, msg);
|
||||||
|
}
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
|
VFOManager::VFO* vfo;
|
||||||
|
|
||||||
|
POCSAGDSP dsp;
|
||||||
|
dsp::buffer::Reshaper<float> reshape;
|
||||||
|
dsp::sink::Handler<uint8_t> dataHandler;
|
||||||
|
dsp::sink::Handler<float> diagHandler;
|
||||||
|
|
||||||
|
pocsag::Decoder decoder;
|
||||||
|
|
||||||
ImGui::SymbolDiagram diag;
|
ImGui::SymbolDiagram diag;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <dsp/digital/binary_slicer.h>
|
#include <dsp/digital/binary_slicer.h>
|
||||||
#include <dsp/routing/doubler.h>
|
#include <dsp/routing/doubler.h>
|
||||||
|
|
||||||
class POCSAGDSP : dsp::Processor<dsp::complex_t, uint8_t> {
|
class POCSAGDSP : public dsp::Processor<dsp::complex_t, uint8_t> {
|
||||||
using base_type = dsp::Processor<dsp::complex_t, uint8_t>;
|
using base_type = dsp::Processor<dsp::complex_t, uint8_t>;
|
||||||
public:
|
public:
|
||||||
POCSAGDSP() {}
|
POCSAGDSP() {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user