// Copyright 2020 Mobilinkd LLC. #pragma once #include "FirFilter.h" #include "PhaseEstimator.h" #include "DeviationError.h" #include "FrequencyError.h" #include "SymbolEvm.h" #include #include namespace modemm17 { namespace detail { 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 const auto evm_b = std::array{0.02008337, 0.04016673, 0.02008337}; static const auto evm_a = std::array{1.0, -1.56101808, 0.64135154}; } // detail struct Fsk4Demod { using demod_result_t = std::tuple; using result_t = std::tuple; BaseFirFilter::value> rrc = makeFirFilter(detail::rrc_taps); PhaseEstimator phase = PhaseEstimator(48000, 4800); DeviationError<10> deviation; FrequencyError<32> frequency; SymbolEvm::value> symbol_evm = makeSymbolEvm(makeIirFilter(detail::evm_b, detail::evm_a)); double sample_rate = 48000; double symbol_rate = 4800; double unlock_gain = 0.02; double lock_gain = 0.001; std::array samples{0}; double t = 0; double dt = symbol_rate / sample_rate; double ideal_dt = dt; bool sample_now = false; double estimated_deviation = 1.0; double estimated_frequency_offset = 0.0; double evm_average = 0.0; Fsk4Demod( double sample_rate, double symbol_rate, double unlock_gain = 0.02, double lock_gain = 0.001 ) : sample_rate(sample_rate), symbol_rate(symbol_rate), unlock_gain(unlock_gain * symbol_rate / sample_rate), lock_gain(lock_gain * symbol_rate / sample_rate), dt(symbol_rate / sample_rate), ideal_dt(dt) { samples.fill(0.0); } demod_result_t demod(bool lock) { estimated_deviation = deviation(samples[1]); for (auto& sample : samples) sample *= estimated_deviation; estimated_frequency_offset = frequency(samples[1]); for (auto& sample : samples) sample -= estimated_frequency_offset; auto phase_estimate = phase(samples); if (samples[1] < 0) phase_estimate *= -1; dt = ideal_dt - (phase_estimate * (lock ? lock_gain : unlock_gain)); t += dt; symbol_evm(samples[1]); int symbol; float evm; std::tie(symbol, evm) = symbol_evm(samples[1]); evm_average = symbol_evm.evm(); samples[0] = samples[2]; return std::make_tuple(samples[1], phase_estimate, symbol, evm); } /** * Process the sample. If a symbol is ready, return a tuple * containing the sample used, the estimated phase, the decoded * symbol, the EVM, the deviation error and the frequency error * (sample, phase, symbol, evm, ed, ef), otherwise None. */ result_t operator()(double sample, bool lock) { auto filtered_sample = rrc(sample); if (sample_now) { samples[2] = filtered_sample; sample_now = false; double prev_sample; double phase_estimate; int symbol; double evm; std::tie(prev_sample, phase_estimate, symbol, evm) = demod(lock); return std::make_tuple( prev_sample, phase_estimate, symbol, evm, estimated_deviation, estimated_frequency_offset, evm_average ); } t += dt; if (t < 1.0) { samples[0] = filtered_sample; } else { t -= 1.0; samples[1] = filtered_sample; sample_now = true; } return result_t{0, 0, 0, 0, 0, 0, 0}; } }; } // modemm17