Skip to content

Commit

Permalink
add VOR receiver module
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandreRouma committed Jan 28, 2025
1 parent ea3675d commit 4799d0e
Show file tree
Hide file tree
Showing 8 changed files with 2,366 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dep
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON)
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF)
option(OPT_BUILD_VOR_RECEIVER "VOR beacon receiver" ON)
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)

# Misc
Expand Down Expand Up @@ -289,6 +290,10 @@ if (OPT_BUILD_RYFI_DECODER)
add_subdirectory("decoder_modules/ryfi_decoder")
endif (OPT_BUILD_RYFI_DECODER)

if (OPT_BUILD_VOR_RECEIVER)
add_subdirectory("decoder_modules/vor_receiver")
endif (OPT_BUILD_VOR_RECEIVER)

if (OPT_BUILD_WEATHER_SAT_DECODER)
add_subdirectory("decoder_modules/weather_sat_decoder")
endif (OPT_BUILD_WEATHER_SAT_DECODER)
Expand Down
8 changes: 8 additions & 0 deletions decoder_modules/vor_receiver/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.13)
project(vor_receiver)

file(GLOB_RECURSE SRC "src/*.cpp")

include(${SDRPP_MODULE_CMAKE})

target_include_directories(vor_receiver PRIVATE "src/")
128 changes: 128 additions & 0 deletions decoder_modules/vor_receiver/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include <imgui.h>
#include <config.h>
#include <core.h>
#include <gui/style.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <filesystem>
#include <dsp/buffer/reshaper.h>
#include <dsp/sink/handler_sink.h>
#include <gui/widgets/constellation_diagram.h>
#include "vor_decoder.h"
#include <fstream>

#define CONCAT(a, b) ((std::string(a) + b).c_str())

SDRPP_MOD_INFO{
/* Name: */ "vor_receiver",
/* Description: */ "VOR Receiver for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};

ConfigManager config;

#define INPUT_SAMPLE_RATE VOR_IN_SR

class VORReceiverModule : public ModuleManager::Instance {
public:
VORReceiverModule(std::string name) {
this->name = name;

// Load config
config.acquire();
// TODO: Load config
config.release();

vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, true);
decoder = new vor::Decoder(vfo->output, 1);
decoder->onBearing.bind(&VORReceiverModule::onBearing, this);

decoder->start();

gui::menu.registerEntry(name, menuHandler, this, this);
}

~VORReceiverModule() {
decoder->stop();
sigpath::vfoManager.deleteVFO(vfo);
gui::menu.removeEntry(name);
delete decoder;
}

void postInit() {}

void enable() {
double bw = gui::waterfall.getBandwidth();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, true);

decoder->setInput(vfo->output);

decoder->start();

enabled = true;
}

void disable() {
decoder->stop();

sigpath::vfoManager.deleteVFO(vfo);
enabled = false;
}

bool isEnabled() {
return enabled;
}

private:
static void menuHandler(void* ctx) {
VORReceiverModule* _this = (VORReceiverModule*)ctx;

float menuWidth = ImGui::GetContentRegionAvail().x;

if (!_this->enabled) { style::beginDisabled(); }

ImGui::Text("Bearing: %f°", _this->bearing);
ImGui::Text("Quality: %0.1f%%", _this->quality);

if (!_this->enabled) { style::endDisabled(); }
}

void onBearing(float nbearing, float nquality) {
bearing = (180.0f * nbearing / FL_M_PI);
quality = nquality * 100.0f;
}

std::string name;
bool enabled = true;

// DSP Chain
VFOManager::VFO* vfo;
vor::Decoder* decoder;

float bearing = 0.0f, quality = 0.0f;
};

MOD_EXPORT void _INIT_() {
// Create default recording directory
std::string root = (std::string)core::args["root"];
json def = json({});
config.setPath(root + "/vor_receiver_config.json");
config.load(def);
config.enableAutoSave();
}

MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new VORReceiverModule(name);
}

MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (VORReceiverModule*)instance;
}

MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}
50 changes: 50 additions & 0 deletions decoder_modules/vor_receiver/src/vor_decoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "vor_decoder.h"

#define STDDEV_NORM_FACTOR 1.813799364234218f // 2.0f * FL_M_PI / sqrt(12)

namespace vor {
Decoder::Decoder(dsp::stream<dsp::complex_t>* in, double integrationTime) {
rx.init(in);
reshape.init(&rx.out, round(1000.0 * integrationTime), 0);
symSink.init(&reshape.out, dataHandler, this);
}

Decoder::~Decoder() {
// TODO
}

void Decoder::setInput(dsp::stream<dsp::complex_t>* in) {
rx.setInput(in);
}

void Decoder::start() {
rx.start();
reshape.start();
symSink.start();
}

void Decoder::stop() {
rx.stop();
reshape.stop();
symSink.stop();
}

void Decoder::dataHandler(float* data, int count, void* ctx) {
// Get the instance from context
Decoder* _this = (Decoder*)ctx;

// Compute the mean and standard deviation of the
float mean, stddev;
volk_32f_stddev_and_mean_32f_x2(&stddev, &mean, data, count);

// Compute the signal quality
float quality = std::max<float>(1.0f - (stddev / STDDEV_NORM_FACTOR), 0.0f);

// Convert the phase difference to a compass heading
mean = -mean;
if (mean < 0) { mean = 2.0f*FL_M_PI + mean; }

// Call the handler
_this->onBearing(mean, quality);
}
}
49 changes: 49 additions & 0 deletions decoder_modules/vor_receiver/src/vor_decoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include "vor_receiver.h"
#include <dsp/buffer/reshaper.h>
#include <dsp/sink/handler_sink.h>
#include <utils/new_event.h>

namespace vor {
// Note: hard coded to 22KHz samplerate
class Decoder {
public:
/**
* Create an instance of a VOR decoder.
* @param in Input IQ stream at 22 KHz sampling rate.
* @param integrationTime Integration time of the bearing data in seconds.
*/
Decoder(dsp::stream<dsp::complex_t>* in, double integrationTime);

// Destructor
~Decoder();

/**
* Set the input stream.
* @param in Input IQ stream at 22 KHz sampling rate.
*/
void setInput(dsp::stream<dsp::complex_t>* in);

/**
* Start the decoder.
*/
void start();

/**
* Stop the decoder.
*/
void stop();

/**
* handler(bearing, signalQuality);
*/
NewEvent<float, float> onBearing;

private:
static void dataHandler(float* data, int count, void* ctx);

// DSP
Receiver rx;
dsp::buffer::Reshaper<float> reshape;
dsp::sink::Handler<float> symSink;
};
}
Loading

0 comments on commit 4799d0e

Please sign in to comment.