mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-11-10 12:47:40 +01:00
more progress on M17 support
This commit is contained in:
parent
187fc2cb9e
commit
efd3c47a6c
@ -31,8 +31,14 @@ namespace ImGui {
|
||||
|
||||
window->DrawList->AddRectFilled(min, ImVec2(min.x+size.x, min.y+size.y), IM_COL32(0,0,0,255));
|
||||
ImU32 col = ImGui::GetColorU32(ImGuiCol_CheckMark, 0.7f);
|
||||
ImU32 col2 = ImGui::GetColorU32(ImGuiCol_CheckMark, 0.7f);
|
||||
float increment = size.x / (float)sampleCount;
|
||||
float val;
|
||||
|
||||
for (auto l : lines) {
|
||||
window->DrawList->AddLine(ImVec2(min.x, (((l * _scale) + 1) * (size.y*0.5f)) + min.y), ImVec2(min.x + size.x, (((l * _scale) + 1) * (size.y*0.5f)) + min.y), IM_COL32(80, 80, 80, 255));
|
||||
}
|
||||
|
||||
for (int i = 0; i < sampleCount; i++) {
|
||||
val = buffer[i] * _scale;
|
||||
if (val > 1.0f || val < -1.0f) { continue; }
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <dsp/stream.h>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
namespace ImGui {
|
||||
@ -17,6 +18,8 @@ namespace ImGui {
|
||||
|
||||
void releaseBuffer();
|
||||
|
||||
std::vector<float> lines;
|
||||
|
||||
private:
|
||||
std::mutex bufferMtx;
|
||||
float* buffer;
|
||||
|
@ -1,8 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(m17_decoder)
|
||||
|
||||
add_subdirectory("libcorrect")
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
@ -14,19 +12,45 @@ endif ()
|
||||
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
||||
|
||||
include_directories("src/")
|
||||
include_directories("libcorrect/include")
|
||||
|
||||
include_directories("C:/Users/ryzerth/Documents/Code/codec2/src")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")
|
||||
|
||||
add_library(m17_decoder SHARED ${SRC})
|
||||
target_link_libraries(m17_decoder PRIVATE sdrpp_core)
|
||||
target_link_libraries(m17_decoder PRIVATE correct_static)
|
||||
set_target_properties(m17_decoder PROPERTIES PREFIX "")
|
||||
|
||||
target_link_directories(sdrpp_core PUBLIC "C:/Users/ryzerth/Documents/Code/codec2/build/src")
|
||||
target_link_libraries(m17_decoder PRIVATE libcodec2)
|
||||
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_include_directories(m17_decoder PUBLIC "C:/Users/ryzerth/Documents/Code/codec2/src")
|
||||
target_link_directories(sdrpp_core PUBLIC "C:/Users/ryzerth/Documents/Code/codec2/build/src")
|
||||
|
||||
target_include_directories(m17_decoder PUBLIC "C:/Program Files (x86)/Correct/include")
|
||||
target_link_directories(sdrpp_core PUBLIC "C:/Program Files (x86)/Correct/lib")
|
||||
|
||||
target_link_libraries(m17_decoder PRIVATE libcodec2)
|
||||
target_link_libraries(m17_decoder PRIVATE correct)
|
||||
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(LIBCODEC2 REQUIRED codec2)
|
||||
|
||||
target_include_directories(m17_decoder PUBLIC ${LIBCODEC2_INCLUDE_DIRS})
|
||||
target_link_directories(m17_decoder PUBLIC ${LIBCODEC2_LIBRARY_DIRS})
|
||||
target_link_libraries(m17_decoder PUBLIC ${LIBCODEC2_LIBRARIES})
|
||||
|
||||
target_link_libraries(m17_decoder PUBLIC correct)
|
||||
|
||||
# Include it because for some reason pkgconfig doesn't look here?
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
target_include_directories(m17_decoder PUBLIC "/usr/local/include")
|
||||
endif()
|
||||
|
||||
endif ()
|
||||
|
||||
|
||||
|
||||
# Install directives
|
||||
install(TARGETS m17_decoder DESTINATION lib/sdrpp/plugins)
|
16
m17_decoder/src/base40.cpp
Normal file
16
m17_decoder/src/base40.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include <base40.h>
|
||||
|
||||
void decode_callsign_base40(uint64_t encoded, char *callsign) {
|
||||
if (encoded >= 262144000000000) { // 40^9
|
||||
*callsign = 0;
|
||||
return;
|
||||
}
|
||||
char *p = callsign;
|
||||
for (; encoded > 0; p++) {
|
||||
*p = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."[encoded % 40];
|
||||
encoded /= 40;
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
return;
|
||||
}
|
4
m17_decoder/src/base40.h
Normal file
4
m17_decoder/src/base40.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
void decode_callsign_base40(uint64_t encoded, char* callsign);
|
72
m17_decoder/src/crc16.h
Normal file
72
m17_decoder/src/crc16.h
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2020 Mobilinkd LLC.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
namespace mobilinkd
|
||||
{
|
||||
|
||||
template <uint16_t Poly = 0x5935, uint16_t Init = 0xFFFF>
|
||||
struct CRC16
|
||||
{
|
||||
static constexpr uint16_t MASK = 0xFFFF;
|
||||
static constexpr uint16_t LSB = 0x0001;
|
||||
static constexpr uint16_t MSB = 0x8000;
|
||||
|
||||
uint16_t reg_ = Init;
|
||||
|
||||
void reset()
|
||||
{
|
||||
reg_ = Init;
|
||||
|
||||
for (size_t i = 0; i != 16; ++i)
|
||||
{
|
||||
auto bit = reg_ & LSB;
|
||||
if (bit) reg_ ^= Poly;
|
||||
reg_ >>= 1;
|
||||
if (bit) reg_ |= MSB;
|
||||
}
|
||||
|
||||
reg_ &= MASK;
|
||||
}
|
||||
|
||||
void operator()(uint8_t byte)
|
||||
{
|
||||
reg_ = crc(byte, reg_);
|
||||
}
|
||||
|
||||
uint16_t crc(uint8_t byte, uint16_t reg)
|
||||
{
|
||||
for (size_t i = 0; i != 8; ++i)
|
||||
{
|
||||
auto msb = reg & MSB;
|
||||
reg = ((reg << 1) & MASK) | ((byte >> (7 - i)) & LSB);
|
||||
if (msb) reg ^= Poly;
|
||||
}
|
||||
return reg & MASK;
|
||||
}
|
||||
|
||||
uint16_t get()
|
||||
{
|
||||
auto reg = reg_;
|
||||
for (size_t i = 0; i != 16; ++i)
|
||||
{
|
||||
auto msb = reg & MSB;
|
||||
reg = ((reg << 1) & MASK);
|
||||
if (msb) reg ^= Poly;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 2> get_bytes()
|
||||
{
|
||||
auto crc = get();
|
||||
std::array<uint8_t, 2> result{uint8_t((crc >> 8) & 0xFF), uint8_t(crc & 0xFF)};
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // mobilinkd
|
233
m17_decoder/src/golay24.h
Normal file
233
m17_decoder/src/golay24.h
Normal file
@ -0,0 +1,233 @@
|
||||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace mobilinkd {
|
||||
|
||||
// Parts are adapted from:
|
||||
// http://aqdi.com/articles/using-the-golay-error-detection-and-correction-code-3/
|
||||
|
||||
namespace Golay24
|
||||
{
|
||||
|
||||
int popcount(uint32_t n) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
count += ((n >> i) & 1);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// Need a constexpr sort.
|
||||
// https://stackoverflow.com/a/40030044/854133
|
||||
template<class T>
|
||||
void swap(T& l, T& r)
|
||||
{
|
||||
T tmp = std::move(l);
|
||||
l = std::move(r);
|
||||
r = std::move(tmp);
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
struct array
|
||||
{
|
||||
constexpr T& operator[](size_t i)
|
||||
{
|
||||
return arr[i];
|
||||
}
|
||||
|
||||
constexpr const T& operator[](size_t i) const
|
||||
{
|
||||
return arr[i];
|
||||
}
|
||||
|
||||
constexpr const T* begin() const
|
||||
{
|
||||
return arr;
|
||||
}
|
||||
constexpr const T* end() const
|
||||
{
|
||||
return arr + N;
|
||||
}
|
||||
|
||||
T arr[N];
|
||||
};
|
||||
|
||||
template <typename T, size_t N>
|
||||
void sort_impl(array<T, N> &array, size_t left, size_t right)
|
||||
{
|
||||
if (left < right)
|
||||
{
|
||||
size_t m = left;
|
||||
|
||||
for (size_t i = left + 1; i<right; i++)
|
||||
if (array[i]<array[left])
|
||||
swap(array[++m], array[i]);
|
||||
|
||||
swap(array[left], array[m]);
|
||||
|
||||
sort_impl(array, left, m);
|
||||
sort_impl(array, m + 1, right);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
array<T, N> sort(array<T, N> array)
|
||||
{
|
||||
auto sorted = array;
|
||||
sort_impl(sorted, 0, N);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
// static constexpr uint16_t POLY = 0xAE3;
|
||||
constexpr uint16_t POLY = 0xC75;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct SyndromeMapEntry
|
||||
{
|
||||
uint32_t a{0};
|
||||
uint16_t b{0};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
/**
|
||||
* Calculate the syndrome of a [23,12] Golay codeword.
|
||||
*
|
||||
* @return the 11-bit syndrome of the codeword in bits [22:12].
|
||||
*/
|
||||
uint32_t syndrome(uint32_t codeword)
|
||||
{
|
||||
codeword &= 0xffffffl;
|
||||
for (size_t i = 0; i != 12; ++i)
|
||||
{
|
||||
if (codeword & 1)
|
||||
codeword ^= POLY;
|
||||
codeword >>= 1;
|
||||
}
|
||||
return (codeword << 12);
|
||||
}
|
||||
|
||||
bool parity(uint32_t codeword)
|
||||
{
|
||||
return popcount(codeword) & 1;
|
||||
}
|
||||
|
||||
SyndromeMapEntry makeSyndromeMapEntry(uint64_t val)
|
||||
{
|
||||
return SyndromeMapEntry{uint32_t(val >> 16), uint16_t(val & 0xFFFF)};
|
||||
}
|
||||
|
||||
uint64_t makeSME(uint64_t syndrome, uint32_t bits)
|
||||
{
|
||||
return (syndrome << 24) | (bits & 0xFFFFFF);
|
||||
}
|
||||
|
||||
constexpr size_t LUT_SIZE = 2048;
|
||||
|
||||
std::array<SyndromeMapEntry, LUT_SIZE> make_lut()
|
||||
{
|
||||
constexpr size_t VECLEN=23;
|
||||
detail::array<uint64_t, LUT_SIZE> result{};
|
||||
|
||||
size_t index = 0;
|
||||
result[index++] = makeSME(syndrome(0), 0);
|
||||
|
||||
for (size_t i = 0; i != VECLEN; ++i)
|
||||
{
|
||||
auto v = (1 << i);
|
||||
result[index++] = makeSME(syndrome(v), v);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != VECLEN - 1; ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j != VECLEN; ++j)
|
||||
{
|
||||
auto v = (1 << i) | (1 << j);
|
||||
result[index++] = makeSME(syndrome(v), v);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != VECLEN - 2; ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j != VECLEN - 1; ++j)
|
||||
{
|
||||
for (size_t k = j + 1; k != VECLEN; ++k)
|
||||
{
|
||||
auto v = (1 << i) | (1 << j) | (1 << k);
|
||||
result[index++] = makeSME(syndrome(v), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = detail::sort(result);
|
||||
|
||||
std::array<SyndromeMapEntry, LUT_SIZE> tmp;
|
||||
for (size_t i = 0; i != LUT_SIZE; ++i)
|
||||
{
|
||||
tmp[i] = makeSyndromeMapEntry(result[i]);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline auto LUT = make_lut();
|
||||
|
||||
/**
|
||||
* Calculate [23,12] Golay codeword.
|
||||
*
|
||||
* @return checkbits(11)|data(12).
|
||||
*/
|
||||
uint32_t encode23(uint16_t data)
|
||||
{
|
||||
// data &= 0xfff;
|
||||
uint32_t codeword = data;
|
||||
for (size_t i = 0; i != 12; ++i)
|
||||
{
|
||||
if (codeword & 1)
|
||||
codeword ^= POLY;
|
||||
codeword >>= 1;
|
||||
}
|
||||
return codeword | (data << 11);
|
||||
}
|
||||
|
||||
uint32_t encode24(uint16_t data)
|
||||
{
|
||||
auto codeword = encode23(data);
|
||||
return ((codeword << 1) | parity(codeword));
|
||||
}
|
||||
|
||||
bool decode(uint32_t input, uint32_t& output)
|
||||
{
|
||||
auto syndrm = syndrome(input >> 1);
|
||||
auto it = std::lower_bound(LUT.begin(), LUT.end(), syndrm,
|
||||
[](const SyndromeMapEntry& sme, uint32_t val){
|
||||
return (sme.a >> 8) < val;
|
||||
});
|
||||
|
||||
if ((it->a >> 8) == syndrm)
|
||||
{
|
||||
// Build the correction from the compressed entry.
|
||||
auto correction = ((((it->a & 0xFF) << 16) | it->b) << 1);
|
||||
// Apply the correction to the input.
|
||||
output = input ^ correction;
|
||||
// Only test parity for 3-bit errors.
|
||||
return popcount(syndrm) < 3 || !parity(output);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // Golay24
|
||||
|
||||
} // mobilinkd
|
112
m17_decoder/src/lsf_decode.cpp
Normal file
112
m17_decoder/src/lsf_decode.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include <lsf_decode.h>
|
||||
#include <string.h>
|
||||
#include <base40.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <crc16.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
bool M17CheckCRC(uint8_t* data, int len) {
|
||||
// TODO: Implement
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* M17DataTypesTxt[4] = {
|
||||
"Unknown",
|
||||
"Data",
|
||||
"Voice",
|
||||
"Voice & Data"
|
||||
};
|
||||
|
||||
const char* M17EncryptionTypesTxt[4] = {
|
||||
"None",
|
||||
"AES",
|
||||
"Scrambler",
|
||||
"Unknown"
|
||||
};
|
||||
|
||||
M17LSF M17DecodeLSF(uint8_t* _lsf) {
|
||||
M17LSF lsf;
|
||||
|
||||
// Extract CRC
|
||||
lsf.rawCRC = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
lsf.rawCRC |= (((uint16_t)_lsf[(i+48+48+16+112) / 8] >> (7 - (i%8))) & 1) << (15 - i);
|
||||
}
|
||||
|
||||
// Check CRC
|
||||
mobilinkd::CRC16 crc16;
|
||||
crc16.reset();
|
||||
for (int i = 0; i < 28; i++) {
|
||||
crc16(_lsf[i]);
|
||||
}
|
||||
if (crc16.get() != lsf.rawCRC) {
|
||||
lsf.valid = false;
|
||||
return lsf;
|
||||
}
|
||||
lsf.valid = true;
|
||||
|
||||
|
||||
// Extract DST
|
||||
lsf.rawDst = 0;
|
||||
for (int i = 0; i < 48; i++) {
|
||||
lsf.rawDst |= (((uint64_t)_lsf[i / 8] >> (7 - (i%8))) & 1) << (47 - i);
|
||||
}
|
||||
|
||||
// Extract SRC
|
||||
lsf.rawSrc = 0;
|
||||
for (int i = 0; i < 48; i++) {
|
||||
lsf.rawSrc |= (((uint64_t)_lsf[(i+48) / 8] >> (7 - (i%8))) & 1) << (47 - i);
|
||||
}
|
||||
|
||||
// Extract TYPE
|
||||
lsf.rawType = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
lsf.rawType |= (((uint16_t)_lsf[(i+48+48) / 8] >> (7 - (i%8))) & 1) << (15 - i);
|
||||
}
|
||||
|
||||
// Extract META
|
||||
memcpy(lsf.meta, &_lsf[14], 14);
|
||||
|
||||
// Decode DST
|
||||
if (lsf.rawDst == 0) {
|
||||
lsf.dst = "Invalid";
|
||||
}
|
||||
else if (lsf.rawDst <= 262143999999999) {
|
||||
char buf[128];
|
||||
decode_callsign_base40(lsf.rawDst, buf);
|
||||
lsf.dst = buf;
|
||||
}
|
||||
else if (lsf.rawDst == 0xFFFFFFFFFFFF) {
|
||||
lsf.dst = "Broadcast";
|
||||
}
|
||||
else {
|
||||
char buf[128];
|
||||
sprintf(buf, "%" PRIX64, lsf.rawDst);
|
||||
lsf.dst = buf;
|
||||
}
|
||||
|
||||
// Decode SRC
|
||||
if (lsf.rawSrc == 0 || lsf.rawSrc == 0xFFFFFFFFFFFF) {
|
||||
lsf.src = "Invalid";
|
||||
}
|
||||
else if (lsf.rawSrc <= 262143999999999) {
|
||||
char buf[128];
|
||||
decode_callsign_base40(lsf.rawSrc, buf);
|
||||
lsf.src = buf;
|
||||
}
|
||||
else {
|
||||
char buf[128];
|
||||
sprintf(buf, "%" PRIX64, lsf.rawSrc);
|
||||
lsf.src = buf;
|
||||
}
|
||||
|
||||
// Decode TYPE
|
||||
lsf.isStream = (lsf.rawType >> 0) & 0b1;
|
||||
lsf.dataType = (M17DataType)((lsf.rawType >> 1) & 0b11);
|
||||
lsf.encryptionType = (M17EncryptionType)((lsf.rawType >> 3) & 0b11);
|
||||
lsf.encryptionSubType = (lsf.rawType >> 5) & 0b11;
|
||||
lsf.channelAccessNum = (lsf.rawType >> 7) & 0b1111;
|
||||
|
||||
return lsf;
|
||||
}
|
42
m17_decoder/src/lsf_decode.h
Normal file
42
m17_decoder/src/lsf_decode.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
enum M17DataType {
|
||||
M17_DATATYPE_UNKNOWN = 0b00,
|
||||
M17_DATATYPE_DATA = 0b01,
|
||||
M17_DATATYPE_VOICE = 0b10,
|
||||
M17_DATATYPE_DATA_VOICE = 0b11
|
||||
};
|
||||
|
||||
enum M17EncryptionType {
|
||||
M17_ENCRYPTION_NONE = 0b00,
|
||||
M17_ENCRYPTION_AES = 0b01,
|
||||
M17_ENCRYPTION_SCRAMBLE = 0b10,
|
||||
M17_ENCRYPTION_UNKNOWN = 0b11
|
||||
};
|
||||
|
||||
extern const char* M17DataTypesTxt[4];
|
||||
extern const char* M17EncryptionTypesTxt[4];
|
||||
|
||||
struct M17LSF {
|
||||
uint64_t rawDst;
|
||||
uint64_t rawSrc;
|
||||
uint16_t rawType;
|
||||
uint8_t meta[14];
|
||||
uint16_t rawCRC;
|
||||
|
||||
std::string dst;
|
||||
std::string src;
|
||||
bool isStream;
|
||||
M17DataType dataType;
|
||||
M17EncryptionType encryptionType;
|
||||
uint8_t encryptionSubType;
|
||||
uint8_t channelAccessNum;
|
||||
|
||||
bool valid;
|
||||
};
|
||||
|
||||
bool M17CheckCRC(uint8_t* data, int len);
|
||||
|
||||
M17LSF M17DecodeLSF(uint8_t* _lsf);
|
@ -3,6 +3,8 @@
|
||||
#include <volk/volk.h>
|
||||
#include <codec2.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <golay24.h>
|
||||
#include <lsf_decode.h>
|
||||
|
||||
extern "C" {
|
||||
#include <correct.h>
|
||||
@ -11,12 +13,14 @@ extern "C" {
|
||||
#define M17_DEVIATION 2400.0f
|
||||
#define M17_BAUDRATE 4800.0f
|
||||
#define M17_RRC_ALPHA 0.5f
|
||||
#define M17_4FSK_HIGH_CUT 0.52f
|
||||
#define M17_4FSK_HIGH_CUT 0.5f
|
||||
|
||||
#define M17_SYNC_SIZE 16
|
||||
#define M17_LICH_SIZE 96
|
||||
#define M17_PAYLOAD_SIZE 144
|
||||
#define M17_ENCODED_PAYLOAD_SIZE 296
|
||||
#define M17_LSF_SIZE 240
|
||||
#define M17_ENCODED_LSF_SIZE 488
|
||||
#define M17_RAW_FRAME_SIZE 384
|
||||
#define M17_CUT_FRAME_SIZE 368
|
||||
|
||||
@ -179,14 +183,14 @@ namespace dsp {
|
||||
if (type == 0) {
|
||||
linkSetupOut.writeBuf[id] = delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE];
|
||||
}
|
||||
else if (type == 1 && id < M17_LICH_SIZE) {
|
||||
lichOut.writeBuf[id/8] |= (delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE]) << (7 - (id%8));
|
||||
else if ((type == 1 || type == 2) && id < M17_LICH_SIZE) {
|
||||
lichOut.writeBuf[id] = delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE];
|
||||
}
|
||||
else if (type == 1) {
|
||||
streamOut.writeBuf[id - M17_LICH_SIZE] = (delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE]);
|
||||
}
|
||||
else if (type == 2) {
|
||||
packetOut.writeBuf[id] = (delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE]);
|
||||
packetOut.writeBuf[id - M17_LICH_SIZE] = (delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE]);
|
||||
}
|
||||
|
||||
outCount++;
|
||||
@ -198,10 +202,11 @@ namespace dsp {
|
||||
if (!linkSetupOut.swap(M17_CUT_FRAME_SIZE)) { return -1; }
|
||||
}
|
||||
else if (type == 1) {
|
||||
if (!lichOut.swap(12)) {return -1; }
|
||||
if (!lichOut.swap(M17_LICH_SIZE)) {return -1; }
|
||||
if (!streamOut.swap(M17_CUT_FRAME_SIZE)) {return -1; }
|
||||
}
|
||||
else if (type == 2) {
|
||||
if (!lichOut.swap(M17_LICH_SIZE)) {return -1; }
|
||||
if (!packetOut.swap(M17_CUT_FRAME_SIZE)) { return -1; }
|
||||
}
|
||||
}
|
||||
@ -213,7 +218,7 @@ namespace dsp {
|
||||
detect = true;
|
||||
outCount = 0;
|
||||
type = 0;
|
||||
spdlog::warn("Found sync frame");
|
||||
//spdlog::warn("Found sync frame");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -222,8 +227,7 @@ namespace dsp {
|
||||
detect = true;
|
||||
outCount = 0;
|
||||
type = 1;
|
||||
memset(lichOut.writeBuf, 0, 12);
|
||||
spdlog::warn("Found stream frame");
|
||||
//spdlog::warn("Found stream frame");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -232,7 +236,7 @@ namespace dsp {
|
||||
detect = true;
|
||||
outCount = 0;
|
||||
type = 2;
|
||||
spdlog::warn("Found packet frame");
|
||||
//spdlog::warn("Found packet frame");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -263,6 +267,85 @@ namespace dsp {
|
||||
|
||||
};
|
||||
|
||||
class M17LSFDecoder : public generic_block<M17LSFDecoder> {
|
||||
public:
|
||||
M17LSFDecoder() {}
|
||||
|
||||
M17LSFDecoder(stream<uint8_t>* in, void (*handler)(M17LSF& lsf, void* ctx), void* ctx) { init(in, handler, ctx); }
|
||||
|
||||
~M17LSFDecoder() {
|
||||
if (!generic_block<M17LSFDecoder>::_block_init) { return; }
|
||||
generic_block<M17LSFDecoder>::stop();
|
||||
correct_convolutional_destroy(conv);
|
||||
}
|
||||
|
||||
void init(stream<uint8_t>* in, void (*handler)(M17LSF& lsf, void* ctx), void* ctx) {
|
||||
_in = in;
|
||||
_handler = handler;
|
||||
_ctx = ctx;
|
||||
|
||||
conv = correct_convolutional_create(2, 5, correct_conv_m17_polynomial);
|
||||
|
||||
generic_block<M17LSFDecoder>::registerInput(_in);
|
||||
generic_block<M17LSFDecoder>::_block_init = true;
|
||||
}
|
||||
|
||||
void setInput(stream<uint8_t>* in) {
|
||||
assert(generic_block<M17LSFDecoder>::_block_init);
|
||||
std::lock_guard<std::mutex> lck(generic_block<M17LSFDecoder>::ctrlMtx);
|
||||
generic_block<M17LSFDecoder>::tempStop();
|
||||
generic_block<M17LSFDecoder>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<M17LSFDecoder>::registerInput(_in);
|
||||
generic_block<M17LSFDecoder>::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
int count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
// Depuncture the data
|
||||
int inOffset = 0;
|
||||
for (int i = 0; i < M17_ENCODED_LSF_SIZE; i++) {
|
||||
if (!M17_PUNCTURING_P1[i % 61]) {
|
||||
depunctured[i] = 0;
|
||||
continue;
|
||||
}
|
||||
depunctured[i] = _in->readBuf[inOffset++];
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
|
||||
// Pack into bytes
|
||||
memset(packed, 0, 61);
|
||||
for (int i = 0; i < M17_ENCODED_LSF_SIZE; i++) {
|
||||
packed[i/8] |= depunctured[i] << (7 - (i%8));
|
||||
}
|
||||
|
||||
// Run through convolutional decoder
|
||||
correct_convolutional_decode(conv, packed, M17_ENCODED_LSF_SIZE, lsf);
|
||||
|
||||
// Decode it and call the handler
|
||||
M17LSF decLsf = M17DecodeLSF(lsf);
|
||||
if (decLsf.valid) { _handler(decLsf, _ctx); }
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private:
|
||||
stream<uint8_t>* _in;
|
||||
|
||||
void (*_handler)(M17LSF& lsf, void* ctx);
|
||||
void* _ctx;
|
||||
|
||||
uint8_t depunctured[488];
|
||||
uint8_t packed[61];
|
||||
uint8_t lsf[30];
|
||||
|
||||
correct_convolutional* conv;
|
||||
|
||||
};
|
||||
|
||||
class M17PayloadFEC : public generic_block<M17PayloadFEC> {
|
||||
public:
|
||||
M17PayloadFEC() {}
|
||||
@ -310,6 +393,7 @@ namespace dsp {
|
||||
}
|
||||
|
||||
// Pack into bytes
|
||||
memset(packed, 0, 37);
|
||||
for (int i = 0; i < M17_ENCODED_PAYLOAD_SIZE; i++) {
|
||||
if (!(i%8)) { packed[i/8] = 0; }
|
||||
packed[i/8] |= depunctured[i] << (7 - (i%8));
|
||||
@ -408,15 +492,116 @@ namespace dsp {
|
||||
|
||||
};
|
||||
|
||||
class M17LICHDecoder : public generic_block<M17LICHDecoder> {
|
||||
public:
|
||||
M17LICHDecoder() {}
|
||||
|
||||
M17LICHDecoder(stream<uint8_t>* in, void (*handler)(M17LSF& lsf, void* ctx), void* ctx) { init(in, handler, ctx); }
|
||||
|
||||
void init(stream<uint8_t>* in, void (*handler)(M17LSF& lsf, void* ctx), void* ctx) {
|
||||
_in = in;
|
||||
_handler = handler;
|
||||
_ctx = ctx;
|
||||
generic_block<M17LICHDecoder>::registerInput(_in);
|
||||
generic_block<M17LICHDecoder>::_block_init = true;
|
||||
}
|
||||
|
||||
void setInput(stream<uint8_t>* in) {
|
||||
assert(generic_block<M17LICHDecoder>::_block_init);
|
||||
std::lock_guard<std::mutex> lck(generic_block<M17LICHDecoder>::ctrlMtx);
|
||||
generic_block<M17LICHDecoder>::tempStop();
|
||||
generic_block<M17LICHDecoder>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<M17LICHDecoder>::registerInput(_in);
|
||||
generic_block<M17LICHDecoder>::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
int count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
// Zero out block
|
||||
memset(chunk, 0, 6);
|
||||
|
||||
// Decode the 4 Golay(24, 12) blocks
|
||||
uint32_t encodedBlock;
|
||||
uint32_t decodedBlock;
|
||||
for (int b = 0; b < 4; b++) {
|
||||
// Pack the 24bit block into a byte
|
||||
encodedBlock = 0;
|
||||
decodedBlock = 0;
|
||||
for (int i = 0; i < 24; i++) { encodedBlock |= _in->readBuf[(b * 24) + i] << (23 - i); }
|
||||
|
||||
// Decode
|
||||
if (!mobilinkd::Golay24::decode(encodedBlock, decodedBlock)) {
|
||||
_in->flush();
|
||||
return count;
|
||||
}
|
||||
|
||||
// Pack the decoded bits into the output
|
||||
int id = 0;
|
||||
uint8_t temp;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
id = (b * 12) + i;
|
||||
chunk[id / 8] |= ((decodedBlock >> (23 - i)) & 1) << (7 - (id%8));
|
||||
}
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
|
||||
int partId = chunk[5] >> 5;
|
||||
|
||||
// If the ID of the chunk is zero, start a new LSF
|
||||
if (partId == 0) {
|
||||
newFrame = true;
|
||||
lastId = 0;
|
||||
memcpy(&lsf[partId*5], chunk, 5);
|
||||
return count;
|
||||
}
|
||||
|
||||
// If we're recording a LSF and a discontinuity shows up, cancel
|
||||
if (newFrame && partId != lastId + 1) {
|
||||
newFrame = false;
|
||||
return count;
|
||||
}
|
||||
|
||||
// If we're recording and there's no discontinuity (see above), add the data to the full frame
|
||||
if (newFrame) {
|
||||
lastId = partId;
|
||||
memcpy(&lsf[partId*5], chunk, 5);
|
||||
|
||||
// If the lsf is complete, send it out
|
||||
if (partId == 5) {
|
||||
newFrame = false;
|
||||
M17LSF decLsf = M17DecodeLSF(lsf);
|
||||
if (decLsf.valid) { _handler(decLsf, _ctx); }
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private:
|
||||
stream<uint8_t>* _in;
|
||||
void (*_handler)(M17LSF& lsf, void* ctx);
|
||||
void* _ctx;
|
||||
|
||||
uint8_t chunk[6];
|
||||
uint8_t lsf[240];
|
||||
bool newFrame = false;
|
||||
int lastId = 0;
|
||||
|
||||
};
|
||||
|
||||
class M17Decoder : public generic_hier_block<M17Decoder> {
|
||||
public:
|
||||
M17Decoder() {}
|
||||
|
||||
M17Decoder(stream<complex_t>* input, float sampleRate) {
|
||||
init(input, sampleRate);
|
||||
M17Decoder(stream<complex_t>* input, float sampleRate, void (*handler)(M17LSF& lsf, void* ctx), void* ctx) {
|
||||
init(input, sampleRate, handler, ctx);
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* input, float sampleRate) {
|
||||
void init(stream<complex_t>* input, float sampleRate, void (*handler)(M17LSF& lsf, void* ctx), void* ctx) {
|
||||
_sampleRate = sampleRate;
|
||||
|
||||
demod.init(input, _sampleRate, M17_DEVIATION);
|
||||
@ -426,11 +611,11 @@ namespace dsp {
|
||||
doubler.init(&recov.out);
|
||||
slice.init(&doubler.outA);
|
||||
demux.init(&slice.out);
|
||||
lsfFEC.init(&demux.linkSetupOut, handler, ctx);
|
||||
payloadFEC.init(&demux.streamOut);
|
||||
decodeLICH.init(&demux.lichOut, handler, ctx);
|
||||
decodeAudio.init(&payloadFEC.out);
|
||||
|
||||
ns0.init(&demux.linkSetupOut);
|
||||
ns1.init(&demux.lichOut);
|
||||
ns2.init(&demux.packetOut);
|
||||
|
||||
diagOut = &doubler.outB;
|
||||
@ -442,11 +627,11 @@ namespace dsp {
|
||||
generic_hier_block<M17Decoder>::registerBlock(&doubler);
|
||||
generic_hier_block<M17Decoder>::registerBlock(&slice);
|
||||
generic_hier_block<M17Decoder>::registerBlock(&demux);
|
||||
generic_hier_block<M17Decoder>::registerBlock(&lsfFEC);
|
||||
generic_hier_block<M17Decoder>::registerBlock(&payloadFEC);
|
||||
generic_hier_block<M17Decoder>::registerBlock(&decodeLICH);
|
||||
generic_hier_block<M17Decoder>::registerBlock(&decodeAudio);
|
||||
|
||||
generic_hier_block<M17Decoder>::registerBlock(&ns0);
|
||||
generic_hier_block<M17Decoder>::registerBlock(&ns1);
|
||||
generic_hier_block<M17Decoder>::registerBlock(&ns2);
|
||||
|
||||
generic_hier_block<M17Decoder>::_block_init = true;
|
||||
@ -468,11 +653,11 @@ namespace dsp {
|
||||
StreamDoubler<float> doubler;
|
||||
M17Slice4FSK slice;
|
||||
M17FrameDemux demux;
|
||||
M17LSFDecoder lsfFEC;
|
||||
M17PayloadFEC payloadFEC;
|
||||
M17LICHDecoder decodeLICH;
|
||||
M17Codec2Decode decodeAudio;
|
||||
|
||||
NullSink<uint8_t> ns0;
|
||||
NullSink<uint8_t> ns1;
|
||||
NullSink<uint8_t> ns2;
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <gui/widgets/symbol_diagram.h>
|
||||
#include <m17dsp.h>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
@ -36,16 +37,31 @@ ConfigManager config;
|
||||
|
||||
class M17DecoderModule : public ModuleManager::Instance {
|
||||
public:
|
||||
M17DecoderModule(std::string name) : diag(1.0f, 480) {
|
||||
M17DecoderModule(std::string name) : diag(0.8, 480) {
|
||||
this->name = name;
|
||||
lsf.valid = false;
|
||||
|
||||
// Load config
|
||||
config.acquire();
|
||||
if (!config.conf.contains(name)) {
|
||||
config.conf[name]["showLines"] = false;
|
||||
}
|
||||
showLines = config.conf[name]["showLines"];
|
||||
if (showLines) {
|
||||
diag.lines.push_back(-0.75f);
|
||||
diag.lines.push_back(-0.25f);
|
||||
diag.lines.push_back(0.25f);
|
||||
diag.lines.push_back(0.75f);
|
||||
}
|
||||
config.release(true);
|
||||
|
||||
|
||||
// Initialize VFO
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 9600, INPUT_SAMPLE_RATE, 9600, 9600, true);
|
||||
vfo->setSnapInterval(500);
|
||||
vfo->setSnapInterval(250);
|
||||
|
||||
// Intialize DSP here
|
||||
decoder.init(vfo->output, INPUT_SAMPLE_RATE);
|
||||
decoder.init(vfo->output, INPUT_SAMPLE_RATE, lsfHandler, this);
|
||||
|
||||
resampWin.init(4000, 4000, audioSampRate);
|
||||
resamp.init(decoder.out, &resampWin, 8000, audioSampRate);
|
||||
@ -90,7 +106,7 @@ public:
|
||||
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), 9600, INPUT_SAMPLE_RATE, 9600, 9600, true);
|
||||
vfo->setSnapInterval(500);
|
||||
vfo->setSnapInterval(250);
|
||||
|
||||
// Set Input of demod here
|
||||
decoder.setInput(vfo->output);
|
||||
@ -130,6 +146,95 @@ private:
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
_this->diag.draw();
|
||||
|
||||
{
|
||||
std::lock_guard lck(_this->lsfMtx);
|
||||
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(now-_this->lastUpdated).count() > 1000) {
|
||||
_this->lsf.valid = false;
|
||||
}
|
||||
|
||||
ImGui::BeginTable(CONCAT("##m17_info_tbl_", _this->name), 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders);
|
||||
if (!_this->lsf.valid) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Source");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("--");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Destination");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("--");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Data Type");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("--");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Encryption");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("-- (Subtype --)");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("CAN");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("--");
|
||||
}
|
||||
else {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Source");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text(_this->lsf.src.c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Destination");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text(_this->lsf.dst.c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Data Type");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text(M17DataTypesTxt[_this->lsf.dataType]);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Encryption");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("%s (Subtype %d)", M17EncryptionTypesTxt[_this->lsf.encryptionType], _this->lsf.encryptionSubType);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("CAN");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("%d", _this->lsf.channelAccessNum);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) {
|
||||
if (_this->showLines) {
|
||||
_this->diag.lines.push_back(-0.75f);
|
||||
_this->diag.lines.push_back(-0.25f);
|
||||
_this->diag.lines.push_back(0.25f);
|
||||
_this->diag.lines.push_back(0.75f);
|
||||
}
|
||||
else {
|
||||
_this->diag.lines.clear();
|
||||
}
|
||||
config.acquire();
|
||||
config.conf[_this->name]["showLines"] = _this->showLines;
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
if (!_this->enabled) { style::endDisabled(); }
|
||||
}
|
||||
|
||||
@ -152,6 +257,13 @@ private:
|
||||
_this->resamp.tempStart();
|
||||
}
|
||||
|
||||
static void lsfHandler(M17LSF& lsf, void* ctx) {
|
||||
M17DecoderModule* _this = (M17DecoderModule*)ctx;
|
||||
std::lock_guard lck(_this->lsfMtx);
|
||||
_this->lastUpdated = std::chrono::high_resolution_clock::now();
|
||||
_this->lsf = lsf;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
|
||||
@ -172,6 +284,11 @@ private:
|
||||
EventHandler<float> srChangeHandler;
|
||||
SinkManager::Stream stream;
|
||||
|
||||
bool showLines = false;
|
||||
|
||||
M17LSF lsf;
|
||||
std::mutex lsfMtx;
|
||||
std::chrono::steady_clock::time_point lastUpdated;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
|
Loading…
Reference in New Issue
Block a user