mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-26 19:08:30 +01:00
add ryfi decoder module
This commit is contained in:
parent
949fde022d
commit
554ba2f596
@ -48,6 +48,7 @@ option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (Dependencies: codec2
|
|||||||
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
|
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
|
||||||
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON)
|
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_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
|
||||||
|
option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF)
|
||||||
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
|
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
@ -260,6 +261,10 @@ if (OPT_BUILD_RADIO)
|
|||||||
add_subdirectory("decoder_modules/radio")
|
add_subdirectory("decoder_modules/radio")
|
||||||
endif (OPT_BUILD_RADIO)
|
endif (OPT_BUILD_RADIO)
|
||||||
|
|
||||||
|
if (OPT_BUILD_RYFI_DECODER)
|
||||||
|
add_subdirectory("decoder_modules/ryfi_decoder")
|
||||||
|
endif (OPT_BUILD_RYFI_DECODER)
|
||||||
|
|
||||||
if (OPT_BUILD_WEATHER_SAT_DECODER)
|
if (OPT_BUILD_WEATHER_SAT_DECODER)
|
||||||
add_subdirectory("decoder_modules/weather_sat_decoder")
|
add_subdirectory("decoder_modules/weather_sat_decoder")
|
||||||
endif (OPT_BUILD_WEATHER_SAT_DECODER)
|
endif (OPT_BUILD_WEATHER_SAT_DECODER)
|
||||||
|
8
decoder_modules/ryfi_decoder/CMakeLists.txt
Normal file
8
decoder_modules/ryfi_decoder/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(ryfi_decoder)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
target_include_directories(ryfi_decoder PRIVATE "src/")
|
139
decoder_modules/ryfi_decoder/src/main.cpp
Normal file
139
decoder_modules/ryfi_decoder/src/main.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#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/routing/splitter.h>
|
||||||
|
#include <dsp/buffer/reshaper.h>
|
||||||
|
#include <dsp/sink/handler_sink.h>
|
||||||
|
#include <gui/widgets/folder_select.h>
|
||||||
|
#include <gui/widgets/constellation_diagram.h>
|
||||||
|
#include "ryfi/receiver.h"
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "ryfi_decoder",
|
||||||
|
/* Description: */ "RyFi decoder for SDR++",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
#define INPUT_BANDWIDTH 800000
|
||||||
|
#define INPUT_SAMPLE_RATE 1500000
|
||||||
|
#define INPUT_BAUDRATE 720000
|
||||||
|
|
||||||
|
#define SYMBOL_DIAG_RATE 30
|
||||||
|
#define SYMBOL_DIAG_COUNT 1024
|
||||||
|
|
||||||
|
class RyFiDecoderModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
RyFiDecoderModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
|
||||||
|
rx.init(vfo->output, INPUT_BAUDRATE, INPUT_SAMPLE_RATE);
|
||||||
|
reshape.init(rx.softOut, SYMBOL_DIAG_COUNT, (INPUT_BAUDRATE / SYMBOL_DIAG_RATE) - SYMBOL_DIAG_COUNT);
|
||||||
|
symSink.init(&reshape.out, symSinkHandler, this);
|
||||||
|
rx.onPacket.bind(&RyFiDecoderModule::packetHandler, this);
|
||||||
|
|
||||||
|
rx.start();
|
||||||
|
reshape.start();
|
||||||
|
symSink.start();
|
||||||
|
|
||||||
|
gui::menu.registerEntry(name, menuHandler, this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RyFiDecoderModule() {
|
||||||
|
rx.stop();
|
||||||
|
reshape.stop();
|
||||||
|
symSink.stop();
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
gui::menu.removeEntry(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
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);
|
||||||
|
|
||||||
|
rx.setInput(vfo->output);
|
||||||
|
|
||||||
|
rx.start();
|
||||||
|
reshape.start();
|
||||||
|
symSink.start();
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
rx.stop();
|
||||||
|
reshape.stop();
|
||||||
|
symSink.stop();
|
||||||
|
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void packetHandler(ryfi::Packet pkt) {
|
||||||
|
flog::debug("Got a {} byte packet!", pkt.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx;
|
||||||
|
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::beginDisabled(); }
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(menuWidth);
|
||||||
|
_this->constDiagram.draw();
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::endDisabled(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void symSinkHandler(dsp::complex_t* data, int count, void* ctx) {
|
||||||
|
RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx;
|
||||||
|
|
||||||
|
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
|
||||||
|
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
|
||||||
|
_this->constDiagram.releaseBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
|
||||||
|
// DSP Chain
|
||||||
|
VFOManager::VFO* vfo;
|
||||||
|
ryfi::Receiver rx;
|
||||||
|
dsp::buffer::Reshaper<dsp::complex_t> reshape;
|
||||||
|
dsp::sink::Handler<dsp::complex_t> symSink;
|
||||||
|
|
||||||
|
ImGui::ConstellationDiagram constDiagram;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new RyFiDecoderModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (RyFiDecoderModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
|
||||||
|
}
|
74
decoder_modules/ryfi_decoder/src/ryfi/conv_codec.cpp
Normal file
74
decoder_modules/ryfi_decoder/src/ryfi/conv_codec.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "conv_codec.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
ConvEncoder::ConvEncoder(dsp::stream<uint8_t>* in) {
|
||||||
|
// Create the convolutional encoder instance
|
||||||
|
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
|
||||||
|
|
||||||
|
// Init the base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvEncoder::~ConvEncoder() {
|
||||||
|
// Destroy the convolutional encoder instance
|
||||||
|
correct_convolutional_destroy(conv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConvEncoder::encode(const uint8_t* in, uint8_t* out, int count) {
|
||||||
|
// Run convolutional encoder on the data
|
||||||
|
return correct_convolutional_encode(conv, in, count, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConvEncoder::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvDecoder::ConvDecoder(dsp::stream<dsp::complex_t>* in) {
|
||||||
|
// Create the convolutional encoder instance
|
||||||
|
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
|
||||||
|
|
||||||
|
// Allocate the soft symbol buffer
|
||||||
|
soft = dsp::buffer::alloc<uint8_t>(STREAM_BUFFER_SIZE);
|
||||||
|
|
||||||
|
// Init the base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvDecoder::~ConvDecoder() {
|
||||||
|
// Destroy the convolutional encoder instance
|
||||||
|
correct_convolutional_destroy(conv);
|
||||||
|
|
||||||
|
// Free the soft symbol buffer
|
||||||
|
dsp::buffer::free(soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConvDecoder::decode(const dsp::complex_t* in, uint8_t* out, int count) {
|
||||||
|
// Convert to uint8
|
||||||
|
const float* _in = (const float*)in;
|
||||||
|
count *= 2;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
soft[i] = std::clamp<int>((_in[i] * 127.0f) + 128.0f, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run convolutional decoder on the data
|
||||||
|
return correct_convolutional_decode_soft(conv, soft, count, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConvDecoder::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
70
decoder_modules/ryfi_decoder/src/ryfi/conv_codec.h
Normal file
70
decoder_modules/ryfi_decoder/src/ryfi/conv_codec.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "dsp/processor.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "correct.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
/**
|
||||||
|
* RyFi Convolutional Encoder.
|
||||||
|
*/
|
||||||
|
class ConvEncoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||||
|
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a convolutional encoder specifying an input stream.
|
||||||
|
* @param in Input stream.
|
||||||
|
*/
|
||||||
|
ConvEncoder(dsp::stream<uint8_t>* in = NULL);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~ConvEncoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode data.
|
||||||
|
* @param in Input bytes.
|
||||||
|
* @param out Output bits.
|
||||||
|
* @param count Number of input bytes.
|
||||||
|
* @return Number of output bits.
|
||||||
|
*/
|
||||||
|
int encode(const uint8_t* in, uint8_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
correct_convolutional* conv;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RyFi Convolutional Decoder.
|
||||||
|
*/
|
||||||
|
class ConvDecoder : public dsp::Processor<dsp::complex_t, uint8_t> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, uint8_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a convolutional encoder specifying an input stream.
|
||||||
|
* @param in Input stream.
|
||||||
|
*/
|
||||||
|
ConvDecoder(dsp::stream<dsp::complex_t>* in = NULL);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~ConvDecoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode soft symbols.
|
||||||
|
* @param in Input soft symbols.
|
||||||
|
* @param out Output bytes.
|
||||||
|
* @param count Number of input bytes.
|
||||||
|
* @return Number of output bits.
|
||||||
|
*/
|
||||||
|
int decode(const dsp::complex_t* in, uint8_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
correct_convolutional* conv;
|
||||||
|
uint8_t* soft = NULL;
|
||||||
|
};
|
||||||
|
}
|
37
decoder_modules/ryfi_decoder/src/ryfi/frame.cpp
Normal file
37
decoder_modules/ryfi_decoder/src/ryfi/frame.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include "frame.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
int Frame::serialize(uint8_t* bytes) const {
|
||||||
|
// Write the counter
|
||||||
|
bytes[0] = (counter >> 8) & 0xFF;
|
||||||
|
bytes[1] = counter & 0xFF;
|
||||||
|
|
||||||
|
// Write the first packet pointer
|
||||||
|
bytes[2] = (firstPacket >> 8) & 0xFF;
|
||||||
|
bytes[3] = firstPacket & 0xFF;
|
||||||
|
|
||||||
|
// Write the last packet pointer
|
||||||
|
bytes[4] = (lastPacket >> 8) & 0xFF;
|
||||||
|
bytes[5] = lastPacket & 0xFF;
|
||||||
|
|
||||||
|
// Write the data
|
||||||
|
memcpy(&bytes[6], content, FRAME_DATA_SIZE);
|
||||||
|
|
||||||
|
// Return the length of a serialized frame
|
||||||
|
return FRAME_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frame::deserialize(const uint8_t* bytes, Frame& frame) {
|
||||||
|
// Read the counter
|
||||||
|
frame.counter = (((uint16_t)bytes[0]) << 8) | ((uint16_t)bytes[1]);
|
||||||
|
|
||||||
|
// Read the first packet pointer
|
||||||
|
frame.firstPacket = (((uint16_t)bytes[2]) << 8) | ((uint16_t)bytes[3]);
|
||||||
|
|
||||||
|
// Read the last packet pointer
|
||||||
|
frame.lastPacket = (((uint16_t)bytes[4]) << 8) | ((uint16_t)bytes[5]);
|
||||||
|
|
||||||
|
// Read the data
|
||||||
|
memcpy(frame.content, &bytes[6], FRAME_DATA_SIZE);
|
||||||
|
}
|
||||||
|
}
|
42
decoder_modules/ryfi_decoder/src/ryfi/frame.h
Normal file
42
decoder_modules/ryfi_decoder/src/ryfi/frame.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "rs_codec.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
enum PacketOffset {
|
||||||
|
PKT_OFFS_NONE = 0xFFFF
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
/**
|
||||||
|
* Serialize the frame to bytes.
|
||||||
|
* @param bytes Buffer to write the serialized frame to.
|
||||||
|
*/
|
||||||
|
int serialize(uint8_t* bytes) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize a frame from bytes.
|
||||||
|
* @param bytes Buffer to deserialize the frame from.
|
||||||
|
* @param frame Object that will contain the deserialize frame.
|
||||||
|
*/
|
||||||
|
static void deserialize(const uint8_t* bytes, Frame& frame);
|
||||||
|
|
||||||
|
// Size of a serialized frame
|
||||||
|
static inline const int FRAME_SIZE = RS_BLOCK_DEC_SIZE*RS_BLOCK_COUNT;
|
||||||
|
|
||||||
|
// Size of the data area of the frame
|
||||||
|
static inline const int FRAME_DATA_SIZE = FRAME_SIZE - 6;
|
||||||
|
|
||||||
|
// Steadily increasing counter.
|
||||||
|
uint16_t counter = 0;
|
||||||
|
|
||||||
|
// Byte offset of the first packet in the frame.
|
||||||
|
uint16_t firstPacket = 0;
|
||||||
|
|
||||||
|
// Byte offset of the last packet in the frame.
|
||||||
|
uint16_t lastPacket = 0;
|
||||||
|
|
||||||
|
// Data area of the frame.
|
||||||
|
uint8_t content[FRAME_DATA_SIZE];
|
||||||
|
};
|
||||||
|
}
|
137
decoder_modules/ryfi_decoder/src/ryfi/framing.cpp
Normal file
137
decoder_modules/ryfi_decoder/src/ryfi/framing.cpp
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#include "framing.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
dsp::complex_t QPSK_SYMBOLS[4] = {
|
||||||
|
{ -0.070710678118f, -0.070710678118f },
|
||||||
|
{ -0.070710678118f, 0.070710678118f },
|
||||||
|
{ 0.070710678118f, -0.070710678118f },
|
||||||
|
{ 0.070710678118f, 0.070710678118f },
|
||||||
|
};
|
||||||
|
|
||||||
|
Framer::Framer(dsp::stream<uint8_t>* in) {
|
||||||
|
// Generate the sync symbols
|
||||||
|
int k = 0;
|
||||||
|
for (int i = 62; i >= 0; i -= 2) {
|
||||||
|
syncSyms[k++] = QPSK_SYMBOLS[(SYNC_WORD >> i) & 0b11];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Framer::encode(const uint8_t* in, dsp::complex_t* out, int count) {
|
||||||
|
// Copy sync symbols
|
||||||
|
memcpy(out, syncSyms, SYNC_SYMS*sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
// Modulate the rest of the bits
|
||||||
|
dsp::complex_t* dataOut = &out[SYNC_SYMS];
|
||||||
|
int dataSyms = count / 2;
|
||||||
|
for (int i = 0; i < dataSyms; i++) {
|
||||||
|
uint8_t bits = (in[i >> 2] >> (6 - 2*(i & 0b11))) & 0b11;
|
||||||
|
dataOut[i] = QPSK_SYMBOLS[bits];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute and return the total number of symbols
|
||||||
|
return SYNC_SYMS + dataSyms;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Framer::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Deframer::Deframer(dsp::stream<dsp::complex_t> *in) {
|
||||||
|
// Compute sync word rotations
|
||||||
|
// 0: 00 01 11 10
|
||||||
|
// 90: 10 00 01 11
|
||||||
|
// 180: 11 10 00 01
|
||||||
|
// 270: 01 11 10 00
|
||||||
|
|
||||||
|
// For 0 and 180 it's the sync and its complement
|
||||||
|
syncRots[ROT_0_DEG] = SYNC_WORD;
|
||||||
|
syncRots[ROT_180_DEG] = ~SYNC_WORD;
|
||||||
|
|
||||||
|
// For 90 and 270 its the quadrature and its complement
|
||||||
|
uint64_t quad;
|
||||||
|
for (int i = 62; i >= 0; i -= 2) {
|
||||||
|
// Get the symbol
|
||||||
|
uint8_t sym = (SYNC_WORD >> i) & 0b11;
|
||||||
|
|
||||||
|
// Rotate it 90 degrees
|
||||||
|
uint8_t rsym;
|
||||||
|
switch (sym) {
|
||||||
|
case 0b00: rsym = 0b10; break;
|
||||||
|
case 0b01: rsym = 0b00; break;
|
||||||
|
case 0b11: rsym = 0b01; break;
|
||||||
|
case 0b10: rsym = 0b11; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push it into the quadrature
|
||||||
|
quad = (quad << 2) | rsym;
|
||||||
|
}
|
||||||
|
syncRots[ROT_90_DEG] = quad;
|
||||||
|
syncRots[ROT_270_DEG] = ~quad;
|
||||||
|
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Deframer::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
dsp::complex_t* in = base_type::_in->readBuf;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (recv) {
|
||||||
|
// Copy the symbol to the output and rotate it approprieate
|
||||||
|
base_type::out.writeBuf[outCount++] = in[i] * symRot;
|
||||||
|
|
||||||
|
// Check if we're done receiving the frame, send it out
|
||||||
|
if (!(--recv)) {
|
||||||
|
if (!base_type::out.swap(outCount)) {
|
||||||
|
base_type::_in->flush();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Get the raw symbol
|
||||||
|
dsp::complex_t fsym = in[i];
|
||||||
|
|
||||||
|
// Decode the symbol
|
||||||
|
uint8_t sym = ((fsym.re > 0) ? 0b10 : 0b00) | ((fsym.im > 0) ? 0b01 : 0b00);
|
||||||
|
|
||||||
|
// Push it to the shift register
|
||||||
|
shift = (shift << 2) | sym;
|
||||||
|
|
||||||
|
// Find the rotation starting with the last known one
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
// Get the test rotation
|
||||||
|
int testRot = (knownRot+i) & 0b11;
|
||||||
|
|
||||||
|
// Check if the hamming distance is close enough
|
||||||
|
int dist;
|
||||||
|
if (distance(shift, syncRots[testRot]) < 6) {
|
||||||
|
// Save the new rotation
|
||||||
|
knownRot = testRot;
|
||||||
|
|
||||||
|
// Start reading in symbols for the frame
|
||||||
|
symRot = symRots[knownRot];
|
||||||
|
recv = 8168; // TODO: Don't hardcode!
|
||||||
|
outCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
86
decoder_modules/ryfi_decoder/src/ryfi/framing.h
Normal file
86
decoder_modules/ryfi_decoder/src/ryfi/framing.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "dsp/processor.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
// Synchronization word.
|
||||||
|
inline const uint64_t SYNC_WORD = 0x341CC540819D8963;
|
||||||
|
|
||||||
|
// Number of synchronization bits.
|
||||||
|
inline const int SYNC_BITS = 64;
|
||||||
|
|
||||||
|
// Number of synchronization symbols.
|
||||||
|
inline const int SYNC_SYMS = SYNC_BITS / 2;
|
||||||
|
|
||||||
|
// Possible constellation rotations
|
||||||
|
enum {
|
||||||
|
ROT_0_DEG = 0,
|
||||||
|
ROT_90_DEG = 1,
|
||||||
|
ROT_180_DEG = 2,
|
||||||
|
ROT_270_DEG = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RyFi Framer.
|
||||||
|
*/
|
||||||
|
class Framer : public dsp::Processor<uint8_t, dsp::complex_t> {
|
||||||
|
using base_type = dsp::Processor<uint8_t, dsp::complex_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a framer specifying an input stream.
|
||||||
|
* @param in Input stream.
|
||||||
|
*/
|
||||||
|
Framer(dsp::stream<uint8_t>* in = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a frame to symbols adding a sync word.
|
||||||
|
*/
|
||||||
|
int encode(const uint8_t* in, dsp::complex_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
dsp::complex_t syncSyms[SYNC_SYMS];
|
||||||
|
};
|
||||||
|
|
||||||
|
class Deframer : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a deframer specifying an input stream.
|
||||||
|
* @param in Input stream.
|
||||||
|
*/
|
||||||
|
Deframer(dsp::stream<dsp::complex_t> *in = NULL);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
inline static constexpr int distance(uint64_t a, uint64_t b) {
|
||||||
|
int dist = 0;
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
dist += ((a & 1) != (b & 1));
|
||||||
|
a >>= 1;
|
||||||
|
b >>= 1;
|
||||||
|
}
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame reading counters
|
||||||
|
int recv = 0;
|
||||||
|
int outCount = 0;
|
||||||
|
|
||||||
|
// Rotation handling
|
||||||
|
int knownRot = 0;
|
||||||
|
uint64_t syncRots[4];
|
||||||
|
dsp::complex_t symRot;
|
||||||
|
const dsp::complex_t symRots[4] = {
|
||||||
|
{ 1.0f, 0.0f }, // 0 deg
|
||||||
|
{ 0.0f, -1.0f }, // 90 deg
|
||||||
|
{ -1.0f, 0.0f }, // 180 deg
|
||||||
|
{ 0.0f, 1.0f }, // 270 deg
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shift register
|
||||||
|
uint64_t shift;
|
||||||
|
};
|
||||||
|
}
|
126
decoder_modules/ryfi_decoder/src/ryfi/packet.cpp
Normal file
126
decoder_modules/ryfi_decoder/src/ryfi/packet.cpp
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#include "packet.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
Packet::Packet() {}
|
||||||
|
|
||||||
|
Packet::Packet(uint8_t* content, int size) {
|
||||||
|
// Check that the size isn't too large
|
||||||
|
if (size > MAX_CONTENT_SIZE) {
|
||||||
|
throw std::runtime_error("Content size is too large to fit in a packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Allocate the buffer
|
||||||
|
allocate(size);
|
||||||
|
|
||||||
|
// Copy over the content
|
||||||
|
memcpy(_content, content, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::Packet(const Packet& b) {
|
||||||
|
// Reallocate the buffer
|
||||||
|
allocate(b._size);
|
||||||
|
|
||||||
|
// Copy over the content
|
||||||
|
memcpy(_content, b._content, b._size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::Packet(Packet&& b) {
|
||||||
|
// Move members
|
||||||
|
_content = b._content;
|
||||||
|
_size = b._size;
|
||||||
|
|
||||||
|
// Destroy old object
|
||||||
|
b._content = NULL;
|
||||||
|
b._size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::~Packet() {
|
||||||
|
// Delete the content
|
||||||
|
if (_content) { delete[] _content; }
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet& Packet::operator=(const Packet& b) {
|
||||||
|
// Reallocate the buffer
|
||||||
|
allocate(b._size);
|
||||||
|
|
||||||
|
// Copy over the content
|
||||||
|
memcpy(_content, b._content, b._size);
|
||||||
|
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet& Packet::operator=(Packet&& b) {
|
||||||
|
// Move members
|
||||||
|
_content = b._content;
|
||||||
|
_size = b._size;
|
||||||
|
|
||||||
|
// Destroy old object
|
||||||
|
b._content = NULL;
|
||||||
|
b._size = 0;
|
||||||
|
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::operator bool() const {
|
||||||
|
return _size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Packet::size() const {
|
||||||
|
// Return the size
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* Packet::data() const {
|
||||||
|
// Return the size
|
||||||
|
return _content;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Packet::setContent(uint8_t* content, int size) {
|
||||||
|
// Check that the size isn't too large
|
||||||
|
if (size > MAX_CONTENT_SIZE) {
|
||||||
|
throw std::runtime_error("Content size is too large to fit in a packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reallocate the buffer
|
||||||
|
allocate(size);
|
||||||
|
|
||||||
|
// Copy over the content
|
||||||
|
memcpy(_content, content, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Packet::serializedSize() const {
|
||||||
|
// Two size bytes + Size of the content
|
||||||
|
return _size + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Packet::serialize(uint8_t* bytes) const {
|
||||||
|
// Write the size in big-endian
|
||||||
|
bytes[0] = (_size >> 8) & 0xFF;
|
||||||
|
bytes[1] = _size & 0xFF;
|
||||||
|
|
||||||
|
// Copy the content of the packet
|
||||||
|
memcpy(&bytes[2], _content, _size);
|
||||||
|
|
||||||
|
// Return the serialized size
|
||||||
|
return serializedSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Packet::allocate(int newSize) {
|
||||||
|
// If the size hasn't changed, do nothing
|
||||||
|
if (newSize == _size) { return; }
|
||||||
|
|
||||||
|
// Free the old buffer
|
||||||
|
if (_content) { delete[] _content; };
|
||||||
|
|
||||||
|
// Update the size
|
||||||
|
_size = newSize;
|
||||||
|
|
||||||
|
// Allocate the buffer
|
||||||
|
_content = new uint8_t[newSize];
|
||||||
|
}
|
||||||
|
}
|
88
decoder_modules/ryfi_decoder/src/ryfi/packet.h
Normal file
88
decoder_modules/ryfi_decoder/src/ryfi/packet.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
/**
|
||||||
|
* RyFi Protocol Packet.
|
||||||
|
*/
|
||||||
|
class Packet {
|
||||||
|
public:
|
||||||
|
// Default constructor
|
||||||
|
Packet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a packet from its content.
|
||||||
|
* @param content Content of the packet.
|
||||||
|
* @param size Number of bytes of content.
|
||||||
|
*/
|
||||||
|
Packet(uint8_t* content, int size);
|
||||||
|
|
||||||
|
// Copy constructor
|
||||||
|
Packet(const Packet& b);
|
||||||
|
|
||||||
|
// Move constructor
|
||||||
|
Packet(Packet&& b);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~Packet();
|
||||||
|
|
||||||
|
// Copy assignment operator
|
||||||
|
Packet& operator=(const Packet& b);
|
||||||
|
|
||||||
|
// Move assignment operator
|
||||||
|
Packet& operator=(Packet&& b);
|
||||||
|
|
||||||
|
// Cast to bool operator
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the content of the packet.
|
||||||
|
* @return Size of the content of the packet.
|
||||||
|
*/
|
||||||
|
int size() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the content of the packet. The pointer is only valid until reallocation or deletion.
|
||||||
|
* @return Content of the packet.
|
||||||
|
*/
|
||||||
|
const uint8_t* data() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content of the packet.
|
||||||
|
* @param content Content of the packet.
|
||||||
|
* @param size Number of bytes of content.
|
||||||
|
*/
|
||||||
|
void setContent(uint8_t* content, int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the serialized packet.
|
||||||
|
* @return Size of the serialized packet.
|
||||||
|
*/
|
||||||
|
int serializedSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the packet to bytes.
|
||||||
|
* @param bytes Buffer to which to write the serialized packet.
|
||||||
|
* @return Size of the serialized packet.
|
||||||
|
*/
|
||||||
|
int serialize(uint8_t* bytes) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize a packet from bytes.
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
static bool deserialize(uint8_t* bytes, int size, Packet& pkt);
|
||||||
|
|
||||||
|
// Maximum size of the content of the packet.
|
||||||
|
static inline const int MAX_CONTENT_SIZE = 0xFFFF;
|
||||||
|
|
||||||
|
// Maximum size of the serialized packet.
|
||||||
|
static inline const int MAX_SERIALIZED_SIZE = MAX_CONTENT_SIZE + 2;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void allocate(int newSize);
|
||||||
|
|
||||||
|
uint8_t* _content = NULL;
|
||||||
|
int _size = 0;
|
||||||
|
};
|
||||||
|
}
|
191
decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp
Normal file
191
decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
#include "receiver.h"
|
||||||
|
|
||||||
|
#include "utils/flog.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
Receiver::Receiver() {}
|
||||||
|
|
||||||
|
Receiver::Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) {
|
||||||
|
init(in, baudrate, samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Receiver::~Receiver() {
|
||||||
|
// Stop everything
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) {
|
||||||
|
// Initialize the DSP
|
||||||
|
demod.init(in, baudrate, samplerate, 31, 0.6, 0.1f, 0.005f, 1e-6, 0.01);
|
||||||
|
doubler.init(&demod.out);
|
||||||
|
softOut = &doubler.outA;
|
||||||
|
deframer.setInput(&doubler.outB);
|
||||||
|
conv.setInput(&deframer.out);
|
||||||
|
rs.setInput(&conv.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::setInput(dsp::stream<dsp::complex_t>* in) {
|
||||||
|
demod.setInput(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::start() {
|
||||||
|
// Do nothing if already running
|
||||||
|
if (running) { return; }
|
||||||
|
|
||||||
|
// Start the worker thread
|
||||||
|
workerThread = std::thread(&Receiver::worker, this);
|
||||||
|
|
||||||
|
// Start the DSP
|
||||||
|
demod.start();
|
||||||
|
doubler.start();
|
||||||
|
deframer.start();
|
||||||
|
conv.start();
|
||||||
|
rs.start();
|
||||||
|
|
||||||
|
// Update the running state
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::stop() {
|
||||||
|
// Do nothing if not running
|
||||||
|
if (!running) { return; }
|
||||||
|
|
||||||
|
// Stop the worker thread
|
||||||
|
rs.out.stopReader();
|
||||||
|
if (workerThread.joinable()) { workerThread.join(); }
|
||||||
|
rs.out.clearReadStop();
|
||||||
|
|
||||||
|
// Stop the DSP
|
||||||
|
demod.stop();
|
||||||
|
doubler.stop();
|
||||||
|
deframer.stop();
|
||||||
|
conv.stop();
|
||||||
|
rs.stop();
|
||||||
|
|
||||||
|
// Update the running state
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::worker() {
|
||||||
|
Frame frame;
|
||||||
|
uint16_t lastCounter = 0;
|
||||||
|
uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE];
|
||||||
|
int pktExpected = 0;
|
||||||
|
int pktRead = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Read a frame
|
||||||
|
int count = rs.out.read();
|
||||||
|
if (count <= 0) { break; }
|
||||||
|
|
||||||
|
// Deserialize the frame
|
||||||
|
Frame::deserialize(rs.out.readBuf, frame);
|
||||||
|
|
||||||
|
// Flush the stream
|
||||||
|
rs.out.flush();
|
||||||
|
|
||||||
|
//flog::info("Frame[{}]: FirstPacket={}, LastPacket={}", frame.counter, frame.firstPacket, frame.lastPacket);
|
||||||
|
|
||||||
|
// Compute the expected frame counter
|
||||||
|
uint16_t expectedCounter = lastCounter + 1;
|
||||||
|
lastCounter = frame.counter;
|
||||||
|
|
||||||
|
// If the frames aren't consecutive
|
||||||
|
int frameRead = 0;
|
||||||
|
if (frame.counter != expectedCounter) {
|
||||||
|
//flog::warn("Lost at least {} frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000);
|
||||||
|
|
||||||
|
// Cancel the partial packet if there was one
|
||||||
|
pktExpected = 0;
|
||||||
|
pktRead = 0;
|
||||||
|
|
||||||
|
// If this frame is not an idle frame or continuation frame
|
||||||
|
if (frame.firstPacket != PKT_OFFS_NONE) {
|
||||||
|
// If the offset of the first packet is not plausible
|
||||||
|
if (frame.firstPacket > Frame::FRAME_DATA_SIZE-2) {
|
||||||
|
flog::warn("Packet had non-plausible offset: {}", frameRead);
|
||||||
|
|
||||||
|
// Skip the frame
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip to the end of the packet
|
||||||
|
frameRead = frame.firstPacket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no partial packet and the frame doesn't contain a packet start, skip it
|
||||||
|
if (!pktExpected && frame.firstPacket == PKT_OFFS_NONE) { continue; }
|
||||||
|
|
||||||
|
// Extract packets from the frame
|
||||||
|
bool firstPacket = true;
|
||||||
|
bool lastPacket = false;
|
||||||
|
while (frameRead < Frame::FRAME_DATA_SIZE) {
|
||||||
|
// If there is a partial packet read as much as possible from it
|
||||||
|
if (pktExpected) {
|
||||||
|
// Compute how many bytes of the packet are available in the frame
|
||||||
|
int readable = std::min<int>(pktExpected - pktRead, Frame::FRAME_DATA_SIZE - frameRead);
|
||||||
|
//flog::debug("Reading {} bytes", readable);
|
||||||
|
|
||||||
|
// Write them to the packet
|
||||||
|
memcpy(&pktBuffer[pktRead], &frame.content[frameRead], readable);
|
||||||
|
pktRead += readable;
|
||||||
|
frameRead += readable;
|
||||||
|
|
||||||
|
// If the packet is read entirely
|
||||||
|
if (pktRead >= pktExpected) {
|
||||||
|
// Create the packet object
|
||||||
|
Packet pkt(pktBuffer, pktExpected);
|
||||||
|
|
||||||
|
// Send off the packet
|
||||||
|
onPacket(pkt);
|
||||||
|
|
||||||
|
// Prepare for the next packet
|
||||||
|
pktRead = 0;
|
||||||
|
pktExpected = 0;
|
||||||
|
|
||||||
|
// If this was the last packet of the frame
|
||||||
|
if (lastPacket || frame.firstPacket == PKT_OFFS_NONE) {
|
||||||
|
// Skip the rest of the frame
|
||||||
|
frameRead = Frame::FRAME_DATA_SIZE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to next packet
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the packet offset is not plausible
|
||||||
|
if (Frame::FRAME_DATA_SIZE - frameRead < 2) {
|
||||||
|
flog::warn("Packet had non-plausible offset: {}", frameRead);
|
||||||
|
|
||||||
|
// Skip the rest of the frame and the packet
|
||||||
|
frameRead = Frame::FRAME_DATA_SIZE;
|
||||||
|
pktExpected = 0;
|
||||||
|
pktRead = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the first packet, use the frame info to skip possible left over data
|
||||||
|
if (firstPacket) {
|
||||||
|
frameRead = frame.firstPacket;
|
||||||
|
firstPacket = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is the last packet
|
||||||
|
lastPacket = (frameRead == frame.lastPacket);
|
||||||
|
|
||||||
|
// Parse the packet size
|
||||||
|
pktExpected = ((uint16_t)frame.content[frameRead]) << 8;
|
||||||
|
pktExpected |= (uint16_t)frame.content[frameRead+1];
|
||||||
|
//flog::debug("Starting to read a {} byte packet at offset {}", pktExpected, frameRead);
|
||||||
|
|
||||||
|
// Skip to the packet content
|
||||||
|
frameRead += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] pktBuffer;
|
||||||
|
}
|
||||||
|
}
|
69
decoder_modules/ryfi_decoder/src/ryfi/receiver.h
Normal file
69
decoder_modules/ryfi_decoder/src/ryfi/receiver.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "utils/new_event.h"
|
||||||
|
#include "dsp/demod/psk.h"
|
||||||
|
#include "dsp/routing/doubler.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "rs_codec.h"
|
||||||
|
#include "conv_codec.h"
|
||||||
|
#include "framing.h"
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
class Receiver {
|
||||||
|
public:
|
||||||
|
Receiver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a transmitter.
|
||||||
|
* @param in Baseband input.
|
||||||
|
* @param baudrate Baudrate to use over the air.
|
||||||
|
* @param samplerate Samplerate of the baseband.
|
||||||
|
*/
|
||||||
|
Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a transmitter.
|
||||||
|
* @param in Baseband input.
|
||||||
|
* @param baudrate Baudrate to use over the air.
|
||||||
|
* @param samplerate Samplerate of the baseband.
|
||||||
|
*/
|
||||||
|
void init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the input stream.
|
||||||
|
* @param in Baseband input.
|
||||||
|
*/
|
||||||
|
void setInput(dsp::stream<dsp::complex_t>* in);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~Receiver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the transmitter's DSP.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the transmitter's DSP.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
dsp::stream<dsp::complex_t>* softOut;
|
||||||
|
|
||||||
|
NewEvent<Packet> onPacket;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void worker();
|
||||||
|
|
||||||
|
// DSP
|
||||||
|
dsp::demod::PSK<4> demod;
|
||||||
|
dsp::routing::Doubler<dsp::complex_t> doubler;
|
||||||
|
Deframer deframer;
|
||||||
|
ConvDecoder conv;
|
||||||
|
RSDecoder rs;
|
||||||
|
|
||||||
|
bool running = false;
|
||||||
|
std::thread workerThread;
|
||||||
|
};
|
||||||
|
}
|
169
decoder_modules/ryfi_decoder/src/ryfi/rs_codec.cpp
Normal file
169
decoder_modules/ryfi_decoder/src/ryfi/rs_codec.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include "rs_codec.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
RSEncoder::RSEncoder(dsp::stream<uint8_t>* in) {
|
||||||
|
// Create the convolutional encoder instance
|
||||||
|
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32);
|
||||||
|
|
||||||
|
// Init the base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
RSEncoder::~RSEncoder() {
|
||||||
|
// Destroy the convolutional encoder instance
|
||||||
|
correct_reed_solomon_destroy(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RSEncoder::encode(const uint8_t* in, uint8_t* out, int count) {
|
||||||
|
// Check the size
|
||||||
|
assert(count == RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE);
|
||||||
|
|
||||||
|
// Go through each block
|
||||||
|
uint8_t block[RS_BLOCK_ENC_SIZE];
|
||||||
|
for (int i = 0; i < RS_BLOCK_COUNT; i++) {
|
||||||
|
// Encode block
|
||||||
|
correct_reed_solomon_encode(rs, &in[i*RS_BLOCK_DEC_SIZE], RS_BLOCK_DEC_SIZE, block);
|
||||||
|
|
||||||
|
// Interleave into the frame
|
||||||
|
int k = 0;
|
||||||
|
for (int j = i; j < RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT; j += RS_BLOCK_COUNT) {
|
||||||
|
out[j] = block[k++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scramble
|
||||||
|
for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) {
|
||||||
|
out[i] ^= RS_SCRAMBLER_SEQ[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RSEncoder::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
RSDecoder::RSDecoder(dsp::stream<uint8_t>* in) {
|
||||||
|
// Create the convolutional encoder instance
|
||||||
|
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32);
|
||||||
|
|
||||||
|
// Init the base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
RSDecoder::~RSDecoder() {
|
||||||
|
// Destroy the convolutional encoder instance
|
||||||
|
correct_reed_solomon_destroy(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RSDecoder::decode(uint8_t* in, uint8_t* out, int count) {
|
||||||
|
// Check the size
|
||||||
|
assert(count == RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE);
|
||||||
|
|
||||||
|
// Descramble (TODO: Don't do it in-place)
|
||||||
|
for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) {
|
||||||
|
in[i] ^= RS_SCRAMBLER_SEQ[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through each block
|
||||||
|
uint8_t block[RS_BLOCK_ENC_SIZE];
|
||||||
|
for (int i = 0; i < RS_BLOCK_COUNT; i++) {
|
||||||
|
// Deinterleave out of the frame
|
||||||
|
int k = 0;
|
||||||
|
for (int j = i; j < count; j += RS_BLOCK_COUNT) {
|
||||||
|
block[k++] = in[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode block and return if decoding fails
|
||||||
|
int res = correct_reed_solomon_decode(rs, block, RS_BLOCK_ENC_SIZE, &out[i*RS_BLOCK_DEC_SIZE]);
|
||||||
|
if (res < 0) { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RSDecoder::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (count && !out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT] = {
|
||||||
|
0x75, 0x05, 0x7C, 0xCE, 0xF1, 0xD0, 0x6C, 0xF6, 0xFA, 0x65, 0xF6, 0xFC, 0xE0, 0x0A, 0x82, 0x17,
|
||||||
|
0x6C, 0xBE, 0x76, 0xA0, 0xD6, 0x46, 0x12, 0x2E, 0xDE, 0xB5, 0xF7, 0xAD, 0xCB, 0x51, 0x63, 0x47,
|
||||||
|
0x27, 0x30, 0x7E, 0x43, 0xD1, 0xA1, 0xCB, 0x10, 0x08, 0x49, 0xDF, 0x86, 0xD4, 0xC4, 0xD7, 0x3C,
|
||||||
|
0x6D, 0x03, 0x07, 0x37, 0x5B, 0xB3, 0xCD, 0x79, 0x6F, 0x1E, 0xBA, 0xC5, 0x6E, 0xC3, 0x8C, 0x7A,
|
||||||
|
0x25, 0x99, 0x61, 0x54, 0x5A, 0x96, 0x57, 0x9B, 0xE0, 0x60, 0x5B, 0x09, 0x6D, 0x8B, 0x2D, 0x9D,
|
||||||
|
0x15, 0x9D, 0x0E, 0xBF, 0x57, 0xFB, 0x9C, 0x49, 0x82, 0x2C, 0x48, 0x59, 0x92, 0x47, 0x79, 0x17,
|
||||||
|
0x16, 0x74, 0xEA, 0xEA, 0xBB, 0xC5, 0x72, 0x32, 0x17, 0xD1, 0xB3, 0xDE, 0xEB, 0x15, 0xC7, 0x55,
|
||||||
|
0x8A, 0xF2, 0x88, 0xC2, 0x33, 0xA6, 0x17, 0x8B, 0xD4, 0x77, 0x22, 0x00, 0x63, 0x47, 0x45, 0x5F,
|
||||||
|
0x36, 0x35, 0x58, 0x8B, 0x88, 0xEC, 0xCA, 0xC4, 0x60, 0x53, 0x9E, 0xBD, 0xB2, 0xF5, 0x51, 0x46,
|
||||||
|
0x34, 0x9A, 0x07, 0x25, 0x3F, 0xF5, 0x65, 0x63, 0x77, 0x3C, 0x5A, 0xFA, 0x4E, 0x0C, 0xF7, 0x1B,
|
||||||
|
0x82, 0xAB, 0x73, 0x06, 0x7F, 0xB7, 0xC6, 0x6B, 0xBF, 0xB1, 0x46, 0xF3, 0x01, 0x91, 0xB1, 0xFF,
|
||||||
|
0x5C, 0x6F, 0xF9, 0x43, 0x0E, 0x6A, 0x70, 0x89, 0x0B, 0xEA, 0x8C, 0xD4, 0x1B, 0x51, 0x01, 0x31,
|
||||||
|
0x71, 0x2E, 0xDF, 0x24, 0xC1, 0xD5, 0xDB, 0x0E, 0xF5, 0xEB, 0x78, 0x79, 0x39, 0x5B, 0xAD, 0xC3,
|
||||||
|
0xA9, 0xA6, 0x60, 0x30, 0xA2, 0x9A, 0x7B, 0xA0, 0xF4, 0xAA, 0xC5, 0x57, 0xB3, 0x16, 0xF9, 0xB5,
|
||||||
|
0x79, 0x20, 0xC1, 0x88, 0x9A, 0x00, 0x43, 0xB2, 0xC6, 0x84, 0x8D, 0x03, 0xF2, 0xD8, 0x90, 0x7A,
|
||||||
|
0x21, 0x37, 0x7E, 0xF7, 0x75, 0xE5, 0xFB, 0xC9, 0xDC, 0xAB, 0x4B, 0xBC, 0x35, 0x38, 0xB9, 0x3A,
|
||||||
|
0x53, 0x89, 0x7E, 0xD5, 0x94, 0x12, 0x2D, 0x9B, 0x91, 0x90, 0x1D, 0x4D, 0x0E, 0xE0, 0x93, 0xF3,
|
||||||
|
0xC1, 0xA1, 0x9B, 0x73, 0x27, 0x22, 0x41, 0x27, 0xEE, 0x2A, 0xD7, 0x45, 0xBC, 0x8F, 0x9B, 0xA2,
|
||||||
|
0x36, 0x11, 0x16, 0x37, 0x1A, 0xF1, 0x2E, 0x71, 0xCF, 0x86, 0x89, 0x83, 0x5A, 0xF1, 0x24, 0x6C,
|
||||||
|
0x56, 0x71, 0x53, 0xE4, 0xD2, 0xCB, 0xCA, 0x86, 0x1E, 0xA0, 0xD5, 0x83, 0x3B, 0xEF, 0x09, 0x09,
|
||||||
|
0xC2, 0x07, 0x53, 0x86, 0xE6, 0x8A, 0xC6, 0x70, 0xFB, 0x91, 0x43, 0xCB, 0x91, 0x6E, 0xA9, 0xBC,
|
||||||
|
0x31, 0x42, 0x61, 0x0C, 0x88, 0xB8, 0x2C, 0xED, 0xD8, 0xE6, 0xA3, 0xEC, 0xAC, 0xB9, 0x45, 0x5E,
|
||||||
|
0x2C, 0x73, 0x3F, 0x2E, 0x06, 0xE0, 0xBF, 0x73, 0xDD, 0x2E, 0x45, 0x50, 0x6C, 0x53, 0x55, 0xF0,
|
||||||
|
0x7F, 0x6E, 0x61, 0xFA, 0xA0, 0x7A, 0x1C, 0xF0, 0xBD, 0xAC, 0x48, 0x61, 0x03, 0x6B, 0xED, 0x54,
|
||||||
|
0x2A, 0x27, 0x94, 0xF6, 0xF9, 0x6A, 0x04, 0x08, 0x0B, 0x3C, 0xC3, 0x30, 0x66, 0x01, 0xFB, 0xDC,
|
||||||
|
0xC9, 0x65, 0x03, 0x83, 0x7D, 0x0A, 0xDF, 0xA5, 0x04, 0x14, 0xE4, 0xF2, 0x4C, 0x01, 0xDF, 0x04,
|
||||||
|
0xD2, 0x80, 0xB9, 0x9B, 0xD9, 0x5E, 0xF8, 0x2A, 0x93, 0x8D, 0x8C, 0x09, 0x9B, 0x38, 0xEC, 0x3B,
|
||||||
|
0xC4, 0x29, 0x90, 0x7C, 0x65, 0x3A, 0xF2, 0x4B, 0x69, 0xD3, 0x63, 0x9B, 0x40, 0x95, 0xC3, 0xFB,
|
||||||
|
0x67, 0x54, 0x40, 0x9B, 0x26, 0x9F, 0x52, 0xFE, 0xD8, 0xD0, 0x24, 0x9C, 0x5C, 0xD4, 0xEF, 0xDE,
|
||||||
|
0x28, 0x66, 0x75, 0x04, 0xCB, 0xA4, 0xC0, 0xB9, 0x4B, 0xC9, 0x20, 0x4B, 0x56, 0xC7, 0x86, 0xC5,
|
||||||
|
0x39, 0x45, 0x18, 0xA7, 0x48, 0x14, 0x1A, 0x51, 0xCA, 0xD0, 0xC0, 0x15, 0xDD, 0xC1, 0x28, 0x4A,
|
||||||
|
0x7A, 0xD2, 0x10, 0xEA, 0x83, 0xD3, 0x3A, 0xEF, 0x48, 0x29, 0x41, 0xA4, 0xD4, 0x57, 0xA6, 0x1D,
|
||||||
|
0x76, 0x24, 0x93, 0x58, 0x7E, 0xB7, 0xDD, 0x0B, 0xF2, 0xCE, 0x71, 0x55, 0xF5, 0xAB, 0x8C, 0xC8,
|
||||||
|
0x70, 0x59, 0x73, 0x69, 0x9D, 0x29, 0x5E, 0x59, 0xF4, 0xB2, 0xC4, 0x97, 0x75, 0xF0, 0x65, 0x1B,
|
||||||
|
0x66, 0x5F, 0xA4, 0x33, 0x5C, 0xC7, 0xBF, 0x45, 0xE6, 0x20, 0xC0, 0xBD, 0xAD, 0xAE, 0x9F, 0x97,
|
||||||
|
0x05, 0xD8, 0x04, 0x2B, 0x0A, 0x46, 0xE8, 0xB8, 0xCB, 0x00, 0xE2, 0x7C, 0x70, 0x1B, 0x49, 0xDE,
|
||||||
|
0x81, 0xEB, 0x24, 0xAC, 0x1B, 0x3E, 0x09, 0xFB, 0xAC, 0xB7, 0xF2, 0xD1, 0xB2, 0x78, 0xF3, 0xAC,
|
||||||
|
0xC7, 0x6A, 0xA2, 0x07, 0x4C, 0xED, 0x61, 0xAD, 0x04, 0x7F, 0x45, 0x83, 0x59, 0x31, 0x27, 0xF0,
|
||||||
|
0x16, 0x6B, 0x0C, 0xAA, 0xD4, 0xD1, 0xCB, 0x1C, 0x51, 0x41, 0x0D, 0x2F, 0x8F, 0xF9, 0xF9, 0x7F,
|
||||||
|
0x22, 0x89, 0x46, 0xF4, 0xB8, 0x93, 0x98, 0x9E, 0x3E, 0x23, 0xF1, 0x6E, 0x64, 0x08, 0xB6, 0xC9,
|
||||||
|
0x6E, 0x53, 0x53, 0xED, 0xAD, 0x21, 0xCD, 0x1A, 0xF0, 0x45, 0xFC, 0x14, 0x00, 0xEA, 0xF7, 0x42,
|
||||||
|
0xEE, 0xDA, 0x58, 0x0D, 0x85, 0xBC, 0x74, 0xFB, 0x73, 0x78, 0xB5, 0x5E, 0x5E, 0x6F, 0x6F, 0x7E,
|
||||||
|
0x39, 0xC2, 0x05, 0x50, 0xDB, 0x3D, 0xB8, 0xF3, 0x8F, 0x80, 0xEC, 0x46, 0x29, 0x39, 0x89, 0xF3,
|
||||||
|
0x55, 0x9C, 0x6A, 0x5F, 0x7C, 0xD9, 0x7C, 0x13, 0xE4, 0x56, 0x5E, 0xE9, 0x60, 0x19, 0xE2, 0x7D,
|
||||||
|
0xC4, 0x41, 0x92, 0x8D, 0xDA, 0x21, 0x58, 0x20, 0xE9, 0xA8, 0x4C, 0x16, 0x34, 0x99, 0xAC, 0xB7,
|
||||||
|
0x30, 0xBD, 0x39, 0x19, 0xAC, 0x9B, 0x4B, 0x27, 0xFA, 0x32, 0xC1, 0x48, 0xA1, 0x80, 0x34, 0x36,
|
||||||
|
0x1E, 0xFB, 0x92, 0x43, 0x35, 0x72, 0x2D, 0xEF, 0xD2, 0xF2, 0xFC, 0xC2, 0x85, 0xAB, 0x59, 0x40,
|
||||||
|
0x8D, 0x9D, 0x1A, 0x1F, 0xE2, 0x92, 0x87, 0xA2, 0xF9, 0x2C, 0x78, 0xE4, 0xC3, 0x26, 0x56, 0x07,
|
||||||
|
0xB3, 0x78, 0xAF, 0x79, 0x3D, 0x88, 0xF4, 0xAD, 0x66, 0x7C, 0x07, 0x58, 0x98, 0x82, 0x1A, 0x26,
|
||||||
|
0xF7, 0xFD, 0xCE, 0xFF, 0x75, 0xED, 0xAB, 0xBD, 0xAE, 0x6D, 0x5C, 0x28, 0x91, 0xF3, 0xB7, 0x5C,
|
||||||
|
0x27, 0x05, 0xEC, 0x3B, 0xE3, 0xDD, 0x93, 0x24, 0x7F, 0xAD, 0x14, 0xAA, 0x49, 0x61, 0x8F, 0x96,
|
||||||
|
0x1F, 0xAA, 0xB2, 0xEE, 0xA8, 0x24, 0x41, 0x7C, 0xDC, 0xF1, 0x28, 0x26, 0xE6, 0x7F, 0x98, 0x20,
|
||||||
|
0x50, 0x5F, 0x90, 0x21, 0x8A, 0x09, 0x26, 0x59, 0xD0, 0x07, 0x2F, 0xE1, 0x35, 0x4D, 0x0B, 0x20,
|
||||||
|
0xB2, 0xD5, 0xDD, 0xB5, 0xAC, 0x1B, 0xFE, 0xD9, 0xE3, 0x35, 0xF1, 0xB8, 0x3F, 0x3D, 0xFC, 0x0B,
|
||||||
|
0x5A, 0x57, 0xA9, 0x92, 0x2B, 0xC8, 0x3E, 0xC2, 0xAA, 0xEF, 0xB9, 0x98, 0x2C, 0xA8, 0xAB, 0xF6,
|
||||||
|
0xA1, 0xBF, 0xBC, 0x8D, 0x97, 0xA2, 0x74, 0xD9, 0xE5, 0x99, 0x85, 0x81, 0x15, 0xB0, 0xE7, 0x8B,
|
||||||
|
0x48, 0x86, 0xF4, 0x94, 0x9C, 0x62, 0x82, 0xD1, 0x2C, 0x24, 0x4B, 0xAC, 0x7A, 0xB8, 0x4E, 0x4A,
|
||||||
|
0xD2, 0xF6, 0xAA, 0xED, 0xE0, 0x9C, 0x98, 0xD2, 0xDF, 0xC1, 0xBC, 0xBF, 0x55, 0x7D, 0x40, 0xB5,
|
||||||
|
0xDE, 0xD4, 0x25, 0xBB, 0x81, 0xF4, 0x07, 0x1D, 0xE7, 0x3C, 0xB4, 0x62, 0xC9, 0x55, 0x0A, 0x3A,
|
||||||
|
0xD5, 0xCE, 0x97, 0xED, 0x30, 0x76, 0x76, 0x51, 0xBC, 0x8C, 0xE4, 0x54, 0xBE, 0xB7, 0xB5, 0xCD,
|
||||||
|
0xF8, 0x76, 0x37, 0x53, 0x2C, 0x9F, 0xE4, 0xC7, 0xEB, 0xF5, 0x8D, 0x23, 0x8A, 0xDA, 0xD1, 0xA9,
|
||||||
|
0xD8, 0x4C, 0x53, 0xF3, 0x49, 0xA7, 0x1A, 0x5D, 0xE5, 0x03, 0x49, 0x52, 0xD3, 0xE2, 0x1F, 0xA5,
|
||||||
|
0x35, 0x9C, 0xBB, 0x0B, 0xC7, 0x0D, 0xA4, 0x65, 0x54, 0x8B, 0x39, 0xF1, 0x3B, 0x67, 0x21, 0x71,
|
||||||
|
0x10, 0xE7, 0x76, 0xC4, 0xA8, 0xC2, 0x9D, 0x93, 0xC6, 0x51, 0xBA, 0x23
|
||||||
|
};
|
||||||
|
}
|
81
decoder_modules/ryfi_decoder/src/ryfi/rs_codec.h
Normal file
81
decoder_modules/ryfi_decoder/src/ryfi/rs_codec.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "dsp/processor.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "correct.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
// Size of an encoded reed-solomon block.
|
||||||
|
inline const int RS_BLOCK_ENC_SIZE = 255;
|
||||||
|
|
||||||
|
// Size of a decoded reed-solomon block.
|
||||||
|
inline const int RS_BLOCK_DEC_SIZE = 223;
|
||||||
|
|
||||||
|
// Number of reed-solomon blocks.
|
||||||
|
inline const int RS_BLOCK_COUNT = 4;
|
||||||
|
|
||||||
|
// Scrambler sequence
|
||||||
|
extern const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RyFi Reed-Solomon Encoder.
|
||||||
|
*/
|
||||||
|
class RSEncoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||||
|
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a reed-solomon encoder specifying an input stream.
|
||||||
|
* @param in Input stream
|
||||||
|
*/
|
||||||
|
RSEncoder(dsp::stream<uint8_t>* in = NULL);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~RSEncoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode data.
|
||||||
|
* @param in Input bytes.
|
||||||
|
* @param out Output bytes.
|
||||||
|
* @param count Number of input bytes.
|
||||||
|
* @return Number of output bytes.
|
||||||
|
*/
|
||||||
|
int encode(const uint8_t* in, uint8_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
correct_reed_solomon* rs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RyFi Reed-Solomon Decoder.
|
||||||
|
*/
|
||||||
|
class RSDecoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||||
|
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a reed-solomon decoder specifying an input stream.
|
||||||
|
* @param in Input stream
|
||||||
|
*/
|
||||||
|
RSDecoder(dsp::stream<uint8_t>* in = NULL);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~RSDecoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode data.
|
||||||
|
* @param in Input bytes.
|
||||||
|
* @param out Output bytes.
|
||||||
|
* @param count Number of input bytes.
|
||||||
|
* @return Number of output bytes.
|
||||||
|
*/
|
||||||
|
int decode(uint8_t* in, uint8_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
correct_reed_solomon* rs;
|
||||||
|
};
|
||||||
|
}
|
177
decoder_modules/ryfi_decoder/src/ryfi/transmitter.cpp
Normal file
177
decoder_modules/ryfi_decoder/src/ryfi/transmitter.cpp
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
#include "transmitter.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
Transmitter::Transmitter(double baudrate, double samplerate) {
|
||||||
|
// Initialize the DSP
|
||||||
|
rs.setInput(&in);
|
||||||
|
conv.setInput(&rs.out);
|
||||||
|
framer.setInput(&conv.out);
|
||||||
|
resamp.init(&framer.out, baudrate, samplerate);
|
||||||
|
|
||||||
|
rrcTaps = dsp::taps::rootRaisedCosine<float>(511, 0.6, baudrate, samplerate);
|
||||||
|
// Normalize the taps
|
||||||
|
float tot = 0.0f;
|
||||||
|
for (int i = 0; i < rrcTaps.size; i++) {
|
||||||
|
tot += rrcTaps.taps[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < rrcTaps.size; i++) {
|
||||||
|
rrcTaps.taps[i] /= tot;
|
||||||
|
}
|
||||||
|
|
||||||
|
rrc.init(&resamp.out, rrcTaps);
|
||||||
|
out = &rrc.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transmitter::~Transmitter() {
|
||||||
|
// Stop everything
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transmitter::start() {
|
||||||
|
// Do nothing if already running
|
||||||
|
if (running) { return; }
|
||||||
|
|
||||||
|
// Start the worker thread
|
||||||
|
workerThread = std::thread(&Transmitter::worker, this);
|
||||||
|
|
||||||
|
// Start the DSP
|
||||||
|
rs.start();
|
||||||
|
conv.start();
|
||||||
|
framer.start();
|
||||||
|
resamp.start();
|
||||||
|
rrc.start();
|
||||||
|
|
||||||
|
// Update the running state
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transmitter::stop() {
|
||||||
|
// Do nothing if not running
|
||||||
|
if (!running) { return; }
|
||||||
|
|
||||||
|
// Stop the worker thread
|
||||||
|
in.stopWriter();
|
||||||
|
if (workerThread.joinable()) { workerThread.join(); }
|
||||||
|
in.clearWriteStop();
|
||||||
|
|
||||||
|
// Stop the DSP
|
||||||
|
rs.stop();
|
||||||
|
conv.stop();
|
||||||
|
framer.stop();
|
||||||
|
resamp.stop();
|
||||||
|
rrc.stop();
|
||||||
|
|
||||||
|
// Update the running state
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Transmitter::send(const Packet& pkt) {
|
||||||
|
// Acquire the packet queue
|
||||||
|
std::lock_guard<std::mutex> lck(packetsMtx);
|
||||||
|
|
||||||
|
// If there are too many packets queued up, drop the packet
|
||||||
|
if (packets.size() >= MAX_QUEUE_SIZE) { return false; }
|
||||||
|
|
||||||
|
// Push the packet onto the queue
|
||||||
|
packets.push(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Transmitter::txFrame(const Frame& frame) {
|
||||||
|
// Serialize the frame
|
||||||
|
int count = frame.serialize(in.writeBuf);
|
||||||
|
|
||||||
|
// Send it off
|
||||||
|
return in.swap(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet Transmitter::popPacket() {
|
||||||
|
// Acquire the packet queue
|
||||||
|
std::unique_lock<std::mutex> lck(packetsMtx);
|
||||||
|
|
||||||
|
// If no packets are available, return empty packet
|
||||||
|
if (!packets.size()) { return Packet(); }
|
||||||
|
|
||||||
|
// Pop the front packet and return it
|
||||||
|
Packet pkt = packets.front();
|
||||||
|
packets.pop();
|
||||||
|
return pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transmitter::worker() {
|
||||||
|
Frame frame;
|
||||||
|
Packet pkt;
|
||||||
|
uint16_t counter = 0;
|
||||||
|
int pktToWrite = 0;
|
||||||
|
int pktWritten = 0;
|
||||||
|
uint8_t* pktBuffer = new uint8_t[Packet::MAX_SERIALIZED_SIZE];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Initialize the frame
|
||||||
|
frame.counter = counter++;
|
||||||
|
frame.firstPacket = PKT_OFFS_NONE;
|
||||||
|
frame.lastPacket = PKT_OFFS_NONE;
|
||||||
|
int frameOffset = 0;
|
||||||
|
|
||||||
|
// Fill the frame with as much packet data as possible
|
||||||
|
while (frameOffset < sizeof(Frame::content)) {
|
||||||
|
// If there is no packet in the process of being sent
|
||||||
|
if (!pktWritten) {
|
||||||
|
// If there is not enough space for the size of the packet
|
||||||
|
if ((sizeof(Frame::content) - frameOffset) < 2) {
|
||||||
|
// Fill the rest of the frame with noise and send it
|
||||||
|
for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next packet
|
||||||
|
pkt = popPacket();
|
||||||
|
|
||||||
|
// If there was an available packet
|
||||||
|
if (pkt) {
|
||||||
|
// Serialize the packet
|
||||||
|
pktToWrite = pkt.serializedSize();
|
||||||
|
pkt.serialize(pktBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none was available
|
||||||
|
if (!pkt) {
|
||||||
|
// Fill the rest of the frame with noise and send it
|
||||||
|
for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the beginning of the packet
|
||||||
|
if (!pktWritten) {
|
||||||
|
//flog::debug("Starting to write a {} byte packet at offset {}", pktToWrite-2, frameOffset);
|
||||||
|
|
||||||
|
// If this is the first packet of the frame, update its offset
|
||||||
|
if (frame.firstPacket == PKT_OFFS_NONE) { frame.firstPacket = frameOffset; }
|
||||||
|
|
||||||
|
// Update the last packet pointer
|
||||||
|
frame.lastPacket = frameOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the amount of data writeable to the frame
|
||||||
|
int writeable = std::min<int>(pktToWrite - pktWritten, sizeof(Frame::content) - frameOffset);
|
||||||
|
|
||||||
|
// Copy the data to the frame
|
||||||
|
memcpy(&frame.content[frameOffset], &pktBuffer[pktWritten], writeable);
|
||||||
|
pktWritten += writeable;
|
||||||
|
frameOffset += writeable;
|
||||||
|
|
||||||
|
// If the packet is done being sent
|
||||||
|
if (pktWritten >= pktToWrite) {
|
||||||
|
// Prepare for a new packet
|
||||||
|
pktToWrite = 0;
|
||||||
|
pktWritten = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the frame
|
||||||
|
if (!txFrame(frame)) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] pktBuffer;
|
||||||
|
}
|
||||||
|
}
|
69
decoder_modules/ryfi_decoder/src/ryfi/transmitter.h
Normal file
69
decoder_modules/ryfi_decoder/src/ryfi/transmitter.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "dsp/multirate/rational_resampler.h"
|
||||||
|
#include "dsp/taps/root_raised_cosine.h"
|
||||||
|
#include "dsp/filter/fir.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "rs_codec.h"
|
||||||
|
#include "conv_codec.h"
|
||||||
|
#include "framing.h"
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
class Transmitter {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a transmitter.
|
||||||
|
* @param baudrate Baudrate to use over the air.
|
||||||
|
* @param samplerate Samplerate of the baseband.
|
||||||
|
*/
|
||||||
|
Transmitter(double baudrate, double samplerate);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~Transmitter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the transmitter's DSP.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the transmitter's DSP.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet.
|
||||||
|
* @param pkg Packet to send.
|
||||||
|
* @return True if the packet was send, false if it was dropped.
|
||||||
|
*/
|
||||||
|
bool send(const Packet& pkt);
|
||||||
|
|
||||||
|
// Baseband output
|
||||||
|
dsp::stream<dsp::complex_t>* out;
|
||||||
|
|
||||||
|
static inline const int MAX_QUEUE_SIZE = 32;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool txFrame(const Frame& frame);
|
||||||
|
Packet popPacket();
|
||||||
|
void worker();
|
||||||
|
|
||||||
|
// Packet queue
|
||||||
|
std::mutex packetsMtx;
|
||||||
|
std::queue<Packet> packets;
|
||||||
|
|
||||||
|
// DSP
|
||||||
|
dsp::stream<uint8_t> in;
|
||||||
|
RSEncoder rs;
|
||||||
|
ConvEncoder conv;
|
||||||
|
Framer framer;
|
||||||
|
dsp::multirate::RationalResampler<dsp::complex_t> resamp;
|
||||||
|
dsp::tap<float> rrcTaps;
|
||||||
|
dsp::filter::FIR<dsp::complex_t, float> rrc;
|
||||||
|
|
||||||
|
bool running = false;
|
||||||
|
std::thread workerThread;
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user