mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 01:55:48 -05:00
M17: debug and SMS transmit
This commit is contained in:
parent
f10da64717
commit
6b863287d8
@ -5,6 +5,7 @@ set(modems_SOURCES
|
||||
m17/FreqDevEstimator.cpp
|
||||
m17/Golay24.cpp
|
||||
m17/M17Demodulator.cpp
|
||||
m17/M17Modulator.cpp
|
||||
)
|
||||
|
||||
set(modems_HEADERS
|
||||
@ -28,7 +29,6 @@ set(modems_HEADERS
|
||||
m17/M17FrameDecoder.h
|
||||
m17/M17Framer.h
|
||||
m17/M17Modulator.h
|
||||
m17/M17Modulator.orig.h
|
||||
m17/M17Randomizer.h
|
||||
m17/M17Synchronizer.h
|
||||
m17/PhaseEstimator.h
|
||||
|
@ -104,11 +104,11 @@ class ClockRecovery
|
||||
int8_t offset = sample_index_ - prev_sample_index_;
|
||||
|
||||
// When in spec, the clock should drift by less than 1 sample per frame.
|
||||
if (offset >= MAX_OFFSET) [[unlikely]]
|
||||
if (offset >= MAX_OFFSET)
|
||||
{
|
||||
offset -= SAMPLES_PER_SYMBOL;
|
||||
}
|
||||
else if (offset <= -MAX_OFFSET) [[unlikely]]
|
||||
else if (offset <= -MAX_OFFSET)
|
||||
{
|
||||
offset += SAMPLES_PER_SYMBOL;
|
||||
}
|
||||
@ -120,7 +120,7 @@ class ClockRecovery
|
||||
{
|
||||
// update_sample_index_() must be called first.
|
||||
|
||||
if (frame_count_ == 0) [[unlikely]]
|
||||
if (frame_count_ == 0)
|
||||
{
|
||||
prev_sample_index_ = sample_index_;
|
||||
offset_ = 0.0;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <type_traits>
|
||||
#include <tuple>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
@ -46,7 +47,10 @@ struct M17_API Correlator
|
||||
limit_ = sample_filter(std::abs(value));
|
||||
buffer_[buffer_pos_] = value;
|
||||
prev_buffer_pos_ = buffer_pos_;
|
||||
if (++buffer_pos_ == buffer_.size()) buffer_pos_ = 0;
|
||||
|
||||
if (++buffer_pos_ == buffer_.size()) {
|
||||
buffer_pos_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
float correlate(sync_t sync)
|
||||
@ -56,11 +60,13 @@ struct M17_API Correlator
|
||||
|
||||
for (size_t i = 0; i != sync.size(); ++i)
|
||||
{
|
||||
if (pos >= buffer_.size())
|
||||
if (pos >= buffer_.size()) {
|
||||
pos -= buffer_.size(); // wrapped
|
||||
}
|
||||
result += sync[i] * buffer_[pos];
|
||||
pos += SAMPLES_PER_SYMBOL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -128,8 +134,14 @@ struct SyncWord
|
||||
value_type magnitude_1_ = 1.;
|
||||
value_type magnitude_2_ = -1.;
|
||||
|
||||
SyncWord(buffer_t&& sync_word, value_type magnitude_1, value_type magnitude_2 = std::numeric_limits<value_type>::lowest())
|
||||
: sync_word_(std::move(sync_word)), magnitude_1_(magnitude_1), magnitude_2_(magnitude_2)
|
||||
SyncWord(
|
||||
buffer_t&& sync_word,
|
||||
value_type magnitude_1,
|
||||
value_type magnitude_2 = std::numeric_limits<value_type>::lowest()
|
||||
) :
|
||||
sync_word_(std::move(sync_word)),
|
||||
magnitude_1_(magnitude_1),
|
||||
magnitude_2_(magnitude_2)
|
||||
{}
|
||||
|
||||
value_type triggered(Correlator& correlator)
|
||||
@ -154,6 +166,7 @@ struct SyncWord
|
||||
samples_.fill(0);
|
||||
triggered_ = true;
|
||||
}
|
||||
|
||||
samples_[correlator.index()] = value;
|
||||
}
|
||||
else
|
||||
@ -165,18 +178,22 @@ struct SyncWord
|
||||
timing_index_ = 0;
|
||||
peak_value = value;
|
||||
uint8_t index = 0;
|
||||
for (auto f : samples_)
|
||||
|
||||
for (auto f : samples_)
|
||||
{
|
||||
if (abs(f) > abs(peak_value))
|
||||
{
|
||||
peak_value = f;
|
||||
timing_index_ = index;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
updated_ = peak_value > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return timing_index_;
|
||||
}
|
||||
|
||||
|
@ -2,45 +2,71 @@
|
||||
|
||||
namespace mobilinkd {
|
||||
|
||||
// const std::array<float, 150> M17Demodulator::rrc_taps = std::array<float, 150>{
|
||||
// 0.0029364388513841593, 0.0031468394550958484, 0.002699564567597445, 0.001661182944400927, 0.00023319405581230247,
|
||||
// -0.0012851320781224025, -0.0025577136087664687, -0.0032843366522956313, -0.0032697038088887226, -0.0024733964729590865,
|
||||
// -0.0010285696910973807, 0.0007766690889758685, 0.002553421969211845, 0.0038920145144327816, 0.004451886520053017,
|
||||
// 0.00404219185231544, 0.002674727068399207, 0.0005756567993179152, -0.0018493784971116507, -0.004092346891623224,
|
||||
// -0.005648131453822014, -0.006126925416243605, -0.005349511529163396, -0.003403189203405097, -0.0006430502751187517,
|
||||
// 0.002365929161655135, 0.004957956568090113, 0.006506845894531803, 0.006569574194782443, 0.0050017573119839134,
|
||||
// 0.002017321931508163, -0.0018256054303579805, -0.00571615173291049, -0.008746639552588416, -0.010105075751866371,
|
||||
// -0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491, 0.01071805138282269,
|
||||
// 0.01505751553351295, 0.01679337935001369, 0.015256245142156299, 0.01042830577908502, 0.003031522725559901,
|
||||
// -0.0055333532968188165, -0.013403099825723372, -0.018598682349642525, -0.01944761739590459, -0.015005271935951746,
|
||||
// -0.0053887880354343935, 0.008056525910253532, 0.022816244158307273, 0.035513467692208076, 0.04244131815783876,
|
||||
// 0.04025481153629372, 0.02671818654865632, 0.0013810216516704976, -0.03394615682795165, -0.07502635967975885,
|
||||
// -0.11540977897637611, -0.14703962203941534, -0.16119995609538576, -0.14969512896336504, -0.10610329539459686,
|
||||
// -0.026921412469634916, 0.08757875030779196, 0.23293327870303457, 0.4006012210123992, 0.5786324696325503,
|
||||
// 0.7528286479934068, 0.908262741447522, 1.0309661131633199, 1.1095611856548013, 1.1366197723675815,
|
||||
// 1.1095611856548013, 1.0309661131633199, 0.908262741447522, 0.7528286479934068, 0.5786324696325503,
|
||||
// 0.4006012210123992, 0.23293327870303457, 0.08757875030779196, -0.026921412469634916, -0.10610329539459686,
|
||||
// -0.14969512896336504, -0.16119995609538576, -0.14703962203941534, -0.11540977897637611, -0.07502635967975885,
|
||||
// -0.03394615682795165, 0.0013810216516704976, 0.02671818654865632, 0.04025481153629372, 0.04244131815783876,
|
||||
// 0.035513467692208076, 0.022816244158307273, 0.008056525910253532, -0.0053887880354343935, -0.015005271935951746,
|
||||
// -0.01944761739590459, -0.018598682349642525, -0.013403099825723372, -0.0055333532968188165, 0.003031522725559901,
|
||||
// 0.01042830577908502, 0.015256245142156299, 0.01679337935001369, 0.01505751553351295, 0.01071805138282269,
|
||||
// 0.004891777252042491, -0.001125978562075172, -0.006136551625729697, -0.009265784007800534, -0.010105075751866371,
|
||||
// -0.008746639552588416, -0.00571615173291049, -0.0018256054303579805, 0.002017321931508163, 0.0050017573119839134,
|
||||
// 0.006569574194782443, 0.006506845894531803, 0.004957956568090113, 0.002365929161655135, -0.0006430502751187517,
|
||||
// -0.003403189203405097, -0.005349511529163396, -0.006126925416243605, -0.005648131453822014, -0.004092346891623224,
|
||||
// -0.0018493784971116507, 0.0005756567993179152, 0.002674727068399207, 0.00404219185231544, 0.004451886520053017,
|
||||
// 0.0038920145144327816, 0.002553421969211845, 0.0007766690889758685, -0.0010285696910973807, -0.0024733964729590865,
|
||||
// -0.0032697038088887226, -0.0032843366522956313, -0.0025577136087664687, -0.0012851320781224025, 0.00023319405581230247,
|
||||
// 0.001661182944400927, 0.002699564567597445, 0.0031468394550958484, 0.0029364388513841593, 0.0
|
||||
// };
|
||||
|
||||
// Generated using scikit-commpy N = 150, aplha = 0.5, Ts = 1/4800 s, Fs = 48000 Hz
|
||||
const std::array<float, 150> M17Demodulator::rrc_taps = std::array<float, 150>{
|
||||
0.0029364388513841593, 0.0031468394550958484, 0.002699564567597445, 0.001661182944400927,
|
||||
0.00023319405581230247, -0.0012851320781224025, -0.0025577136087664687, -0.0032843366522956313,
|
||||
-0.0032697038088887226, -0.0024733964729590865, -0.0010285696910973807, 0.0007766690889758685,
|
||||
0.002553421969211845, 0.0038920145144327816, 0.004451886520053017, 0.00404219185231544,
|
||||
0.002674727068399207, 0.0005756567993179152, -0.0018493784971116507, -0.004092346891623224,
|
||||
-0.005648131453822014, -0.006126925416243605, -0.005349511529163396, -0.003403189203405097,
|
||||
-0.0006430502751187517, 0.002365929161655135, 0.004957956568090113, 0.006506845894531803,
|
||||
0.006569574194782443, 0.0050017573119839134, 0.002017321931508163, -0.0018256054303579805,
|
||||
-0.00571615173291049, -0.008746639552588416, -0.010105075751866371, -0.009265784007800534,
|
||||
-0.006136551625729697, -0.001125978562075172, 0.004891777252042491, 0.01071805138282269,
|
||||
0.01505751553351295, 0.01679337935001369, 0.015256245142156299, 0.01042830577908502,
|
||||
0.003031522725559901, -0.0055333532968188165, -0.013403099825723372, -0.018598682349642525,
|
||||
-0.01944761739590459, -0.015005271935951746, -0.0053887880354343935, 0.008056525910253532,
|
||||
0.022816244158307273, 0.035513467692208076, 0.04244131815783876, 0.04025481153629372,
|
||||
0.02671818654865632, 0.0013810216516704976, -0.03394615682795165, -0.07502635967975885,
|
||||
-0.11540977897637611, -0.14703962203941534, -0.16119995609538576, -0.14969512896336504,
|
||||
-0.10610329539459686, -0.026921412469634916, 0.08757875030779196, 0.23293327870303457,
|
||||
0.4006012210123992, 0.5786324696325503, 0.7528286479934068, 0.908262741447522,
|
||||
1.0309661131633199, 1.1095611856548013, 1.1366197723675815, 1.1095611856548013,
|
||||
1.0309661131633199, 0.908262741447522, 0.7528286479934068, 0.5786324696325503,
|
||||
0.4006012210123992, 0.23293327870303457, 0.08757875030779196, -0.026921412469634916,
|
||||
-0.10610329539459686, -0.14969512896336504, -0.16119995609538576, -0.14703962203941534,
|
||||
-0.11540977897637611, -0.07502635967975885, -0.03394615682795165, 0.0013810216516704976,
|
||||
0.02671818654865632, 0.04025481153629372, 0.04244131815783876, 0.035513467692208076,
|
||||
0.022816244158307273, 0.008056525910253532, -0.0053887880354343935, -0.015005271935951746,
|
||||
-0.01944761739590459, -0.018598682349642525, -0.013403099825723372, -0.0055333532968188165,
|
||||
0.003031522725559901, 0.01042830577908502, 0.015256245142156299, 0.01679337935001369,
|
||||
0.01505751553351295, 0.01071805138282269, 0.004891777252042491, -0.001125978562075172,
|
||||
-0.006136551625729697, -0.009265784007800534, -0.010105075751866371, -0.008746639552588416,
|
||||
-0.00571615173291049, -0.0018256054303579805, 0.002017321931508163, 0.0050017573119839134,
|
||||
0.006569574194782443, 0.006506845894531803, 0.004957956568090113, 0.002365929161655135,
|
||||
-0.0006430502751187517, -0.003403189203405097, -0.005349511529163396, -0.006126925416243605,
|
||||
-0.005648131453822014, -0.004092346891623224, -0.0018493784971116507, 0.0005756567993179152,
|
||||
0.002674727068399207, 0.00404219185231544, 0.004451886520053017, 0.0038920145144327816,
|
||||
0.002553421969211845, 0.0007766690889758685, -0.0010285696910973807, -0.0024733964729590865,
|
||||
-0.0032697038088887226, -0.0032843366522956313, -0.0025577136087664687, -0.0012851320781224025,
|
||||
0.00023319405581230247, 0.001661182944400927, 0.002699564567597445, 0.0031468394550958484,
|
||||
0.0029364388513841593, 0.0
|
||||
-8.434122e-04, +3.898184e-04, +1.628891e-03, +2.576674e-03, +2.987740e-03,
|
||||
+2.729505e-03, +1.820181e-03, +4.333001e-04, -1.134215e-03, -2.525029e-03,
|
||||
-3.402452e-03, -3.531573e-03, -2.841363e-03, -1.451929e-03, +3.417005e-04,
|
||||
+2.128236e-03, +3.470212e-03, +4.006361e-03, +3.543024e-03, +2.112321e-03,
|
||||
-1.893023e-05, -2.395144e-03, -4.465932e-03, -5.709548e-03, -5.759027e-03,
|
||||
-4.501582e-03, -2.125844e-03, +8.982825e-04, +3.907892e-03, +6.188389e-03,
|
||||
+7.139194e-03, +6.427125e-03, +4.090469e-03, +5.649447e-04, -3.381677e-03,
|
||||
-6.799652e-03, -8.765190e-03, -8.603529e-03, -6.076811e-03, -1.489520e-03,
|
||||
+4.321674e-03, +1.012385e-02, +1.453219e-02, +1.631886e-02, +1.472302e-02,
|
||||
+9.691259e-03, +1.984723e-03, -6.888307e-03, -1.492227e-02, -2.001531e-02,
|
||||
-2.045303e-02, -1.538011e-02, -5.154591e-03, +8.509368e-03, +2.267330e-02,
|
||||
+3.359618e-02, +3.740502e-02, +3.091849e-02, +1.248579e-02, -1.731807e-02,
|
||||
-5.529141e-02, -9.561492e-02, -1.303248e-01, -1.502279e-01, -1.461577e-01,
|
||||
-1.104008e-01, -3.808012e-02, +7.173159e-02, +2.153664e-01, +3.845237e-01,
|
||||
+5.668902e-01, +7.473693e-01, +9.097718e-01, +1.038746e+00, +1.121677e+00,
|
||||
+1.150282e+00, +1.121677e+00, +1.038746e+00, +9.097718e-01, +7.473693e-01,
|
||||
+5.668902e-01, +3.845237e-01, +2.153664e-01, +7.173159e-02, -3.808012e-02,
|
||||
-1.104008e-01, -1.461577e-01, -1.502279e-01, -1.303248e-01, -9.561492e-02,
|
||||
-5.529141e-02, -1.731807e-02, +1.248579e-02, +3.091849e-02, +3.740502e-02,
|
||||
+3.359618e-02, +2.267330e-02, +8.509368e-03, -5.154591e-03, -1.538011e-02,
|
||||
-2.045303e-02, -2.001531e-02, -1.492227e-02, -6.888307e-03, +1.984723e-03,
|
||||
+9.691259e-03, +1.472302e-02, +1.631886e-02, +1.453219e-02, +1.012385e-02,
|
||||
+4.321674e-03, -1.489520e-03, -6.076811e-03, -8.603529e-03, -8.765190e-03,
|
||||
-6.799652e-03, -3.381677e-03, +5.649447e-04, +4.090469e-03, +6.427125e-03,
|
||||
+7.139194e-03, +6.188389e-03, +3.907892e-03, +8.982825e-04, -2.125844e-03,
|
||||
-4.501582e-03, -5.759027e-03, -5.709548e-03, -4.465932e-03, -2.395144e-03,
|
||||
-1.893023e-05, +2.112321e-03, +3.543024e-03, +4.006361e-03, +3.470212e-03,
|
||||
+2.128236e-03, +3.417005e-04, -1.451929e-03, -2.841363e-03, -3.531573e-03,
|
||||
-3.402452e-03, -2.525029e-03, -1.134215e-03, +4.333001e-04, +1.820181e-03,
|
||||
+2.729505e-03, +2.987740e-03, +2.576674e-03, +1.628891e-03, +3.898184e-04,
|
||||
};
|
||||
|
||||
void M17Demodulator::update_values(uint8_t index)
|
||||
@ -71,8 +97,18 @@ void M17Demodulator::dcd_off()
|
||||
|
||||
if (diagnostic_callback)
|
||||
{
|
||||
diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState,
|
||||
clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), -1);
|
||||
diagnostic_callback(
|
||||
int(dcd_),
|
||||
dev.error(),
|
||||
dev.deviation(),
|
||||
dev.offset(),
|
||||
(int) demodState,
|
||||
clock_recovery.clock_estimate(),
|
||||
sample_index,
|
||||
sync_sample_index,
|
||||
clock_recovery.sample_index(),
|
||||
-1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +141,7 @@ void M17Demodulator::do_unlocked()
|
||||
missing_sync_count += 1;
|
||||
auto sync_index = preamble_sync(correlator);
|
||||
auto sync_updated = preamble_sync.updated();
|
||||
|
||||
if (sync_updated)
|
||||
{
|
||||
sync_count = 0;
|
||||
@ -115,11 +152,14 @@ void M17Demodulator::do_unlocked()
|
||||
sample_index = sync_index;
|
||||
demodState = DemodState::LSF_SYNC;
|
||||
}
|
||||
return;
|
||||
|
||||
return;
|
||||
}
|
||||
auto sync_index = lsf_sync(correlator);
|
||||
|
||||
auto sync_index = lsf_sync(correlator);
|
||||
auto sync_updated = lsf_sync.updated();
|
||||
if (sync_updated)
|
||||
|
||||
if (sync_updated)
|
||||
{
|
||||
sync_count = 0;
|
||||
missing_sync_count = 0;
|
||||
@ -128,19 +168,20 @@ void M17Demodulator::do_unlocked()
|
||||
update_values(sync_index);
|
||||
sample_index = sync_index;
|
||||
demodState = DemodState::FRAME;
|
||||
if (sync_updated < 0)
|
||||
{
|
||||
|
||||
if (sync_updated < 0) {
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::LSF;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sync_index = packet_sync(correlator);
|
||||
sync_updated = packet_sync.updated();
|
||||
if (sync_updated < 0)
|
||||
|
||||
if (sync_updated < 0)
|
||||
{
|
||||
sync_count = 0;
|
||||
missing_sync_count = 0;
|
||||
@ -166,34 +207,59 @@ void M17Demodulator::do_lsf_sync()
|
||||
if (correlator.index() == sample_index)
|
||||
{
|
||||
sync_triggered = preamble_sync.triggered(correlator);
|
||||
|
||||
if (sync_triggered > 0.1)
|
||||
{
|
||||
{
|
||||
std::cerr << "M17Demodulator::do_lsf_sync: preamble:" << sync_triggered << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
sync_triggered = lsf_sync.triggered(correlator);
|
||||
bert_triggered = packet_sync.triggered(correlator);
|
||||
if (bert_triggered < 0)
|
||||
|
||||
if (sync_triggered != 0) {
|
||||
std::cerr << "M17Demodulator::do_lsf_sync: sync_triggered:" << sync_triggered << std::endl;
|
||||
}
|
||||
|
||||
if (bert_triggered != 0) {
|
||||
std::cerr << "M17Demodulator::do_lsf_sync: bert_triggered:" << bert_triggered << std::endl;
|
||||
}
|
||||
|
||||
if (bert_triggered < 0)
|
||||
{
|
||||
missing_sync_count = 0;
|
||||
need_clock_update_ = true;
|
||||
update_values(sample_index);
|
||||
demodState = DemodState::FRAME;
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::BERT;
|
||||
std::cerr << "M17Demodulator::do_lsf_sync: BERT:" << (int) sync_word_type << std::endl;
|
||||
}
|
||||
else if (bert_triggered > 0)
|
||||
{
|
||||
missing_sync_count = 0;
|
||||
need_clock_update_ = true;
|
||||
update_values(sample_index);
|
||||
demodState = DemodState::FRAME;
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
|
||||
std::cerr << "M17Demodulator::do_lsf_sync: PACKET:" << (int) sync_word_type << std::endl;
|
||||
}
|
||||
else if (std::abs(sync_triggered) > 0.1)
|
||||
{
|
||||
missing_sync_count = 0;
|
||||
need_clock_update_ = true;
|
||||
update_values(sample_index);
|
||||
if (sync_triggered > 0)
|
||||
|
||||
if (sync_triggered > 0)
|
||||
{
|
||||
demodState = DemodState::FRAME;
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::LSF;
|
||||
std::cerr << "M17Demodulator::do_lsf_sync: LSF:" << (int) sync_word_type << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
demodState = DemodState::FRAME;
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
|
||||
std::cerr << "M17Demodulator::do_lsf_sync: STREAM:" << (int) sync_word_type << std::endl;
|
||||
}
|
||||
}
|
||||
else if (++missing_sync_count > 192)
|
||||
@ -201,6 +267,7 @@ void M17Demodulator::do_lsf_sync()
|
||||
demodState = DemodState::UNLOCKED;
|
||||
decoder.reset();
|
||||
missing_sync_count = 0;
|
||||
std::cerr << "M17Demodulator::do_lsf_sync: UNLOCKED:" << (int) sync_word_type << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -220,22 +287,26 @@ void M17Demodulator::do_stream_sync()
|
||||
uint8_t sync_index = lsf_sync(correlator);
|
||||
int8_t sync_updated = lsf_sync.updated();
|
||||
sync_count += 1;
|
||||
|
||||
if (sync_updated < 0) // Stream sync word
|
||||
{
|
||||
missing_sync_count = 0;
|
||||
if (sync_count > 70)
|
||||
|
||||
if (sync_count > 70)
|
||||
{
|
||||
update_values(sync_index);
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
|
||||
demodState = DemodState::FRAME;
|
||||
}
|
||||
return;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (sync_count > 87)
|
||||
{
|
||||
update_values(sync_index);
|
||||
missing_sync_count += 1;
|
||||
if (missing_sync_count < MAX_MISSING_SYNC)
|
||||
|
||||
if (missing_sync_count < MAX_MISSING_SYNC)
|
||||
{
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
|
||||
demodState = DemodState::FRAME;
|
||||
@ -257,6 +328,7 @@ void M17Demodulator::do_packet_sync()
|
||||
auto sync_index = packet_sync(correlator);
|
||||
auto sync_updated = packet_sync.updated();
|
||||
sync_count += 1;
|
||||
|
||||
if (sync_count > 70 && sync_updated)
|
||||
{
|
||||
missing_sync_count = 0;
|
||||
@ -267,6 +339,7 @@ void M17Demodulator::do_packet_sync()
|
||||
else if (sync_count > 87)
|
||||
{
|
||||
missing_sync_count += 1;
|
||||
|
||||
if (missing_sync_count < MAX_MISSING_SYNC)
|
||||
{
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
|
||||
@ -288,6 +361,7 @@ void M17Demodulator::do_bert_sync()
|
||||
auto sync_index = packet_sync(correlator);
|
||||
auto sync_updated = packet_sync.updated();
|
||||
sync_count += 1;
|
||||
|
||||
if (sync_count > 70 && sync_updated < 0)
|
||||
{
|
||||
missing_sync_count = 0;
|
||||
@ -298,7 +372,8 @@ void M17Demodulator::do_bert_sync()
|
||||
else if (sync_count > 87)
|
||||
{
|
||||
missing_sync_count += 1;
|
||||
if (missing_sync_count < MAX_MISSING_SYNC)
|
||||
|
||||
if (missing_sync_count < MAX_MISSING_SYNC)
|
||||
{
|
||||
sync_word_type = M17FrameDecoder::SyncWordType::BERT;
|
||||
demodState = DemodState::FRAME;
|
||||
@ -314,25 +389,25 @@ void M17Demodulator::do_bert_sync()
|
||||
|
||||
void M17Demodulator::do_frame(float filtered_sample)
|
||||
{
|
||||
if (correlator.index() != sample_index) return;
|
||||
if (correlator.index() != sample_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
static uint8_t cost_count = 0;
|
||||
|
||||
auto sample = filtered_sample - dev.offset();
|
||||
sample *= dev.idev();
|
||||
sample *= polarity;
|
||||
|
||||
auto n = llr<4>(sample);
|
||||
int8_t* tmp;
|
||||
auto len = framer(n, &tmp);
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
std::cerr << "M17Demodulator::do_frame: sync_word_type:" << (int) sync_word_type << " len:" << len << std::endl;
|
||||
need_clock_update_ = true;
|
||||
|
||||
M17FrameDecoder::input_buffer_t buffer;
|
||||
std::copy(tmp, tmp + len, buffer.begin());
|
||||
auto valid = decoder(sync_word_type, buffer, viterbi_cost);
|
||||
|
||||
cost_count = viterbi_cost > 90 ? cost_count + 1 : 0;
|
||||
cost_count = viterbi_cost > 100 ? cost_count + 1 : cost_count;
|
||||
cost_count = viterbi_cost > 110 ? cost_count + 1 : cost_count;
|
||||
@ -384,17 +459,14 @@ void M17Demodulator::do_frame(float filtered_sample)
|
||||
|
||||
void M17Demodulator::operator()(const float input)
|
||||
{
|
||||
static int16_t initializing = 1920;
|
||||
|
||||
count_++;
|
||||
|
||||
dcd(input);
|
||||
|
||||
// We need to pump a few ms of data through on startup to initialize
|
||||
// the demodulator.
|
||||
if (initializing) [[unlikely]]
|
||||
if (initializing_count_)
|
||||
{
|
||||
--initializing;
|
||||
--initializing_count_;
|
||||
initialize(input);
|
||||
count_ = 0;
|
||||
return;
|
||||
@ -409,8 +481,18 @@ void M17Demodulator::operator()(const float input)
|
||||
|
||||
if (diagnostic_callback)
|
||||
{
|
||||
diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState,
|
||||
clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), viterbi_cost);
|
||||
diagnostic_callback(
|
||||
int(dcd_),
|
||||
dev.error(),
|
||||
dev.deviation(),
|
||||
dev.offset(),
|
||||
(int) demodState,
|
||||
clock_recovery.clock_estimate(),
|
||||
sample_index,
|
||||
sync_sample_index,
|
||||
clock_recovery.sample_index(),
|
||||
viterbi_cost
|
||||
);
|
||||
}
|
||||
|
||||
count_ = 0;
|
||||
@ -420,7 +502,6 @@ void M17Demodulator::operator()(const float input)
|
||||
}
|
||||
|
||||
auto filtered_sample = demod_filter(input);
|
||||
|
||||
correlator.sample(filtered_sample);
|
||||
|
||||
if (correlator.index() == 0)
|
||||
@ -438,17 +519,21 @@ void M17Demodulator::operator()(const float input)
|
||||
uint8_t sync_diff = std::abs(sample_index - sync_sample_index);
|
||||
bool clock_diff_ok = clock_diff <= 1 || clock_diff == 9;
|
||||
bool sync_diff_ok = sync_diff <= 1 || sync_diff == 9;
|
||||
if (clock_diff_ok) sample_index = clock_index;
|
||||
else if (sync_diff_ok) sample_index = sync_sample_index;
|
||||
// else unchanged.
|
||||
|
||||
if (clock_diff_ok) {
|
||||
sample_index = clock_index;
|
||||
} else if (sync_diff_ok) {
|
||||
sample_index = sync_sample_index;
|
||||
}
|
||||
|
||||
// else unchanged.
|
||||
need_clock_update_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
clock_recovery(filtered_sample);
|
||||
|
||||
if (demodState != DemodState::UNLOCKED && correlator.index() == sample_index)
|
||||
{
|
||||
if (demodState != DemodState::UNLOCKED && correlator.index() == sample_index) {
|
||||
dev.sample(filtered_sample);
|
||||
}
|
||||
|
||||
@ -484,8 +569,18 @@ void M17Demodulator::operator()(const float input)
|
||||
|
||||
if (diagnostic_callback)
|
||||
{
|
||||
diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState,
|
||||
clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), viterbi_cost);
|
||||
diagnostic_callback(
|
||||
int(dcd_),
|
||||
dev.error(),
|
||||
dev.deviation(),
|
||||
dev.offset(),
|
||||
(int) demodState,
|
||||
clock_recovery.clock_estimate(),
|
||||
sample_index,
|
||||
sync_sample_index,
|
||||
clock_recovery.sample_index(),
|
||||
viterbi_cost
|
||||
);
|
||||
}
|
||||
|
||||
dcd.update();
|
||||
|
@ -51,10 +51,9 @@ struct M17_API M17Demodulator
|
||||
DataCarrierDetect<SAMPLE_RATE, 500> dcd{2500, 4000, 1.0, 4.0};
|
||||
ClockRecovery<SAMPLE_RATE, SYMBOL_RATE> clock_recovery;
|
||||
|
||||
collelator_t correlator;
|
||||
sync_word_t preamble_sync{{+3,-3,+3,-3,+3,-3,+3,-3}, 29.f};
|
||||
sync_word_t lsf_sync{{+3,+3,+3,+3,-3,-3,+3,-3}, 32.f, -31.f}; // LSF or STREAM (inverted)
|
||||
sync_word_t packet_sync{{3,-3,3,3,-3,-3,-3,-3}, 31.f, -31.f}; // PACKET or BERT (inverted)
|
||||
sync_word_t preamble_sync{{+3, -3, +3, -3, +3, -3, +3, -3}, 29.f};
|
||||
sync_word_t lsf_sync{ {+3, +3, +3, +3, -3, -3, +3, -3}, 32.f, -31.f}; // LSF or STREAM (inverted)
|
||||
sync_word_t packet_sync{ {+3, -3, +3, +3, -3, -3, -3, -3}, 31.f, -31.f}; // PACKET or BERT (inverted)
|
||||
|
||||
FreqDevEstimator dev;
|
||||
float idev;
|
||||
@ -79,8 +78,11 @@ struct M17_API M17Demodulator
|
||||
diagnostic_callback_t diagnostic_callback;
|
||||
|
||||
M17Demodulator(callback_t callback) :
|
||||
decoder(callback)
|
||||
{}
|
||||
decoder(callback),
|
||||
initializing_count_(1920)
|
||||
{
|
||||
demodState = DemodState::UNLOCKED;
|
||||
}
|
||||
|
||||
virtual ~M17Demodulator() {}
|
||||
|
||||
@ -95,14 +97,12 @@ struct M17_API M17Demodulator
|
||||
void do_bert_sync();
|
||||
void do_frame(float filtered_sample);
|
||||
|
||||
bool locked() const
|
||||
{
|
||||
bool locked() const {
|
||||
return dcd_;
|
||||
}
|
||||
|
||||
void passall(bool enabled)
|
||||
{
|
||||
passall_ = enabled;
|
||||
void passall(bool enabled) {
|
||||
passall_ = enabled;
|
||||
// decoder.passall(enabled);
|
||||
}
|
||||
|
||||
@ -112,12 +112,13 @@ struct M17_API M17Demodulator
|
||||
}
|
||||
|
||||
void update_values(uint8_t index);
|
||||
|
||||
void operator()(const float input);
|
||||
|
||||
private:
|
||||
static const std::array<float, 150> rrc_taps;
|
||||
BaseFirFilter<rrc_taps.size()> demod_filter{rrc_taps};
|
||||
collelator_t correlator;
|
||||
int16_t initializing_count_;
|
||||
};
|
||||
|
||||
} // mobilinkd
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace mobilinkd
|
||||
{
|
||||
@ -23,16 +24,13 @@ namespace mobilinkd
|
||||
template <typename C, size_t N>
|
||||
void dump(const std::array<C,N>& data, char header = 'D')
|
||||
{
|
||||
putchar(header);
|
||||
putchar('=');
|
||||
for (auto c : data)
|
||||
{
|
||||
const char hex[] = "0123456789ABCDEF";
|
||||
putchar(hex[uint8_t(c)>>4]);
|
||||
putchar(hex[uint8_t(c)&0xf]);
|
||||
std::cerr << header << " = ";
|
||||
|
||||
for (auto c : data) {
|
||||
std::cerr << std::hex << std::setw(2) << std::setfill('0') << (int) c << " ";
|
||||
}
|
||||
putchar('\r');
|
||||
putchar('\n');
|
||||
|
||||
std::cerr << std::dec << std::endl;
|
||||
}
|
||||
|
||||
struct M17FrameDecoder
|
||||
@ -98,14 +96,13 @@ struct M17FrameDecoder
|
||||
callback_t callback_;
|
||||
|
||||
output_buffer_t output_buffer;
|
||||
depunctured_buffer_t depuncture_buffer;
|
||||
decode_buffer_t decode_buffer;
|
||||
uint16_t frame_number = 0;
|
||||
|
||||
uint8_t lich_segments{0}; ///< one bit per received LICH fragment.
|
||||
|
||||
M17FrameDecoder(callback_t callback)
|
||||
: callback_(callback)
|
||||
M17FrameDecoder(callback_t callback) :
|
||||
callback_(callback)
|
||||
{}
|
||||
|
||||
void update_state(std::array<uint8_t, 240>& lsf_output)
|
||||
@ -149,13 +146,15 @@ struct M17FrameDecoder
|
||||
* @param viterbi_cost
|
||||
* @return
|
||||
*/
|
||||
DecodeResult decode_lsf(input_buffer_t&, int& viterbi_cost)
|
||||
DecodeResult decode_lsf(input_buffer_t& buffer, int& viterbi_cost)
|
||||
{
|
||||
depunctured_buffer_t depuncture_buffer;
|
||||
depuncture(buffer, depuncture_buffer.lsf, P1);
|
||||
viterbi_cost = viterbi_.decode(depuncture_buffer.lsf, decode_buffer.lsf);
|
||||
to_byte_array(decode_buffer.lsf, output_buffer.lsf);
|
||||
|
||||
// std::cerr << "M17FrameDecoder::decode_lsf: vierbi:" << viterbi_cost << std::endl;
|
||||
// dump(output_buffer.lsf);
|
||||
// printf("cost = %lu\n", viterbi_cost);
|
||||
|
||||
crc_.reset();
|
||||
for (auto c : output_buffer.lsf) crc_(c);
|
||||
@ -168,6 +167,11 @@ struct M17FrameDecoder
|
||||
callback_(output_buffer, viterbi_cost);
|
||||
return DecodeResult::OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "M17FrameDecoder::decode_lsf: bad CRC:" << std::endl;
|
||||
dump(output_buffer.lsf);
|
||||
}
|
||||
|
||||
lich_segments = 0;
|
||||
output_buffer.lsf.fill(0);
|
||||
@ -260,6 +264,7 @@ struct M17FrameDecoder
|
||||
|
||||
DecodeResult decode_bert(input_buffer_t&, int& viterbi_cost)
|
||||
{
|
||||
depunctured_buffer_t depuncture_buffer;
|
||||
viterbi_cost = viterbi_.decode(depuncture_buffer.bert, decode_buffer.bert);
|
||||
to_byte_array(decode_buffer.bert, output_buffer.bert);
|
||||
|
||||
@ -273,6 +278,7 @@ struct M17FrameDecoder
|
||||
{
|
||||
std::array<int8_t, 272> tmp;
|
||||
std::copy(buffer.begin() + 96, buffer.end(), tmp.begin());
|
||||
depunctured_buffer_t depuncture_buffer;
|
||||
|
||||
depuncture(tmp, depuncture_buffer.stream, P2);
|
||||
viterbi_cost = viterbi_.decode(depuncture_buffer.stream, decode_buffer.stream);
|
||||
@ -298,8 +304,10 @@ struct M17FrameDecoder
|
||||
* @param frame_type is either BASIC_PACKET or FULL_PACKET.
|
||||
* @return the result of decoding the packet frame.
|
||||
*/
|
||||
DecodeResult decode_packet(input_buffer_t&, int& viterbi_cost, FrameType type)
|
||||
DecodeResult decode_packet(input_buffer_t& buffer, int& viterbi_cost, FrameType type)
|
||||
{
|
||||
depunctured_buffer_t depuncture_buffer;
|
||||
depuncture(buffer, depuncture_buffer.packet, P3);
|
||||
viterbi_cost = viterbi_.decode(depuncture_buffer.packet, decode_buffer.packet);
|
||||
to_byte_array(decode_buffer.packet, output_buffer.packet);
|
||||
|
||||
|
112
modems/m17/M17Modulator.cpp
Normal file
112
modems/m17/M17Modulator.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include "M17Modulator.h"
|
||||
|
||||
namespace mobilinkd
|
||||
{
|
||||
|
||||
/*
|
||||
sync_word_t preamble_sync{{+3, -3, +3, -3, +3, -3, +3, -3}, 29.f};
|
||||
sync_word_t lsf_sync{ {+3, +3, +3, +3, -3, -3, +3, -3}, 32.f, -31.f}; // LSF or STREAM (inverted)
|
||||
sync_word_t packet_sync{ {+3, -3, +3, +3, -3, -3, -3, -3}, 31.f, -31.f}; // PACKET or BERT (inverted)
|
||||
*/
|
||||
|
||||
const std::array<uint8_t, 2> M17Modulator::SYNC_WORD = {0x32, 0x43}; // 00 11 00 10 01 00 00 11 | +1 -3 +1 -1 +3 +1 +1 -3 ????
|
||||
const std::array<uint8_t, 2> M17Modulator::LSF_SYNC_WORD = {0x55, 0xF7}; // 01 01 01 01 11 11 01 11 | +3 +3 +3 +3 -3 -3 +3 -3
|
||||
const std::array<uint8_t, 2> M17Modulator::STREAM_SYNC_WORD = {0xFF, 0x5D}; // 11 11 11 11 01 01 11 01 | -3 -3 -3 -3 +3 +3 -3 +3
|
||||
const std::array<uint8_t, 2> M17Modulator::PACKET_SYNC_WORD = {0x75, 0xFF}; // 01 11 01 01 11 11 11 11 | +3 -3 +3 +3 -3 -3 -3 -3
|
||||
const std::array<uint8_t, 2> M17Modulator::BERT_SYNC_WORD = {0xDF, 0x55}; // 11 01 11 11 01 01 01 01 | -3 +3 -3 -3 +3 +3 +3 +3
|
||||
const std::array<uint8_t, 2> M17Modulator::EOT_SYNC = {0x55, 0x5D}; // ????
|
||||
|
||||
// Generated using scikit-commpy
|
||||
// const std::array<float, 150> M17Modulator::rrc_taps = std::array<float, 150>{
|
||||
// 0.0029364388513841593, 0.0031468394550958484, 0.002699564567597445, 0.001661182944400927, 0.00023319405581230247, // 5
|
||||
// -0.0012851320781224025, -0.0025577136087664687, -0.0032843366522956313, -0.0032697038088887226, -0.0024733964729590865, // 10
|
||||
// -0.0010285696910973807, 0.0007766690889758685, 0.002553421969211845, 0.0038920145144327816, 0.004451886520053017, // 15
|
||||
// 0.00404219185231544, 0.002674727068399207, 0.0005756567993179152, -0.0018493784971116507, -0.004092346891623224, // 20
|
||||
// -0.005648131453822014, -0.006126925416243605, -0.005349511529163396, -0.003403189203405097, -0.0006430502751187517, // 25
|
||||
// 0.002365929161655135, 0.004957956568090113, 0.006506845894531803, 0.006569574194782443, 0.0050017573119839134, // 30
|
||||
// 0.002017321931508163, -0.0018256054303579805, -0.00571615173291049, -0.008746639552588416, -0.010105075751866371, // 35
|
||||
// -0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491, 0.01071805138282269, // 40
|
||||
// 0.01505751553351295, 0.01679337935001369, 0.015256245142156299, 0.01042830577908502, 0.003031522725559901, // 45
|
||||
// -0.0055333532968188165, -0.013403099825723372, -0.018598682349642525, -0.01944761739590459, -0.015005271935951746, // 50
|
||||
// -0.0053887880354343935, 0.008056525910253532, 0.022816244158307273, 0.035513467692208076, 0.04244131815783876, // 55
|
||||
// 0.04025481153629372, 0.02671818654865632, 0.0013810216516704976, -0.03394615682795165, -0.07502635967975885, // 60
|
||||
// -0.11540977897637611, -0.14703962203941534, -0.16119995609538576, -0.14969512896336504, -0.10610329539459686, // 65
|
||||
// -0.026921412469634916, 0.08757875030779196, 0.23293327870303457, 0.4006012210123992, 0.5786324696325503, // 70
|
||||
// 0.7528286479934068, 0.908262741447522, 1.0309661131633199, 1.1095611856548013, 1.1366197723675815, // 75
|
||||
// 1.1095611856548013, 1.0309661131633199, 0.908262741447522, 0.7528286479934068, 0.5786324696325503, // 80
|
||||
// 0.4006012210123992, 0.23293327870303457, 0.08757875030779196, -0.026921412469634916, -0.10610329539459686, // 85
|
||||
// -0.14969512896336504, -0.16119995609538576, -0.14703962203941534, -0.11540977897637611, -0.07502635967975885, // 90
|
||||
// -0.03394615682795165, 0.0013810216516704976, 0.02671818654865632, 0.04025481153629372, 0.04244131815783876, // 95
|
||||
// 0.035513467692208076, 0.022816244158307273, 0.008056525910253532, -0.0053887880354343935, -0.015005271935951746, // 100
|
||||
// -0.01944761739590459, -0.018598682349642525, -0.013403099825723372, -0.0055333532968188165, 0.003031522725559901, // 105
|
||||
// 0.01042830577908502, 0.015256245142156299, 0.01679337935001369, 0.01505751553351295, 0.01071805138282269, // 110
|
||||
// 0.004891777252042491, -0.001125978562075172, -0.006136551625729697, -0.009265784007800534, -0.010105075751866371, // 115
|
||||
// -0.008746639552588416, -0.00571615173291049, -0.0018256054303579805, 0.002017321931508163, 0.0050017573119839134, // 120
|
||||
// 0.006569574194782443, 0.006506845894531803, 0.004957956568090113, 0.002365929161655135, -0.0006430502751187517, // 125
|
||||
// -0.003403189203405097, -0.005349511529163396, -0.006126925416243605, -0.005648131453822014, -0.004092346891623224, // 130
|
||||
// -0.0018493784971116507, 0.0005756567993179152, 0.002674727068399207, 0.00404219185231544, 0.004451886520053017, // 135
|
||||
// 0.0038920145144327816, 0.002553421969211845, 0.0007766690889758685, -0.0010285696910973807, -0.0024733964729590865, // 140
|
||||
// -0.0032697038088887226, -0.0032843366522956313, -0.0025577136087664687, -0.0012851320781224025, 0.00023319405581230247, // 145
|
||||
// 0.001661182944400927, 0.002699564567597445, 0.0031468394550958484, 0.0029364388513841593, 0.0 // 150
|
||||
// };
|
||||
|
||||
// Generated using scikit-commpy N = 150, aplha = 0.5, Ts = 1/4800 s, Fs = 48000 Hz
|
||||
/*
|
||||
import sys
|
||||
import commpy.filters
|
||||
|
||||
N = 150
|
||||
alpha=float(sys.argv[1])
|
||||
tsymbol = 1/4800
|
||||
fs = 48000
|
||||
|
||||
print(f'N={N} alpha={alpha} tsym={tsymbol} fs={fs}')
|
||||
|
||||
t, h = commpy.filters.rrcosfilter(N, alpha, tsymbol, fs)
|
||||
line = " "
|
||||
|
||||
print(len(h))
|
||||
|
||||
for i, coeff in enumerate(h):
|
||||
line += f'{coeff:+e}, '
|
||||
if (i+1) % 5 == 0:
|
||||
print(line)
|
||||
line = " "
|
||||
|
||||
print("")
|
||||
*/
|
||||
const std::array<float, 150> M17Modulator::rrc_taps = std::array<float, 150>{
|
||||
-8.434122e-04, +3.898184e-04, +1.628891e-03, +2.576674e-03, +2.987740e-03,
|
||||
+2.729505e-03, +1.820181e-03, +4.333001e-04, -1.134215e-03, -2.525029e-03,
|
||||
-3.402452e-03, -3.531573e-03, -2.841363e-03, -1.451929e-03, +3.417005e-04,
|
||||
+2.128236e-03, +3.470212e-03, +4.006361e-03, +3.543024e-03, +2.112321e-03,
|
||||
-1.893023e-05, -2.395144e-03, -4.465932e-03, -5.709548e-03, -5.759027e-03,
|
||||
-4.501582e-03, -2.125844e-03, +8.982825e-04, +3.907892e-03, +6.188389e-03,
|
||||
+7.139194e-03, +6.427125e-03, +4.090469e-03, +5.649447e-04, -3.381677e-03,
|
||||
-6.799652e-03, -8.765190e-03, -8.603529e-03, -6.076811e-03, -1.489520e-03,
|
||||
+4.321674e-03, +1.012385e-02, +1.453219e-02, +1.631886e-02, +1.472302e-02,
|
||||
+9.691259e-03, +1.984723e-03, -6.888307e-03, -1.492227e-02, -2.001531e-02,
|
||||
-2.045303e-02, -1.538011e-02, -5.154591e-03, +8.509368e-03, +2.267330e-02,
|
||||
+3.359618e-02, +3.740502e-02, +3.091849e-02, +1.248579e-02, -1.731807e-02,
|
||||
-5.529141e-02, -9.561492e-02, -1.303248e-01, -1.502279e-01, -1.461577e-01,
|
||||
-1.104008e-01, -3.808012e-02, +7.173159e-02, +2.153664e-01, +3.845237e-01,
|
||||
+5.668902e-01, +7.473693e-01, +9.097718e-01, +1.038746e+00, +1.121677e+00,
|
||||
+1.150282e+00, +1.121677e+00, +1.038746e+00, +9.097718e-01, +7.473693e-01,
|
||||
+5.668902e-01, +3.845237e-01, +2.153664e-01, +7.173159e-02, -3.808012e-02,
|
||||
-1.104008e-01, -1.461577e-01, -1.502279e-01, -1.303248e-01, -9.561492e-02,
|
||||
-5.529141e-02, -1.731807e-02, +1.248579e-02, +3.091849e-02, +3.740502e-02,
|
||||
+3.359618e-02, +2.267330e-02, +8.509368e-03, -5.154591e-03, -1.538011e-02,
|
||||
-2.045303e-02, -2.001531e-02, -1.492227e-02, -6.888307e-03, +1.984723e-03,
|
||||
+9.691259e-03, +1.472302e-02, +1.631886e-02, +1.453219e-02, +1.012385e-02,
|
||||
+4.321674e-03, -1.489520e-03, -6.076811e-03, -8.603529e-03, -8.765190e-03,
|
||||
-6.799652e-03, -3.381677e-03, +5.649447e-04, +4.090469e-03, +6.427125e-03,
|
||||
+7.139194e-03, +6.188389e-03, +3.907892e-03, +8.982825e-04, -2.125844e-03,
|
||||
-4.501582e-03, -5.759027e-03, -5.709548e-03, -4.465932e-03, -2.395144e-03,
|
||||
-1.893023e-05, +2.112321e-03, +3.543024e-03, +4.006361e-03, +3.470212e-03,
|
||||
+2.128236e-03, +3.417005e-04, -1.451929e-03, -2.841363e-03, -3.531573e-03,
|
||||
-3.402452e-03, -2.525029e-03, -1.134215e-03, +4.333001e-04, +1.820181e-03,
|
||||
+2.729505e-03, +2.987740e-03, +2.576674e-03, +1.628891e-03, +3.898184e-04,
|
||||
};
|
||||
|
||||
} // namespace mobilinkd
|
||||
|
@ -41,9 +41,12 @@ public:
|
||||
using frame_t = std::array<uint8_t, 46>; // M17 frame (without sync word).
|
||||
using packet_t = std::array<uint8_t, 25>; // Packet payload
|
||||
|
||||
static constexpr std::array<uint8_t, 2> SYNC_WORD = {0x32, 0x43};
|
||||
static constexpr std::array<uint8_t, 2> LSF_SYNC_WORD = {0x55, 0xF7};
|
||||
static constexpr std::array<uint8_t, 2> DATA_SYNC_WORD = {0xFF, 0x5D};
|
||||
static const std::array<uint8_t, 2> SYNC_WORD;
|
||||
static const std::array<uint8_t, 2> LSF_SYNC_WORD;
|
||||
static const std::array<uint8_t, 2> STREAM_SYNC_WORD;
|
||||
static const std::array<uint8_t, 2> PACKET_SYNC_WORD;
|
||||
static const std::array<uint8_t, 2> BERT_SYNC_WORD;
|
||||
static const std::array<uint8_t, 2> EOT_SYNC;
|
||||
|
||||
static constexpr int8_t bits_to_symbol(uint8_t bits)
|
||||
{
|
||||
@ -58,12 +61,23 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
static std::array<int8_t, N / 2> bits_to_symbols(const std::array<T, N>& bits)
|
||||
{
|
||||
std::array<int8_t, N / 2> result;
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i != N; i += 2)
|
||||
{
|
||||
result[index++] = bits_to_symbol((bits[i] << 1) | bits[i + 1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
static std::array<int8_t, N * 4> bytes_to_symbols(const std::array<T, N>& bytes)
|
||||
{
|
||||
std::array<int8_t, N * 4> result;
|
||||
size_t index = 0;
|
||||
|
||||
for (auto b : bytes)
|
||||
{
|
||||
for (size_t i = 0; i != 4; ++i)
|
||||
@ -72,160 +86,116 @@ public:
|
||||
b <<= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
void make_preamble(std::array<uint8_t, 48>& preamble_bytes)
|
||||
/*
|
||||
* Converts a suite of 192 symbols (from the 384 bits of a frame) into 1920 16 bit integer samples to be used
|
||||
* in the final FM modulator (baseband). Sample rate is expected to be 48 kS/s. This is the original 48 kS/s
|
||||
* 16 bit audio output of the modulator.
|
||||
*/
|
||||
template <size_t N>
|
||||
std::array<int16_t, N*10> symbols_to_baseband(std::array<int8_t, N> symbols, bool invert = false)
|
||||
{
|
||||
// Preamble is simple... bytes -> symbols.
|
||||
preamble_bytes.fill(0x77);
|
||||
std::array<int16_t, N*10> baseband;
|
||||
baseband.fill(0);
|
||||
|
||||
for (size_t i = 0; i != symbols.size(); ++i) {
|
||||
baseband[i * 10] = symbols[i];
|
||||
}
|
||||
|
||||
for (auto& b : baseband) {
|
||||
b = rrc(b) * 7168.0 * (invert ? -1.0 : 1.0);
|
||||
}
|
||||
|
||||
return baseband;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode each LSF segment into a Golay-encoded LICH segment bitstream.
|
||||
*/
|
||||
static
|
||||
lich_segment_t make_lich_segment(std::array<uint8_t, 5> segment, uint8_t segment_number)
|
||||
static std::array<int8_t, 368> make_lsf(lsf_t& lsf, const std::string& src, const std::string& dest, int8_t can = 10, bool streamElsePacket = false)
|
||||
{
|
||||
lich_segment_t result;
|
||||
uint16_t tmp;
|
||||
uint32_t encoded;
|
||||
|
||||
tmp = segment[0] << 4 | ((segment[1] >> 4) & 0x0F);
|
||||
encoded = mobilinkd::Golay24::encode24(tmp);
|
||||
|
||||
for (size_t i = 0; i != 24; ++i)
|
||||
{
|
||||
assign_bit_index(result, i, (encoded & (1 << 23)) != 0);
|
||||
encoded <<= 1;
|
||||
}
|
||||
|
||||
tmp = ((segment[1] & 0x0F) << 8) | segment[2];
|
||||
encoded = mobilinkd::Golay24::encode24(tmp);
|
||||
|
||||
for (size_t i = 24; i != 48; ++i)
|
||||
{
|
||||
assign_bit_index(result, i, (encoded & (1 << 23)) != 0);
|
||||
encoded <<= 1;
|
||||
}
|
||||
|
||||
tmp = segment[3] << 4 | ((segment[4] >> 4) & 0x0F);
|
||||
encoded = mobilinkd::Golay24::encode24(tmp);
|
||||
|
||||
for (size_t i = 48; i != 72; ++i)
|
||||
{
|
||||
assign_bit_index(result, i, (encoded & (1 << 23)) != 0);
|
||||
encoded <<= 1;
|
||||
}
|
||||
|
||||
tmp = ((segment[4] & 0x0F) << 8) | (segment_number << 5);
|
||||
encoded = mobilinkd::Golay24::encode24(tmp);
|
||||
|
||||
for (size_t i = 72; i != 96; ++i)
|
||||
{
|
||||
assign_bit_index(result, i, (encoded & (1 << 23)) != 0);
|
||||
encoded <<= 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the link setup frame and split into LICH segments.
|
||||
*/
|
||||
void make_link_setup(lich_t& lich, mobilinkd::M17Modulator::frame_t lsf_frame)
|
||||
{
|
||||
using namespace mobilinkd;
|
||||
|
||||
lsf_t lsf;
|
||||
lsf.fill(0);
|
||||
|
||||
auto rit = std::copy(source_.begin(), source_.end(), lsf.begin());
|
||||
std::copy(dest_.begin(), dest_.end(), rit);
|
||||
lsf[12] = 0;
|
||||
lsf[13] = 5;
|
||||
M17Randomizer<368> randomizer;
|
||||
PolynomialInterleaver<45, 92, 368> interleaver;
|
||||
CRC16<0x5935, 0xFFFF> crc;
|
||||
|
||||
crc_.reset();
|
||||
mobilinkd::LinkSetupFrame::call_t callsign;
|
||||
callsign.fill(0);
|
||||
std::copy(src.begin(), src.end(), callsign.begin());
|
||||
auto encoded_src = mobilinkd::LinkSetupFrame::encode_callsign(callsign);
|
||||
|
||||
for (size_t i = 0; i != 28; ++i) {
|
||||
crc_(lsf[i]);
|
||||
mobilinkd::LinkSetupFrame::encoded_call_t encoded_dest = {0xff,0xff,0xff,0xff,0xff,0xff};
|
||||
|
||||
if (!dest.empty())
|
||||
{
|
||||
callsign.fill(0);
|
||||
std::copy(dest.begin(), dest.end(), callsign.begin());
|
||||
encoded_dest = mobilinkd::LinkSetupFrame::encode_callsign(callsign);
|
||||
}
|
||||
|
||||
auto checksum = crc_.get_bytes();
|
||||
auto rit = std::copy(encoded_dest.begin(), encoded_dest.end(), lsf.begin());
|
||||
std::copy(encoded_src.begin(), encoded_src.end(), rit);
|
||||
|
||||
lsf[12] = can >> 1;
|
||||
lsf[13] = (streamElsePacket ? 5 : 4) | ((can & 1) << 7);
|
||||
|
||||
crc.reset();
|
||||
|
||||
for (size_t i = 0; i != 28; ++i) {
|
||||
crc(lsf[i]);
|
||||
}
|
||||
|
||||
std::array<uint8_t, 2> checksum = crc.get_bytes();
|
||||
lsf[28] = checksum[0];
|
||||
lsf[29] = checksum[1];
|
||||
|
||||
// Build LICH segments
|
||||
for (size_t i = 0; i != lich.size(); ++i)
|
||||
std::array<uint8_t, 488> encoded;
|
||||
size_t index = 0;
|
||||
uint32_t memory = 0;
|
||||
|
||||
for (auto b : lsf)
|
||||
{
|
||||
std::array<uint8_t, 5> segment;
|
||||
std::copy(lsf.begin() + i * 5, lsf.begin() + (i + 1) * 5, segment.begin());
|
||||
auto lich_segment = make_lich_segment(segment, i);
|
||||
std::copy(lich_segment.begin(), lich_segment.end(), lich[i].begin());
|
||||
for (size_t i = 0; i != 8; ++i)
|
||||
{
|
||||
uint32_t x = (b & 0x80) >> 7;
|
||||
b <<= 1;
|
||||
memory = mobilinkd::update_memory<4>(memory, x);
|
||||
encoded[index++] = mobilinkd::convolve_bit(031, memory);
|
||||
encoded[index++] = mobilinkd::convolve_bit(027, memory);
|
||||
}
|
||||
}
|
||||
|
||||
auto encoded = conv_encode(lsf);
|
||||
auto size = puncture_bytes(encoded, lsf_frame, P1);
|
||||
// Flush the encoder.
|
||||
for (size_t i = 0; i != 4; ++i)
|
||||
{
|
||||
memory = mobilinkd::update_memory<4>(memory, 0);
|
||||
encoded[index++] = mobilinkd::convolve_bit(031, memory);
|
||||
encoded[index++] = mobilinkd::convolve_bit(027, memory);
|
||||
}
|
||||
|
||||
std::array<int8_t, 368> punctured;
|
||||
auto size = puncture(encoded, punctured, P1);
|
||||
|
||||
if (size != 368) {
|
||||
std::cerr << "make_link_setup: incorrect size (not 368)" << size;
|
||||
std::cerr << "mobilinkd::M17Modulator::make_lsf: incorrect size (not 368)" << size;
|
||||
}
|
||||
|
||||
interleaver_.interleave(lsf_frame);
|
||||
randomizer_(lsf_frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the LICH and Convolutionally encoded payload, interleave and randomize
|
||||
* the frame bits, and produce the frame.
|
||||
*/
|
||||
void make_audio_frame(const lich_segment_t& lich, const payload_t& data, mobilinkd::M17Modulator::frame_t& frame)
|
||||
{
|
||||
using namespace mobilinkd;
|
||||
|
||||
auto it = std::copy(lich.begin(), lich.end(), frame.begin());
|
||||
std::copy(data.begin(), data.end(), it);
|
||||
|
||||
interleaver_.interleave(frame);
|
||||
randomizer_(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble the audio frame payload by appending the frame number, encoded audio,
|
||||
* and CRC, then convolutionally coding and puncturing the data.
|
||||
*/
|
||||
payload_t make_audio_payload(uint16_t frame_number, const codec_frame_t& payload)
|
||||
{
|
||||
std::array<uint8_t, 20> data; // FN, Audio, CRC = 2 + 16 + 2;
|
||||
data[0] = uint8_t((frame_number >> 8) & 0xFF);
|
||||
data[1] = uint8_t(frame_number & 0xFF);
|
||||
std::copy(payload.begin(), payload.end(), data.begin() + 2);
|
||||
crc_.reset();
|
||||
|
||||
for (size_t i = 0; i != 18; ++i) {
|
||||
crc_(data[i]);
|
||||
}
|
||||
|
||||
auto checksum = crc_.get_bytes();
|
||||
data[18] = checksum[0];
|
||||
data[19] = checksum[1];
|
||||
|
||||
auto encoded = conv_encode(data);
|
||||
|
||||
payload_t punctured;
|
||||
auto size = puncture_bytes(encoded, punctured, mobilinkd::P2);
|
||||
|
||||
if (size != 272) {
|
||||
std::cerr << "mobilinkd::M17Modulator::make_audio_payload: incorrect size (not 272)" << size;
|
||||
}
|
||||
interleaver.interleave(punctured);
|
||||
randomizer.randomize(punctured);
|
||||
|
||||
return punctured;
|
||||
}
|
||||
|
||||
frame_t make_packet_frame(uint8_t packet_number, bool last_packet, packet_t packet, int packet_size)
|
||||
std::array<int8_t, 368> make_packet_frame(
|
||||
uint8_t packet_number,
|
||||
int packet_size,
|
||||
bool last_packet,
|
||||
const std::array<uint8_t, 25> packet
|
||||
)
|
||||
{
|
||||
M17Randomizer<368> randomizer;
|
||||
PolynomialInterleaver<45, 92, 368> interleaver;
|
||||
|
||||
std::array<uint8_t, 26> packet_assembly;
|
||||
packet_assembly.fill(0);
|
||||
std::copy(packet.begin(), packet.begin() + packet_size, packet_assembly.begin());
|
||||
@ -240,75 +210,71 @@ public:
|
||||
|
||||
if (last_packet)
|
||||
{
|
||||
packet_assembly[25] = 0x80 | (packet_size<<2);
|
||||
packet_assembly[25] = 0x80 | ((packet_size+2)<<2); // sent packet size includes CRC
|
||||
packet_assembly[packet_size] = crc_.get_bytes()[1];
|
||||
packet_assembly[packet_size+1] = crc_.get_bytes()[0];
|
||||
std::cerr << "M17Modulator::make_packet_frame:" << std::hex << (int) crc_.get_bytes()[1] << ":" << (int) crc_.get_bytes()[0] << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet_assembly[25] = (packet_number<<2);
|
||||
}
|
||||
|
||||
std::array<uint8_t, 2*26+1> encoded = conv_encode(packet_assembly);
|
||||
frame_t punctured;
|
||||
auto size = puncture_bytes(encoded, punctured, mobilinkd::P3);
|
||||
std::array<uint8_t, 420> encoded;
|
||||
size_t index = 0;
|
||||
uint32_t memory = 0;
|
||||
uint8_t b;
|
||||
|
||||
for (int bi = 0; bi < 25; bi++)
|
||||
{
|
||||
b = packet_assembly[bi];
|
||||
|
||||
for (size_t i = 0; i != 8; ++i)
|
||||
{
|
||||
uint32_t x = (b & 0x80) >> 7;
|
||||
b <<= 1;
|
||||
memory = mobilinkd::update_memory<4>(memory, x);
|
||||
encoded[index++] = mobilinkd::convolve_bit(031, memory);
|
||||
encoded[index++] = mobilinkd::convolve_bit(027, memory);
|
||||
}
|
||||
}
|
||||
|
||||
b = packet_assembly[25];
|
||||
|
||||
for (size_t i = 0; i != 6; ++i)
|
||||
{
|
||||
uint32_t x = (b & 0x80) >> 7;
|
||||
b <<= 1;
|
||||
memory = mobilinkd::update_memory<4>(memory, x);
|
||||
encoded[index++] = mobilinkd::convolve_bit(031, memory);
|
||||
encoded[index++] = mobilinkd::convolve_bit(027, memory);
|
||||
}
|
||||
|
||||
// Flush the encoder.
|
||||
for (size_t i = 0; i != 4; ++i)
|
||||
{
|
||||
memory = mobilinkd::update_memory<4>(memory, 0);
|
||||
encoded[index++] = mobilinkd::convolve_bit(031, memory);
|
||||
encoded[index++] = mobilinkd::convolve_bit(027, memory);
|
||||
}
|
||||
|
||||
std::array<int8_t, 368> punctured;
|
||||
auto size = puncture(encoded, punctured, P3);
|
||||
|
||||
if (size != 368) {
|
||||
std::cerr << "mobilinkd::M17Modulator::make_packet_frame: incorrect size (not 368)" << size;
|
||||
}
|
||||
|
||||
interleaver.interleave(punctured);
|
||||
randomizer.randomize(punctured);
|
||||
|
||||
return punctured;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a suite of 192 symbols (from the 384 bits of a frame) into 1920 16 bit integer samples to be used
|
||||
* in the final FM modulator (baseband). Sample rate is expected to be 48 kS/s. This is the original 48 kS/s
|
||||
* 16 bit audio output of the modulator.
|
||||
*/
|
||||
static baseband_t symbols_to_baseband(const symbols_t& symbols)
|
||||
{
|
||||
// Generated using scikit-commpy
|
||||
static const auto rrc_taps = std::array<float, 79>{
|
||||
-0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491,
|
||||
0.01071805138282269, 0.01505751553351295, 0.01679337935001369, 0.015256245142156299,
|
||||
0.01042830577908502, 0.003031522725559901, -0.0055333532968188165, -0.013403099825723372,
|
||||
-0.018598682349642525, -0.01944761739590459, -0.015005271935951746, -0.0053887880354343935,
|
||||
0.008056525910253532, 0.022816244158307273, 0.035513467692208076, 0.04244131815783876,
|
||||
0.04025481153629372, 0.02671818654865632, 0.0013810216516704976, -0.03394615682795165,
|
||||
-0.07502635967975885, -0.11540977897637611, -0.14703962203941534, -0.16119995609538576,
|
||||
-0.14969512896336504, -0.10610329539459686, -0.026921412469634916, 0.08757875030779196,
|
||||
0.23293327870303457, 0.4006012210123992, 0.5786324696325503, 0.7528286479934068,
|
||||
0.908262741447522, 1.0309661131633199, 1.1095611856548013, 1.1366197723675815,
|
||||
1.1095611856548013, 1.0309661131633199, 0.908262741447522, 0.7528286479934068,
|
||||
0.5786324696325503, 0.4006012210123992, 0.23293327870303457, 0.08757875030779196,
|
||||
-0.026921412469634916, -0.10610329539459686, -0.14969512896336504, -0.16119995609538576,
|
||||
-0.14703962203941534, -0.11540977897637611, -0.07502635967975885, -0.03394615682795165,
|
||||
0.0013810216516704976, 0.02671818654865632, 0.04025481153629372, 0.04244131815783876,
|
||||
0.035513467692208076, 0.022816244158307273, 0.008056525910253532, -0.0053887880354343935,
|
||||
-0.015005271935951746, -0.01944761739590459, -0.018598682349642525, -0.013403099825723372,
|
||||
-0.0055333532968188165, 0.003031522725559901, 0.01042830577908502, 0.015256245142156299,
|
||||
0.01679337935001369, 0.01505751553351295, 0.01071805138282269, 0.004891777252042491,
|
||||
-0.001125978562075172, -0.006136551625729697, -0.009265784007800534
|
||||
};
|
||||
static BaseFirFilter<std::tuple_size<decltype(rrc_taps)>::value> rrc = makeFirFilter(rrc_taps);
|
||||
|
||||
std::array<int16_t, 1920> baseband;
|
||||
baseband.fill(0);
|
||||
|
||||
for (size_t i = 0; i != symbols.size(); ++i) {
|
||||
baseband[i * 10] = symbols[i];
|
||||
}
|
||||
|
||||
for (auto& b : baseband) {
|
||||
b = rrc(b) * 25;
|
||||
}
|
||||
|
||||
return baseband;
|
||||
}
|
||||
|
||||
M17Modulator(const std::string& source, const std::string& dest = "") :
|
||||
source_(encode_callsign(source)),
|
||||
dest_(encode_callsign(dest))
|
||||
dest_(encode_callsign(dest)),
|
||||
rrc(makeFirFilter(rrc_taps))
|
||||
{ }
|
||||
|
||||
/**
|
||||
@ -327,11 +293,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
M17ByteRandomizer<46> randomizer_;
|
||||
PolynomialInterleaver<45, 92, 368> interleaver_;
|
||||
CRC16<0x5935, 0xFFFF> crc_;
|
||||
LinkSetupFrame::encoded_call_t source_;
|
||||
LinkSetupFrame::encoded_call_t dest_;
|
||||
BaseFirFilter<150> rrc;
|
||||
static const std::array<float, 150> rrc_taps;
|
||||
CRC16<0x5935, 0xFFFF> crc_;
|
||||
|
||||
static LinkSetupFrame::encoded_call_t encode_callsign(std::string callsign)
|
||||
{
|
||||
|
@ -567,7 +567,7 @@
|
||||
<widget class="QLabel" name="sourceText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<width>90</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
@ -602,7 +602,7 @@
|
||||
<widget class="QLabel" name="destText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<width>90</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
@ -637,7 +637,7 @@
|
||||
<widget class="QLabel" name="typeText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
|
@ -15,9 +15,9 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <boost/crc.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
// #include <boost/crc.hpp>
|
||||
// #include <boost/program_options.hpp>
|
||||
// #include <boost/optional.hpp>
|
||||
#include <codec2/codec2.h>
|
||||
|
||||
#include <QDebug>
|
||||
@ -278,7 +278,6 @@ void M17DemodProcessor::resetInfo()
|
||||
|
||||
void M17DemodProcessor::setDCDOff()
|
||||
{
|
||||
qDebug("M17DemodProcessor::setDCDOff");
|
||||
m_demod.dcd_off();
|
||||
}
|
||||
|
||||
@ -301,25 +300,36 @@ void M17DemodProcessor::append_packet(std::vector<uint8_t>& result, mobilinkd::M
|
||||
|
||||
bool M17DemodProcessor::decode_packet(mobilinkd::M17FrameDecoder::packet_buffer_t const& packet_segment)
|
||||
{
|
||||
// qDebug() << tr("M17DemodProcessor::decode_packet: 0x%1").arg((int) packet_segment[25], 2, 16, QChar('0'));
|
||||
if (packet_segment[25] & 0x80) // last frame of packet.
|
||||
{
|
||||
size_t packet_size = (packet_segment[25] & 0x7F) >> 2;
|
||||
packet_size = std::min(packet_size, size_t(25)); // on last frame this is the remainder byte count
|
||||
m_packetFrameCounter = 0;
|
||||
|
||||
for (size_t i = 0; i != packet_size; ++i) {
|
||||
for (size_t i = 0; i < packet_size; ++i) {
|
||||
m_currentPacket.push_back(packet_segment[i]);
|
||||
}
|
||||
|
||||
if (m_currentPacket.size() < 3) {
|
||||
if (m_currentPacket.size() < 3)
|
||||
{
|
||||
qDebug() << "M17DemodProcessor::decode_packet: too small:" << m_currentPacket.size();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "M17DemodProcessor::decode_packet: last chunk size:" << packet_size << " packet size:" << m_currentPacket.size();
|
||||
}
|
||||
|
||||
boost::crc_optimal<16, 0x1021, 0xFFFF, 0xFFFF, true, true> crc;
|
||||
crc.process_bytes(&m_currentPacket.front(), m_currentPacket.size());
|
||||
uint16_t calcChecksum = crc.checksum();
|
||||
uint16_t xmitChecksum = m_currentPacket.back() + (1<<8) * m_currentPacket.end()[-2];
|
||||
mobilinkd::CRC16<0x5935, 0xFFFF> crc16;
|
||||
crc16.reset();
|
||||
|
||||
for (std::vector<uint8_t>::const_iterator it = m_currentPacket.begin(); it != m_currentPacket.end() - 2; ++it) {
|
||||
crc16(*it);
|
||||
}
|
||||
|
||||
uint16_t calcChecksum = crc16.get_bytes()[0] + (crc16.get_bytes()[1]<<8);
|
||||
uint16_t xmitChecksum = m_currentPacket.back() + (m_currentPacket.end()[-2]<<8);
|
||||
|
||||
if (calcChecksum == xmitChecksum) // (checksum == 0x0f47)
|
||||
{
|
||||
@ -355,7 +365,9 @@ bool M17DemodProcessor::decode_packet(mobilinkd::M17FrameDecoder::packet_buffer_
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning() << "M17DemodProcessor::decode_packet: Packet checksum error: " << std::hex << calcChecksum << "vs" << xmitChecksum << std::dec;
|
||||
QString ccrc = tr("0x%1").arg(calcChecksum, 4, 16, QChar('0'));
|
||||
QString xcrc = tr("0x%1").arg(xmitChecksum, 4, 16, QChar('0'));
|
||||
qWarning() << "M17DemodProcessor::decode_packet: Packet checksum error: " << ccrc << "vs" << xcrc;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -368,12 +380,16 @@ bool M17DemodProcessor::decode_packet(mobilinkd::M17FrameDecoder::packet_buffer_
|
||||
return false;
|
||||
}
|
||||
|
||||
m_packetFrameCounter++;
|
||||
if (m_packetFrameCounter == 0) {
|
||||
m_currentPacket.clear();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != 25; ++i) {
|
||||
for (size_t i = 0; i < 25; ++i) {
|
||||
m_currentPacket.push_back(packet_segment[i]);
|
||||
}
|
||||
|
||||
m_packetFrameCounter++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -759,6 +759,8 @@ void M17ModGUI::makeUIConnections()
|
||||
QObject::connect(ui->loopPacket, &ButtonSwitch::toggled, this, &M17ModGUI::on_loopPacket_toggled);
|
||||
QObject::connect(ui->loopPacketInterval, &QDial::valueChanged, this, &M17ModGUI::on_loopPacketInterval_valueChanged);
|
||||
QObject::connect(ui->smsText, &CustomTextEdit::editingFinished, this, &M17ModGUI::on_smsText_editingFinished);
|
||||
QObject::connect(ui->source, &QLineEdit::editingFinished, this, &M17ModGUI::on_source_editingFinished);
|
||||
QObject::connect(ui->destination, &QLineEdit::editingFinished, this, &M17ModGUI::on_destination_editingFinished);
|
||||
}
|
||||
|
||||
void M17ModGUI::updateAbsoluteCenterFrequency()
|
||||
@ -775,7 +777,7 @@ M17ModSettings::PacketType M17ModGUI::indexToPacketType(int index)
|
||||
case 1:
|
||||
return M17ModSettings::PacketType::PacketAPRS;
|
||||
default:
|
||||
return M17ModSettings::PacketType::PacketNone;
|
||||
return M17ModSettings::PacketType::PacketSMS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -788,6 +790,6 @@ int M17ModGUI::packetTypeToIndex(M17ModSettings::PacketType type)
|
||||
case M17ModSettings::PacketType::PacketAPRS:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,12 @@
|
||||
|
||||
#include "m17modprocessor.h"
|
||||
|
||||
M17ModProcessor::M17ModProcessor()
|
||||
MESSAGE_CLASS_DEFINITION(M17ModProcessor::MsgSendSMS, Message)
|
||||
|
||||
M17ModProcessor::M17ModProcessor() :
|
||||
m_m17Modulator("MYCALL", "")
|
||||
{
|
||||
m_basebandFifo.setSampleSize(sizeof(int16_t), 48000);
|
||||
m_basebandFifo.setSampleSize(sizeof(int16_t), 96000);
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
}
|
||||
|
||||
@ -37,6 +40,7 @@ bool M17ModProcessor::handleMessage(const Message& cmd)
|
||||
packetBytes.prepend(0x05); // SMS standard type
|
||||
packetBytes.truncate(798); // Maximum packet size is 798 payload + 2 bytes CRC = 800 bytes (32*25)
|
||||
processPacket(notif.getSourceCall(), notif.getDestCall(), packetBytes);
|
||||
// test(notif.getSourceCall(), notif.getDestCall());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -56,44 +60,85 @@ void M17ModProcessor::handleInputMessages()
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModProcessor::test(const QString& sourceCall, const QString& destCall)
|
||||
{
|
||||
m_m17Modulator.source(sourceCall.toStdString());
|
||||
m_m17Modulator.dest(destCall.toStdString());
|
||||
|
||||
for (int i = 0; i < 25; i++) {
|
||||
send_preamble();
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModProcessor::processPacket(const QString& sourceCall, const QString& destCall, const QByteArray& packetBytes)
|
||||
{
|
||||
mobilinkd::M17Modulator modulator(sourceCall.toStdString(), destCall.toStdString());
|
||||
// preamble
|
||||
std::array<uint8_t, 48> preamble_bytes;
|
||||
mobilinkd::M17Modulator::make_preamble(preamble_bytes);
|
||||
std::array<int8_t, 48 * 4> fullframe_symbols = mobilinkd::M17Modulator::bytes_to_symbols(preamble_bytes);
|
||||
std::array<int16_t, 1920> baseband = mobilinkd::M17Modulator::symbols_to_baseband(fullframe_symbols);
|
||||
m_basebandFifo.write((const quint8*) baseband.data(), 1920);
|
||||
qDebug("M17ModProcessor::processPacket: %s to %s: %s", qPrintable(sourceCall), qPrintable(destCall), qPrintable(packetBytes));
|
||||
m_m17Modulator.source(sourceCall.toStdString());
|
||||
m_m17Modulator.dest(destCall.toStdString());
|
||||
|
||||
send_preamble(); // preamble
|
||||
|
||||
// LSF
|
||||
mobilinkd::M17Modulator::lich_t lichSegments; // Not used for packet
|
||||
mobilinkd::M17Modulator::frame_t frame;
|
||||
modulator.make_link_setup(lichSegments, frame);
|
||||
std::array<int8_t, 46 * 4> frame_symbols = mobilinkd::M17Modulator::bytes_to_symbols(frame);
|
||||
std::copy(mobilinkd::M17Modulator::LSF_SYNC_WORD.begin(), mobilinkd::M17Modulator::LSF_SYNC_WORD.end(), fullframe_symbols.begin());
|
||||
std::copy(frame_symbols.begin(), frame_symbols.end(), fullframe_symbols.begin()+2);
|
||||
baseband = mobilinkd::M17Modulator::symbols_to_baseband(fullframe_symbols);
|
||||
m_basebandFifo.write((const quint8*) baseband.data(), 1920);
|
||||
std::array<uint8_t, 30> lsf;
|
||||
std::array<int8_t, 368> lsf_frame = mobilinkd::M17Modulator::make_lsf(lsf, sourceCall.toStdString(), destCall.toStdString());
|
||||
output_baseband(mobilinkd::M17Modulator::LSF_SYNC_WORD, lsf_frame);
|
||||
|
||||
// Packets
|
||||
std::copy(mobilinkd::M17Modulator::DATA_SYNC_WORD.begin(), mobilinkd::M17Modulator::DATA_SYNC_WORD.end(), fullframe_symbols.begin());
|
||||
mobilinkd::M17Modulator::packet_t packet;
|
||||
int remainderCount = packetBytes.size();
|
||||
int packetCount = 0;
|
||||
std::array<int8_t, 368> packet_frame;
|
||||
// std::copy(mobilinkd::M17Modulator::DATA_SYNC_WORD.begin(), mobilinkd::M17Modulator::DATA_SYNC_WORD.end(), fullframe_symbols.begin());
|
||||
mobilinkd::M17Modulator::packet_t packet;
|
||||
|
||||
while (remainderCount > 25)
|
||||
{
|
||||
std::copy(packetBytes.begin() + (packetCount*25), packetBytes.begin() + ((packetCount+1)*25), packet.begin());
|
||||
frame = modulator.make_packet_frame(packetCount, false, packet, 25);
|
||||
std::copy(frame_symbols.begin(), frame_symbols.end(), fullframe_symbols.begin()+2);
|
||||
baseband = mobilinkd::M17Modulator::symbols_to_baseband(fullframe_symbols);
|
||||
m_basebandFifo.write((const quint8*) baseband.data(), 1920);
|
||||
packet_frame = m_m17Modulator.make_packet_frame(packetCount, 25, false, packet);
|
||||
output_baseband(mobilinkd::M17Modulator::PACKET_SYNC_WORD, packet_frame);
|
||||
remainderCount -= 25;
|
||||
packetCount++;
|
||||
}
|
||||
|
||||
std::copy(packetBytes.begin() + (packetCount*25), packetBytes.begin() + (packetCount*25) + remainderCount, packet.begin());
|
||||
frame = modulator.make_packet_frame(packetCount, true, packet, remainderCount);
|
||||
std::copy(frame_symbols.begin(), frame_symbols.end(), fullframe_symbols.begin()+2);
|
||||
baseband = mobilinkd::M17Modulator::symbols_to_baseband(fullframe_symbols);
|
||||
packet_frame = m_m17Modulator.make_packet_frame(packetCount, remainderCount, true, packet);
|
||||
output_baseband(mobilinkd::M17Modulator::PACKET_SYNC_WORD, packet_frame);
|
||||
qDebug("M17ModProcessor::processPacket: last: packetCount: %d remainderCount: %d", packetCount, remainderCount);
|
||||
|
||||
send_eot(); // EOT
|
||||
}
|
||||
|
||||
void M17ModProcessor::send_preamble()
|
||||
{
|
||||
// Preamble is simple... bytes -> symbols -> baseband.
|
||||
std::array<uint8_t, 48> preamble_bytes;
|
||||
preamble_bytes.fill(0x77);
|
||||
std::array<int8_t, 192> preamble_symbols = mobilinkd::M17Modulator::bytes_to_symbols(preamble_bytes);
|
||||
std::array<int16_t, 1920> preamble_baseband = m_m17Modulator.symbols_to_baseband(preamble_symbols);
|
||||
m_basebandFifo.write((const quint8*) preamble_baseband.data(), 1920);
|
||||
}
|
||||
|
||||
void M17ModProcessor::send_eot()
|
||||
{
|
||||
std::array<uint8_t, 2> EOT_SYNC = { 0x55, 0x5D };
|
||||
std::array<uint8_t, 48> eot_bytes;
|
||||
|
||||
for (unsigned int i = 0; i < eot_bytes.size(); i += 2) {
|
||||
std::copy(EOT_SYNC.begin(), EOT_SYNC.end(), eot_bytes.begin() + i);
|
||||
}
|
||||
|
||||
std::array<int8_t, 192> eot_symbols = mobilinkd::M17Modulator::bytes_to_symbols(eot_bytes);
|
||||
std::array<int16_t, 1920> eot_baseband = m_m17Modulator.symbols_to_baseband(eot_symbols);
|
||||
m_basebandFifo.write((const quint8*) eot_baseband.data(), 1920);
|
||||
}
|
||||
|
||||
void M17ModProcessor::output_baseband(std::array<uint8_t, 2> sync_word, const std::array<int8_t, 368>& frame)
|
||||
{
|
||||
std::array<int8_t, 184> symbols = mobilinkd::M17Modulator::bits_to_symbols(frame); // 368 bits -> 184 dibit symbols
|
||||
std::array<int8_t, 8> sw = mobilinkd::M17Modulator::bytes_to_symbols(sync_word); // 16 bits -> 8 dibit symbols
|
||||
|
||||
std::array<int8_t, 192> temp; // 384 = 368 + 16 bits -> 192 dibit symbols
|
||||
auto fit = std::copy(sw.begin(), sw.end(), temp.begin()); // start with sync word dibits
|
||||
std::copy(symbols.begin(), symbols.end(), fit); // then the frame dibits
|
||||
std::array<int16_t, 1920> baseband = m_m17Modulator.symbols_to_baseband(temp); // 1920 48 kS/s int16_t samples
|
||||
m_basebandFifo.write((const quint8*) baseband.data(), 1920);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "m17/M17Modulator.h"
|
||||
#include "util/message.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "audio/audiofifo.h"
|
||||
@ -58,14 +59,19 @@ public:
|
||||
~M17ModProcessor();
|
||||
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
AudioFifo *getSymbolFifo() { return &m_basebandFifo; }
|
||||
AudioFifo *getBasebandFifo() { return &m_basebandFifo; }
|
||||
|
||||
private:
|
||||
MessageQueue m_inputMessageQueue;
|
||||
AudioFifo m_basebandFifo; //!< Samples are 16 bit integer baseband 48 kS/s samples
|
||||
mobilinkd::M17Modulator m_m17Modulator;
|
||||
|
||||
bool handleMessage(const Message& cmd);
|
||||
void processPacket(const QString& sourceCall, const QString& destCall, const QByteArray& packetBytes);
|
||||
void test(const QString& sourceCall, const QString& destCall);
|
||||
void send_preamble();
|
||||
void send_eot();
|
||||
void output_baseband(std::array<uint8_t, 2> sync_word, const std::array<int8_t, 368>& frame);
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
|
@ -44,7 +44,7 @@ void M17ModSettings::resetToDefaults()
|
||||
m_title = "M17 Modulator";
|
||||
m_m17Mode = M17Mode::M17ModeNone;
|
||||
m_audioType = AudioType::AudioNone;
|
||||
m_packetType = PacketType::PacketNone;
|
||||
m_packetType = PacketType::PacketSMS;
|
||||
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
|
||||
m_feedbackAudioDeviceName = AudioDeviceManager::m_defaultDeviceName;
|
||||
m_feedbackVolumeFactor = 0.5f;
|
||||
@ -150,7 +150,7 @@ bool M17ModSettings::deserialize(const QByteArray& data)
|
||||
m_m17Mode = tmp < 0 ? M17ModeNone : tmp > (int) M17ModeM17BERT ? M17ModeM17BERT : (M17Mode) tmp;
|
||||
d.readS32(9, &tmp, 0);
|
||||
m_audioType = tmp < 0 ? AudioNone : tmp > (int) AudioInput ? AudioInput : (AudioType) tmp;
|
||||
m_packetType = tmp < 0 ? PacketNone : tmp > (int) PacketSMS ? PacketSMS : (PacketType) tmp;
|
||||
m_packetType = tmp < 0 ? PacketSMS : tmp > (int) PacketAPRS ? PacketAPRS : (PacketType) tmp;
|
||||
|
||||
d.readBlob(11, &bytetmp);
|
||||
|
||||
|
@ -45,7 +45,6 @@ struct M17ModSettings
|
||||
|
||||
enum PacketType
|
||||
{
|
||||
PacketNone,
|
||||
PacketSMS,
|
||||
PacketAPRS
|
||||
};
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "m17modsource.h"
|
||||
|
||||
const int M17ModSource::m_levelNbSamples = 480; // every 10ms
|
||||
const float M17ModSource::m_preemphasis = 120.0e-6; // 120us
|
||||
|
||||
M17ModSource::M17ModSource() :
|
||||
m_channelSampleRate(48000),
|
||||
@ -37,6 +38,7 @@ M17ModSource::M17ModSource() :
|
||||
m_peakLevel(0.0f),
|
||||
m_levelSum(0.0f),
|
||||
m_ifstream(nullptr),
|
||||
m_preemphasisFilter(m_preemphasis*48000),
|
||||
m_mutex(QMutex::Recursive)
|
||||
{
|
||||
m_audioFifo.setLabel("M17ModSource.m_audioFifo");
|
||||
@ -53,6 +55,8 @@ M17ModSource::M17ModSource() :
|
||||
m_demodBufferFill = 0;
|
||||
|
||||
m_magsq = 0.0;
|
||||
m_basebandMax = 0;
|
||||
m_basebandMin = 0;
|
||||
|
||||
m_processor = new M17ModProcessor();
|
||||
|
||||
@ -268,9 +272,30 @@ void M17ModSource::pullAF(Real& sample, bool& carrier)
|
||||
|
||||
void M17ModSource::pullM17(Real& sample, bool& carrier)
|
||||
{
|
||||
// TODO
|
||||
carrier = false;
|
||||
sample = 0.0f;
|
||||
quint8 bbSampleBytes[2];
|
||||
carrier = m_processor->getBasebandFifo()->readOne(bbSampleBytes);
|
||||
int16_t basbandSample = bbSampleBytes[0] | (bbSampleBytes[1]<<8);
|
||||
|
||||
if (carrier)
|
||||
{
|
||||
if (basbandSample > m_basebandMax)
|
||||
{
|
||||
qDebug("M17ModSource::pullM17: max: %d", basbandSample);
|
||||
m_basebandMax = basbandSample;
|
||||
}
|
||||
|
||||
if (basbandSample < m_basebandMin)
|
||||
{
|
||||
qDebug("M17ModSource::pullM17: min: %d", basbandSample);
|
||||
m_basebandMin = basbandSample;
|
||||
}
|
||||
|
||||
sample = basbandSample / 32768.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
sample = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModSource::pushFeedback(Real sample)
|
||||
@ -351,6 +376,7 @@ void M17ModSource::applyAudioSampleRate(int sampleRate)
|
||||
m_interpolator.create(48, sampleRate, m_settings.m_rfBandwidth / 2.2, 3.0);
|
||||
m_lowpass.create(301, sampleRate, m_settings.m_rfBandwidth);
|
||||
m_toneNco.setFreq(m_settings.m_toneFrequency, sampleRate);
|
||||
m_preemphasisFilter.configure(m_preemphasis*sampleRate);
|
||||
m_audioSampleRate = sampleRate;
|
||||
applyFeedbackAudioSampleRate(m_feedbackAudioSampleRate);
|
||||
|
||||
@ -449,6 +475,8 @@ void M17ModSource::handleAudio()
|
||||
|
||||
void M17ModSource::sendPacket()
|
||||
{
|
||||
qDebug("M17ModSource::sendPacket: %d", (int) m_settings.m_packetType);
|
||||
|
||||
if (m_settings.m_packetType == M17ModSettings::PacketType::PacketSMS)
|
||||
{
|
||||
M17ModProcessor::MsgSendSMS *msg = M17ModProcessor::MsgSendSMS::create(
|
||||
|
@ -119,8 +119,11 @@ private:
|
||||
|
||||
std::ifstream *m_ifstream;
|
||||
M17ModProcessor *m_processor;
|
||||
HighPassFilterRC m_preemphasisFilter;
|
||||
|
||||
QMutex m_mutex;
|
||||
int16_t m_basebandMin;
|
||||
int16_t m_basebandMax;
|
||||
|
||||
static const int m_levelNbSamples;
|
||||
static const float m_preemphasis;
|
||||
|
@ -160,6 +160,19 @@ uint32_t AudioFifo::read(quint8* data, uint32_t numSamples)
|
||||
return total;
|
||||
}
|
||||
|
||||
bool AudioFifo::readOne(quint8* data)
|
||||
{
|
||||
if ((!m_fifo) || isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, m_fifo + (m_head * m_sampleSize), m_sampleSize);
|
||||
m_head += 1;
|
||||
m_head %= m_size;
|
||||
m_fill -= 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint AudioFifo::drain(uint32_t numSamples)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
@ -38,6 +38,7 @@ public:
|
||||
|
||||
uint32_t write(const quint8* data, uint32_t numSamples);
|
||||
uint32_t read(quint8* data, uint32_t numSamples);
|
||||
bool readOne(quint8* data);
|
||||
|
||||
uint32_t drain(uint32_t numSamples);
|
||||
void clear();
|
||||
|
Loading…
Reference in New Issue
Block a user