diff --git a/modems/CMakeLists.txt b/modems/CMakeLists.txt index a6e37437e..d0f954180 100644 --- a/modems/CMakeLists.txt +++ b/modems/CMakeLists.txt @@ -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 diff --git a/modems/m17/ClockRecovery.h b/modems/m17/ClockRecovery.h index af002fe02..de2760846 100644 --- a/modems/m17/ClockRecovery.h +++ b/modems/m17/ClockRecovery.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; diff --git a/modems/m17/Correlator.h b/modems/m17/Correlator.h index 3b5bdcb83..d1c6fc4ed 100644 --- a/modems/m17/Correlator.h +++ b/modems/m17/Correlator.h @@ -12,6 +12,7 @@ #include #include #include +#include #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::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::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_; } diff --git a/modems/m17/M17Demodulator.cpp b/modems/m17/M17Demodulator.cpp index c43ac14df..7a4ce22e4 100644 --- a/modems/m17/M17Demodulator.cpp +++ b/modems/m17/M17Demodulator.cpp @@ -2,45 +2,71 @@ namespace mobilinkd { +// const std::array M17Demodulator::rrc_taps = std::array{ +// 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 M17Demodulator::rrc_taps = std::array{ - 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(); diff --git a/modems/m17/M17Demodulator.h b/modems/m17/M17Demodulator.h index 51b3703da..bb2fea0a3 100644 --- a/modems/m17/M17Demodulator.h +++ b/modems/m17/M17Demodulator.h @@ -51,10 +51,9 @@ struct M17_API M17Demodulator DataCarrierDetect dcd{2500, 4000, 1.0, 4.0}; ClockRecovery 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 rrc_taps; BaseFirFilter demod_filter{rrc_taps}; + collelator_t correlator; + int16_t initializing_count_; }; } // mobilinkd diff --git a/modems/m17/M17FrameDecoder.h b/modems/m17/M17FrameDecoder.h index 004fb4ada..7d32f22fc 100644 --- a/modems/m17/M17FrameDecoder.h +++ b/modems/m17/M17FrameDecoder.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace mobilinkd { @@ -23,16 +24,13 @@ namespace mobilinkd template void dump(const std::array& 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& 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 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); diff --git a/modems/m17/M17Modulator.cpp b/modems/m17/M17Modulator.cpp new file mode 100644 index 000000000..135e647b5 --- /dev/null +++ b/modems/m17/M17Modulator.cpp @@ -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 M17Modulator::SYNC_WORD = {0x32, 0x43}; // 00 11 00 10 01 00 00 11 | +1 -3 +1 -1 +3 +1 +1 -3 ???? +const std::array 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 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 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 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 M17Modulator::EOT_SYNC = {0x55, 0x5D}; // ???? + +// Generated using scikit-commpy +// const std::array M17Modulator::rrc_taps = std::array{ +// 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 M17Modulator::rrc_taps = std::array{ + -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 + diff --git a/modems/m17/M17Modulator.h b/modems/m17/M17Modulator.h index 12fbd16af..6325b83fa 100644 --- a/modems/m17/M17Modulator.h +++ b/modems/m17/M17Modulator.h @@ -41,9 +41,12 @@ public: using frame_t = std::array; // M17 frame (without sync word). using packet_t = std::array; // Packet payload - static constexpr std::array SYNC_WORD = {0x32, 0x43}; - static constexpr std::array LSF_SYNC_WORD = {0x55, 0xF7}; - static constexpr std::array DATA_SYNC_WORD = {0xFF, 0x5D}; + static const std::array SYNC_WORD; + static const std::array LSF_SYNC_WORD; + static const std::array STREAM_SYNC_WORD; + static const std::array PACKET_SYNC_WORD; + static const std::array BERT_SYNC_WORD; + static const std::array EOT_SYNC; static constexpr int8_t bits_to_symbol(uint8_t bits) { @@ -58,12 +61,23 @@ public: return 0; } + template + static std::array bits_to_symbols(const std::array& bits) + { + std::array 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 static std::array bytes_to_symbols(const std::array& bytes) { std::array 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& 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 + std::array symbols_to_baseband(std::array symbols, bool invert = false) { - // Preamble is simple... bytes -> symbols. - preamble_bytes.fill(0x77); + std::array 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 segment, uint8_t segment_number) + static std::array 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 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 encoded; + size_t index = 0; + uint32_t memory = 0; + + for (auto b : lsf) { - std::array 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 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 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 make_packet_frame( + uint8_t packet_number, + int packet_size, + bool last_packet, + const std::array packet + ) { + M17Randomizer<368> randomizer; + PolynomialInterleaver<45, 92, 368> interleaver; + std::array 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 encoded = conv_encode(packet_assembly); - frame_t punctured; - auto size = puncture_bytes(encoded, punctured, mobilinkd::P3); + std::array 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 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{ - -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::value> rrc = makeFirFilter(rrc_taps); - - std::array 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 rrc_taps; + CRC16<0x5935, 0xFFFF> crc_; static LinkSetupFrame::encoded_call_t encode_callsign(std::string callsign) { diff --git a/plugins/channelrx/demodm17/m17demodgui.ui b/plugins/channelrx/demodm17/m17demodgui.ui index 3f9e64d12..4da0798ab 100644 --- a/plugins/channelrx/demodm17/m17demodgui.ui +++ b/plugins/channelrx/demodm17/m17demodgui.ui @@ -567,7 +567,7 @@ - 80 + 90 0 @@ -602,7 +602,7 @@ - 80 + 90 0 @@ -637,7 +637,7 @@ - 110 + 120 0 diff --git a/plugins/channelrx/demodm17/m17demodprocessor.cpp b/plugins/channelrx/demodm17/m17demodprocessor.cpp index 931cac3a8..0067c89a2 100644 --- a/plugins/channelrx/demodm17/m17demodprocessor.cpp +++ b/plugins/channelrx/demodm17/m17demodprocessor.cpp @@ -15,9 +15,9 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include -#include -#include +// #include +// #include +// #include #include #include @@ -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& 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::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; } diff --git a/plugins/channeltx/modm17/m17modgui.cpp b/plugins/channeltx/modm17/m17modgui.cpp index cd0492cd1..1df7c62da 100644 --- a/plugins/channeltx/modm17/m17modgui.cpp +++ b/plugins/channeltx/modm17/m17modgui.cpp @@ -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; } } diff --git a/plugins/channeltx/modm17/m17modprocessor.cpp b/plugins/channeltx/modm17/m17modprocessor.cpp index 5933106a9..2ef296589 100644 --- a/plugins/channeltx/modm17/m17modprocessor.cpp +++ b/plugins/channeltx/modm17/m17modprocessor.cpp @@ -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 preamble_bytes; - mobilinkd::M17Modulator::make_preamble(preamble_bytes); - std::array fullframe_symbols = mobilinkd::M17Modulator::bytes_to_symbols(preamble_bytes); - std::array 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 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 lsf; + std::array 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 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 preamble_bytes; + preamble_bytes.fill(0x77); + std::array preamble_symbols = mobilinkd::M17Modulator::bytes_to_symbols(preamble_bytes); + std::array preamble_baseband = m_m17Modulator.symbols_to_baseband(preamble_symbols); + m_basebandFifo.write((const quint8*) preamble_baseband.data(), 1920); +} + +void M17ModProcessor::send_eot() +{ + std::array EOT_SYNC = { 0x55, 0x5D }; + std::array 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 eot_symbols = mobilinkd::M17Modulator::bytes_to_symbols(eot_bytes); + std::array eot_baseband = m_m17Modulator.symbols_to_baseband(eot_symbols); + m_basebandFifo.write((const quint8*) eot_baseband.data(), 1920); +} + +void M17ModProcessor::output_baseband(std::array sync_word, const std::array& frame) +{ + std::array symbols = mobilinkd::M17Modulator::bits_to_symbols(frame); // 368 bits -> 184 dibit symbols + std::array sw = mobilinkd::M17Modulator::bytes_to_symbols(sync_word); // 16 bits -> 8 dibit symbols + + std::array 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 baseband = m_m17Modulator.symbols_to_baseband(temp); // 1920 48 kS/s int16_t samples m_basebandFifo.write((const quint8*) baseband.data(), 1920); } diff --git a/plugins/channeltx/modm17/m17modprocessor.h b/plugins/channeltx/modm17/m17modprocessor.h index 8a2ba7f32..5492b3e28 100644 --- a/plugins/channeltx/modm17/m17modprocessor.h +++ b/plugins/channeltx/modm17/m17modprocessor.h @@ -21,6 +21,7 @@ #include #include +#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 sync_word, const std::array& frame); private slots: void handleInputMessages(); diff --git a/plugins/channeltx/modm17/m17modsettings.cpp b/plugins/channeltx/modm17/m17modsettings.cpp index b5062e2e2..5611d9615 100644 --- a/plugins/channeltx/modm17/m17modsettings.cpp +++ b/plugins/channeltx/modm17/m17modsettings.cpp @@ -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); diff --git a/plugins/channeltx/modm17/m17modsettings.h b/plugins/channeltx/modm17/m17modsettings.h index bb0ede7d2..b1ebbbfcb 100644 --- a/plugins/channeltx/modm17/m17modsettings.h +++ b/plugins/channeltx/modm17/m17modsettings.h @@ -45,7 +45,6 @@ struct M17ModSettings enum PacketType { - PacketNone, PacketSMS, PacketAPRS }; diff --git a/plugins/channeltx/modm17/m17modsource.cpp b/plugins/channeltx/modm17/m17modsource.cpp index a52ad489c..2235d4ee6 100644 --- a/plugins/channeltx/modm17/m17modsource.cpp +++ b/plugins/channeltx/modm17/m17modsource.cpp @@ -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( diff --git a/plugins/channeltx/modm17/m17modsource.h b/plugins/channeltx/modm17/m17modsource.h index 6890edc5b..954e5dd34 100644 --- a/plugins/channeltx/modm17/m17modsource.h +++ b/plugins/channeltx/modm17/m17modsource.h @@ -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; diff --git a/sdrbase/audio/audiofifo.cpp b/sdrbase/audio/audiofifo.cpp index b7d58b0a9..94e43d74f 100644 --- a/sdrbase/audio/audiofifo.cpp +++ b/sdrbase/audio/audiofifo.cpp @@ -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); diff --git a/sdrbase/audio/audiofifo.h b/sdrbase/audio/audiofifo.h index f80aa9a0d..cb9c1ac9a 100644 --- a/sdrbase/audio/audiofifo.h +++ b/sdrbase/audio/audiofifo.h @@ -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();