Skip to content

Commit 3fc8935

Browse files
beginning of pager decoder
1 parent 4b68351 commit 3fc8935

File tree

9 files changed

+571
-2
lines changed

9 files changed

+571
-2
lines changed

CMakeLists.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies:
4343
option(OPT_BUILD_KG_SSTV_DECODER "Build the KG SSTV (KG-STV) decoder module (no dependencies required)" OFF)
4444
option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (Dependencies: codec2)" OFF)
4545
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
46+
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" OFF)
4647
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
4748
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
4849

@@ -234,6 +235,10 @@ if (OPT_BUILD_METEOR_DEMODULATOR)
234235
add_subdirectory("decoder_modules/meteor_demodulator")
235236
endif (OPT_BUILD_METEOR_DEMODULATOR)
236237

238+
if (OPT_BUILD_PAGER_DECODER)
239+
add_subdirectory("decoder_modules/pager_decoder")
240+
endif (OPT_BUILD_PAGER_DECODER)
241+
237242
if (OPT_BUILD_RADIO)
238243
add_subdirectory("decoder_modules/radio")
239244
endif (OPT_BUILD_RADIO)
@@ -242,6 +247,7 @@ if (OPT_BUILD_WEATHER_SAT_DECODER)
242247
add_subdirectory("decoder_modules/weather_sat_decoder")
243248
endif (OPT_BUILD_WEATHER_SAT_DECODER)
244249

250+
add_subdirectory("decoder_modules/pager_decoder")
245251

246252
# Misc
247253
if (OPT_BUILD_DISCORD_PRESENCE)
@@ -302,7 +308,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
302308
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.dylib\" \"$<TARGET_FILE_DIR:sdrpp>\")
303309
endif ()
304310

305-
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_SCANNER=ON -DOPT_BUILD_SCHEDULER=ON -DOPT_BUILD_USRP_SOURCE=ON
311+
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_SCANNER=ON -DOPT_BUILD_SCHEDULER=ON -DOPT_BUILD_USRP_SOURCE=ON -DOPT_BUILD_PAGER_DECODER=ON
306312

307313
# Create module cmake file
308314
configure_file(${CMAKE_SOURCE_DIR}/sdrpp_module.cmake ${CMAKE_CURRENT_BINARY_DIR}/sdrpp_module.cmake @ONLY)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
cmake_minimum_required(VERSION 3.13)
2+
project(pager_decoder)
3+
4+
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
5+
6+
include(${SDRPP_MODULE_CMAKE})
7+
8+
target_include_directories(pager_decoder PRIVATE "src/")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#pragma once
2+
3+
class Decoder {
4+
public:
5+
6+
virtual void showMenu();
7+
8+
};
+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
#include <imgui.h>
2+
#include <config.h>
3+
#include <core.h>
4+
#include <gui/style.h>
5+
#include <gui/gui.h>
6+
#include <signal_path/signal_path.h>
7+
#include <module.h>
8+
#include <filesystem>
9+
#include <dsp/stream.h>
10+
#include <dsp/buffer/reshaper.h>
11+
#include <dsp/multirate/rational_resampler.h>
12+
#include <dsp/sink/handler_sink.h>
13+
#include <gui/widgets/folder_select.h>
14+
#include <gui/widgets/symbol_diagram.h>
15+
#include <fstream>
16+
#include <chrono>
17+
#include <dsp/demod/quadrature.h>
18+
#include <dsp/clock_recovery/mm.h>
19+
#include <dsp/taps/root_raised_cosine.h>
20+
#include <dsp/correction/dc_blocker.h>
21+
#include <dsp/loop/fast_agc.h>
22+
#include <utils/optionlist.h>
23+
#include <dsp/digital/binary_slicer.h>
24+
#include <dsp/routing/doubler.h>
25+
#include "pocsag/pocsag.h"
26+
27+
#define CONCAT(a, b) ((std::string(a) + b).c_str())
28+
29+
SDRPP_MOD_INFO{
30+
/* Name: */ "pager_decoder",
31+
/* Description: */ "POCSAG and Flex Pager Decoder"
32+
/* Author: */ "Ryzerth",
33+
/* Version: */ 0, 1, 0,
34+
/* Max instances */ -1
35+
};
36+
37+
const char* msgTypes[] = {
38+
"Numeric",
39+
"Unknown (0b01)",
40+
"Unknown (0b10)",
41+
"Alphanumeric",
42+
};
43+
44+
ConfigManager config;
45+
46+
#define INPUT_SAMPLE_RATE 24000.0
47+
#define INPUT_BANDWIDTH 12500.0
48+
#define INPUT_BAUD_RATE 2400.0
49+
50+
enum Protocol {
51+
PROTOCOL_POCSAG,
52+
PROTOCOL_FLEX
53+
};
54+
55+
class PagerDecoderModule : public ModuleManager::Instance {
56+
public:
57+
PagerDecoderModule(std::string name) : diag(0.6, 2400) {
58+
this->name = name;
59+
60+
// Define protocols
61+
protocols.define("POCSAG", PROTOCOL_POCSAG);
62+
protocols.define("FLEX", PROTOCOL_FLEX);
63+
64+
// Load config
65+
config.acquire();
66+
if (!config.conf.contains(name)) {
67+
config.conf[name]["showLines"] = false;
68+
}
69+
showLines = config.conf[name]["showLines"];
70+
if (showLines) {
71+
diag.lines.push_back(-1.0);
72+
diag.lines.push_back(-1.0/3.0);
73+
diag.lines.push_back(1.0/3.0);
74+
diag.lines.push_back(1.0);
75+
}
76+
config.release(true);
77+
78+
// Initialize VFO
79+
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
80+
vfo->setSnapInterval(1);
81+
82+
// Initialize DSP here (negative dev to invert signal)
83+
demod.init(vfo->output, -4500.0, INPUT_SAMPLE_RATE);
84+
dcBlock.init(&demod.out, 0.001);
85+
float taps[] = { 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f };
86+
shape = dsp::taps::fromArray<float>(10, taps);
87+
fir.init(&dcBlock.out, shape);
88+
recov.init(&fir.out, INPUT_SAMPLE_RATE/INPUT_BAUD_RATE, 1e5, 0.1, 0.05);
89+
doubler.init(&recov.out);
90+
slicer.init(&doubler.outB);
91+
dataHandler.init(&slicer.out, _dataHandler, this);
92+
reshape.init(&doubler.outA, 2400.0, (INPUT_BAUD_RATE / 30.0) - 2400.0);
93+
diagHandler.init(&reshape.out, _diagHandler, this);
94+
95+
// Initialize decode
96+
decoder.onMessage.bind(&PagerDecoderModule::messageHandler, this);
97+
98+
// Start DSP Here
99+
demod.start();
100+
dcBlock.start();
101+
fir.start();
102+
recov.start();
103+
doubler.start();
104+
slicer.start();
105+
dataHandler.start();
106+
reshape.start();
107+
diagHandler.start();
108+
109+
gui::menu.registerEntry(name, menuHandler, this, this);
110+
}
111+
112+
~PagerDecoderModule() {
113+
gui::menu.removeEntry(name);
114+
// Stop DSP
115+
if (enabled) {
116+
demod.stop();
117+
dcBlock.stop();
118+
fir.stop();
119+
recov.stop();
120+
doubler.stop();
121+
slicer.stop();
122+
dataHandler.stop();
123+
reshape.stop();
124+
diagHandler.stop();
125+
sigpath::vfoManager.deleteVFO(vfo);
126+
}
127+
128+
sigpath::sinkManager.unregisterStream(name);
129+
}
130+
131+
void postInit() {}
132+
133+
void enable() {
134+
double bw = gui::waterfall.getBandwidth();
135+
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);
136+
vfo->setSnapInterval(250);
137+
138+
// Start DSP
139+
demod.start();
140+
dcBlock.start();
141+
fir.start();
142+
recov.start();
143+
doubler.start();
144+
slicer.start();
145+
dataHandler.start();
146+
reshape.start();
147+
diagHandler.start();
148+
149+
enabled = true;
150+
}
151+
152+
void disable() {
153+
demod.stop();
154+
dcBlock.stop();
155+
fir.stop();
156+
recov.stop();
157+
doubler.stop();
158+
slicer.stop();
159+
dataHandler.stop();
160+
reshape.stop();
161+
diagHandler.stop();
162+
reshape.stop();
163+
diagHandler.stop();
164+
165+
sigpath::vfoManager.deleteVFO(vfo);
166+
enabled = false;
167+
}
168+
169+
bool isEnabled() {
170+
return enabled;
171+
}
172+
173+
private:
174+
static void menuHandler(void* ctx) {
175+
PagerDecoderModule* _this = (PagerDecoderModule*)ctx;
176+
177+
float menuWidth = ImGui::GetContentRegionAvail().x;
178+
179+
if (!_this->enabled) { style::beginDisabled(); }
180+
181+
ImGui::LeftLabel("Protocol");
182+
ImGui::FillWidth();
183+
if (ImGui::Combo(("##pager_decoder_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
184+
// TODO
185+
}
186+
187+
ImGui::SetNextItemWidth(menuWidth);
188+
_this->diag.draw();
189+
190+
if (!_this->enabled) { style::endDisabled(); }
191+
}
192+
193+
static void _dataHandler(uint8_t* data, int count, void* ctx) {
194+
PagerDecoderModule* _this = (PagerDecoderModule*)ctx;
195+
_this->decoder.process(data, count);
196+
}
197+
198+
static void _diagHandler(float* data, int count, void* ctx) {
199+
PagerDecoderModule* _this = (PagerDecoderModule*)ctx;
200+
float* buf = _this->diag.acquireBuffer();
201+
memcpy(buf, data, count * sizeof(float));
202+
_this->diag.releaseBuffer();
203+
}
204+
205+
void messageHandler(pocsag::Address addr, pocsag::MessageType type, const std::string& msg) {
206+
flog::debug("[{}]: '{}'", (uint32_t)addr, msg);
207+
}
208+
209+
std::string name;
210+
bool enabled = true;
211+
212+
int protoId = 0;
213+
214+
OptionList<std::string, Protocol> protocols;
215+
216+
pocsag::Decoder decoder;
217+
218+
// DSP Chain
219+
VFOManager::VFO* vfo;
220+
dsp::demod::Quadrature demod;
221+
dsp::correction::DCBlocker<float> dcBlock;
222+
dsp::tap<float> shape;
223+
dsp::filter::FIR<float, float> fir;
224+
dsp::clock_recovery::MM<float> recov;
225+
dsp::routing::Doubler<float> doubler;
226+
dsp::digital::BinarySlicer slicer;
227+
dsp::buffer::Reshaper<float> reshape;
228+
dsp::sink::Handler<uint8_t> dataHandler;
229+
dsp::sink::Handler<float> diagHandler;
230+
231+
ImGui::SymbolDiagram diag;
232+
233+
bool showLines = false;
234+
};
235+
236+
MOD_EXPORT void _INIT_() {
237+
// Create default recording directory
238+
json def = json({});
239+
config.setPath(core::args["root"].s() + "/pager_decoder_config.json");
240+
config.load(def);
241+
config.enableAutoSave();
242+
}
243+
244+
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
245+
return new PagerDecoderModule(name);
246+
}
247+
248+
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
249+
delete (PagerDecoderModule*)instance;
250+
}
251+
252+
MOD_EXPORT void _END_() {
253+
config.disableAutoSave();
254+
config.save();
255+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
#include "../decoder.h"
3+
#include <utils/optionlist.h>
4+
#include <gui/widgets/symbol_diagram.h>
5+
#include <gui/style.h>
6+
7+
class POCSAGDecoder : public Decoder {
8+
public:
9+
POCSAGDecoder() : diag(0.6, 2400) {
10+
// Define baudrate options
11+
baudrates.define(512, "512 Baud", 512);
12+
baudrates.define(1200, "1200 Baud", 1200);
13+
baudrates.define(2400, "2400 Baud", 2400);
14+
}
15+
16+
void showMenu() {
17+
ImGui::LeftLabel("Baudrate");
18+
ImGui::FillWidth();
19+
if (ImGui::Combo(("##pager_decoder_proto_" + name).c_str(), &brId, baudrates.txt)) {
20+
// TODO
21+
}
22+
}
23+
24+
private:
25+
std::string name;
26+
27+
ImGui::SymbolDiagram diag;
28+
29+
int brId = 2;
30+
31+
OptionList<int, int> baudrates;
32+
};

0 commit comments

Comments
 (0)