1
0
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:
f4exb 2022-06-17 02:25:34 +02:00
parent f10da64717
commit 6b863287d8
19 changed files with 659 additions and 347 deletions

View File

@ -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

View File

@ -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;

View File

@ -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_;
}

View File

@ -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();

View File

@ -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

View File

@ -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
View 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

View File

@ -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)
{

View File

@ -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>

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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);

View File

@ -45,7 +45,6 @@ struct M17ModSettings
enum PacketType
{
PacketNone,
PacketSMS,
PacketAPRS
};

View File

@ -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(

View File

@ -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;

View File

@ -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);

View File

@ -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();