1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 09:18:54 -05:00

M17 demod: removed FloatType template parameter

This commit is contained in:
f4exb 2022-06-09 20:12:35 +02:00
parent f326860f64
commit 424d072f0c
20 changed files with 652 additions and 731 deletions

View File

@ -13,22 +13,21 @@
namespace mobilinkd
{
template <typename FloatType>
struct CarrierDetect
{
using result_t = std::tuple<bool, FloatType>;
using result_t = std::tuple<bool, float>;
BaseIirFilter<FloatType, 3> filter_;
FloatType lock_;
FloatType unlock_;
BaseIirFilter<3> filter_;
float lock_;
float unlock_;
bool locked_ = false;
CarrierDetect(std::array<FloatType, 3> const& b, std::array<FloatType, 3> const& a, FloatType lock_level, FloatType unlock_level)
CarrierDetect(std::array<float, 3> const& b, std::array<float, 3> const& a, float lock_level, float unlock_level)
: filter_(b, a), lock_(lock_level), unlock_(unlock_level)
{
}
result_t operator()(FloatType value)
result_t operator()(float value)
{
auto filtered = filter_(std::abs(value));
if (locked_ && (filtered > unlock_)) locked_ = false;

View File

@ -13,16 +13,16 @@ namespace mobilinkd
/**
* Calculate the phase estimates for each sample position.
*
*
* This performs a running calculation of the phase of each bit position.
* It is very noisy for individual samples, but quite accurate when
* averaged over an entire M17 frame.
*
*
* It is designed to be used to calculate the best bit position for each
* frame of data. Samples are collected and averaged. When update() is
* called, the best sample index and clock are estimated, and the counters
* reset for the next frame.
*
*
* It starts counting bit 0 as the first bit received after a reset.
*
* This is very efficient as it only uses addition and subtraction for
@ -37,24 +37,24 @@ namespace mobilinkd
* @inv sample_index_ is in the interval [0, SAMPLES_PER_SYMBOL).
* @inv clock_ is in the interval [0.9995, 1.0005]
*/
template <typename FloatType, size_t SampleRate, size_t SymbolRate>
template <size_t SampleRate, size_t SymbolRate>
class ClockRecovery
{
static constexpr size_t SAMPLES_PER_SYMBOL = SampleRate / SymbolRate;
static constexpr int8_t MAX_OFFSET = SAMPLES_PER_SYMBOL / 2;
static constexpr FloatType dx = 1.0 / SAMPLES_PER_SYMBOL;
static constexpr FloatType MAX_CLOCK = 1.0005;
static constexpr FloatType MIN_CLOCK = 0.9995;
static constexpr float dx = 1.0 / SAMPLES_PER_SYMBOL;
static constexpr float MAX_CLOCK = 1.0005;
static constexpr float MIN_CLOCK = 0.9995;
std::array<FloatType, SAMPLES_PER_SYMBOL> estimates_;
std::array<float, SAMPLES_PER_SYMBOL> estimates_;
size_t sample_count_ = 0;
uint16_t frame_count_ = 0;
uint8_t sample_index_ = 0;
uint8_t prev_sample_index_ = 0;
uint8_t index_ = 0;
FloatType offset_ = 0.0;
FloatType clock_ = 1.0;
FloatType prev_sample_ = 0.0;
float offset_ = 0.0;
float clock_ = 1.0;
float prev_sample_ = 0.0;
/**
* Find the sample index.
@ -78,7 +78,7 @@ class ClockRecovery
bool is_positive = false;
for (size_t i = 0; i != SAMPLES_PER_SYMBOL; ++i)
{
FloatType phase = estimates_[i];
float phase = estimates_[i];
if (!is_positive && phase > 0)
{
@ -90,7 +90,7 @@ class ClockRecovery
break;
}
}
sample_index_ = index == 0 ? SAMPLES_PER_SYMBOL - 1 : index - 1;
}
@ -99,7 +99,7 @@ class ClockRecovery
*
* This should never be greater than one.
*/
FloatType calc_offset_()
float calc_offset_()
{
int8_t offset = sample_index_ - prev_sample_index_;
@ -139,14 +139,14 @@ public:
{
estimates_.fill(0);
}
/**
* Update clock recovery with the given sample. This will advance the
* current sample index by 1.
*/
void operator()(FloatType sample)
void operator()(float sample)
{
FloatType dy = (sample - prev_sample_);
float dy = (sample - prev_sample_);
if (sample + prev_sample_ < 0)
{
@ -155,7 +155,7 @@ public:
}
prev_sample_ = sample;
estimates_[index_] += dy;
index_ += 1;
if (index_ == SAMPLES_PER_SYMBOL)
@ -189,18 +189,18 @@ public:
/**
* Return the estimated sample clock increment based on the last update.
*
*
* The value is only valid after samples have been collected and update()
* has been called.
*/
FloatType clock_estimate() const
float clock_estimate() const
{
return clock_;
}
/**
* Return the estimated "best sample index" based on the last update.
*
*
* The value is only valid after samples have been collected and update()
* has been called.
*/
@ -208,20 +208,20 @@ public:
{
return sample_index_;
}
/**
* Update the sample index and clock estimates, and reset the state for
* the next frame of data.
*
*
* @pre index_ = 0
* @pre sample_count_ > 0
*
*
* After this is called, sample_index() and clock_estimate() will have
* valid, updated results.
*
*
* The more samples between calls to update, the more accurate the
* estimates will be.
*
*
* @return true if the preconditions are met and the update has been
* performed, otherwise false.
*/
@ -231,7 +231,7 @@ public:
update_sample_index_();
update_clock_();
frame_count_ = std::min(0x1000, 1 + frame_count_);
sample_count_ = 0;
estimates_.fill(0);

View File

@ -3,16 +3,7 @@
namespace mobilinkd {
// IIR with Nyquist of 1/240.
template<>
const std::array<double,3> Correlator<double>::b = {4.24433681e-05, 8.48867363e-05, 4.24433681e-05};
template<>
const std::array<double,3> Correlator<double>::a = {1.0, -1.98148851, 0.98165828};
template<>
const std::array<float,3> Correlator<float>::b = {4.24433681e-05, 8.48867363e-05, 4.24433681e-05};
template<>
const std::array<float,3> Correlator<float>::a = {1.0, -1.98148851, 0.98165828};
const std::array<float,3> Correlator::b = {4.24433681e-05, 8.48867363e-05, 4.24433681e-05};
const std::array<float,3> Correlator::a = {1.0, -1.98148851, 0.98165828};
} // namespace mobilinkd

View File

@ -15,32 +15,31 @@
namespace mobilinkd {
template <typename FloatType>
struct Correlator
{
static constexpr size_t SYMBOLS = 8;
static constexpr size_t SAMPLES_PER_SYMBOL = 10;
using value_type = FloatType;
using buffer_t = std::array<FloatType, SYMBOLS * SAMPLES_PER_SYMBOL>;
using value_type = float;
using buffer_t = std::array<float, SYMBOLS * SAMPLES_PER_SYMBOL>;
using sync_t = std::array<int8_t, SYMBOLS>;
using sample_filter_t = BaseIirFilter<FloatType, 3>;
using sample_filter_t = BaseIirFilter<3>;
buffer_t buffer_;
FloatType limit_ = 0.;
float limit_ = 0.;
size_t symbol_pos_ = 0;
size_t buffer_pos_ = 0;
size_t prev_buffer_pos_ = 0;
int code = -1;
// IIR with Nyquist of 1/240.
static const std::array<FloatType,3> b;
static const std::array<FloatType,3> a;
static const std::array<float,3> b;
static const std::array<float,3> a;
sample_filter_t sample_filter{b, a};
std::array<int, SYMBOLS> tmp;
void sample(FloatType value)
void sample(float value)
{
limit_ = sample_filter(std::abs(value));
buffer_[buffer_pos_] = value;
@ -48,9 +47,9 @@ struct Correlator
if (++buffer_pos_ == buffer_.size()) buffer_pos_ = 0;
}
FloatType correlate(sync_t sync)
float correlate(sync_t sync)
{
FloatType result = 0.;
float result = 0.;
size_t pos = prev_buffer_pos_ + SAMPLES_PER_SYMBOL;
for (size_t i = 0; i != sync.size(); ++i)
@ -63,7 +62,7 @@ struct Correlator
return result;
}
FloatType limit() const {return limit_;}
float limit() const {return limit_;}
size_t index() const {return prev_buffer_pos_ % SAMPLES_PER_SYMBOL;}
/**
@ -78,10 +77,10 @@ struct Correlator
* second holds true for the sync words used for M17. The third will
* hold true if passed the timing index from a triggered sync word.
*/
std::tuple<FloatType, FloatType> outer_symbol_levels(size_t sample_index)
std::tuple<float, float> outer_symbol_levels(size_t sample_index)
{
FloatType min_sum = 0;
FloatType max_sum = 0;
float min_sum = 0;
float max_sum = 0;
size_t min_count = 0;
size_t max_count = 0;
size_t index = 0;

View File

@ -25,23 +25,23 @@ namespace mobilinkd {
*
* Note: the input to this DCD must be unfiltered (raw) baseband input.
*/
template <typename FloatType, size_t SampleRate, size_t Accuracy = 1000>
template <size_t SampleRate, size_t Accuracy = 1000>
struct DataCarrierDetect
{
using ComplexType = std::complex<FloatType>;
using NDFT = NSlidingDFT<FloatType, SampleRate, SampleRate / Accuracy, 2>;
using ComplexType = std::complex<float>;
using NDFT = NSlidingDFT<SampleRate, SampleRate / Accuracy, 2>;
NDFT dft_;
FloatType ltrigger_;
FloatType htrigger_;
FloatType level_1 = 0.0;
FloatType level_2 = 0.0;
FloatType level_ = 0.0;
float ltrigger_;
float htrigger_;
float level_1 = 0.0;
float level_2 = 0.0;
float level_ = 0.0;
bool triggered_ = false;
DataCarrierDetect(
size_t freq1, size_t freq2,
FloatType ltrigger = 2.0, FloatType htrigger = 5.0)
float ltrigger = 2.0, float htrigger = 5.0)
: dft_({freq1, freq2}), ltrigger_(ltrigger), htrigger_(htrigger)
{
}
@ -50,7 +50,7 @@ struct DataCarrierDetect
* Accept unfiltered baseband input and output a decision on whether
* a carrier has been detected after every @tparam BlockSize inputs.
*/
void operator()(FloatType sample)
void operator()(float sample)
{
auto result = dft_(sample);
level_1 += std::norm(result[0]);
@ -69,7 +69,7 @@ struct DataCarrierDetect
}
FloatType level() const { return level_; }
float level() const { return level_; }
bool dcd() const { return triggered_; }
};

View File

@ -9,11 +9,10 @@
namespace mobilinkd
{
template <typename T, size_t N = 10>
template <size_t N = 10>
struct DeviationError
{
using float_type = T;
using array_t = std::array<float_type, N>;
using array_t = std::array<float, N>;
array_t minima_{0};
array_t maxima_{0};
@ -23,18 +22,18 @@ struct DeviationError
bool max_rolled_ = false;
size_t min_count_ = 0;
size_t max_count_ = 0;
float_type min_estimate_ = 0.0;
float_type max_estimate_ = 0.0;
float min_estimate_ = 0.0;
float max_estimate_ = 0.0;
const float_type ZERO = 0.0;
const float ZERO = 0.0;
DeviationError()
{
minima_.fill(0.0);
maxima_.fill(0.0);
}
float_type operator()(float_type sample)
float operator()(float sample)
{
if (sample > ZERO)
{

View File

@ -10,27 +10,27 @@
namespace mobilinkd
{
template <typename FloatType, size_t N>
struct BaseFirFilter : FilterBase<FloatType>
template <size_t N>
struct BaseFirFilter : FilterBase<float>
{
using array_t = std::array<FloatType, N>;
using array_t = std::array<float, N>;
const array_t& taps_;
array_t history_;
size_t pos_ = 0;
BaseFirFilter(const array_t& taps)
: taps_(taps)
{
history_.fill(0.0);
}
FloatType operator()(FloatType input) override
float operator()(float input) override
{
history_[pos_++] = input;
if (pos_ == N) pos_ = 0;
FloatType result = 0.0;
float result = 0.0;
size_t index = pos_;
for (size_t i = 0; i != N; ++i)
@ -49,10 +49,10 @@ struct BaseFirFilter : FilterBase<FloatType>
}
};
template <typename FloatType, size_t N>
BaseFirFilter<FloatType, N> makeFirFilter(const std::array<FloatType, N>& taps)
template <size_t N>
BaseFirFilter<N> makeFirFilter(const std::array<float, N>& taps)
{
return std::move(BaseFirFilter<FloatType, N>(taps));
return std::move(BaseFirFilter<N>(taps));
}

View File

@ -2,16 +2,7 @@
namespace mobilinkd {
template<>
const std::array<double, 3> FreqDevEstimator<double>::dc_b = { 0.09763107, 0.19526215, 0.09763107 };
template<>
const std::array<double, 3> FreqDevEstimator<double>::dc_a = { 1. , -0.94280904, 0.33333333 };
template<>
const std::array<float, 3> FreqDevEstimator<float>::dc_b = { 0.09763107, 0.19526215, 0.09763107 };
template<>
const std::array<float, 3> FreqDevEstimator<float>::dc_a = { 1. , -0.94280904, 0.33333333 };
const std::array<float, 3> FreqDevEstimator::dc_b = { 0.09763107, 0.19526215, 0.09763107 };
const std::array<float, 3> FreqDevEstimator::dc_a = { 1. , -0.94280904, 0.33333333 };
} // namespace mobilinkd

View File

@ -24,29 +24,28 @@ namespace mobilinkd {
* Estimates are expected to be updated at each sync word. But they can
* be updated more frequently, such as during the preamble.
*/
template <typename FloatType>
class FreqDevEstimator
{
using sample_filter_t = BaseIirFilter<FloatType, 3>;
using sample_filter_t = BaseIirFilter<3>;
// IIR with Nyquist of 1/4.
static const std::array<FloatType, 3> dc_b;
static const std::array<FloatType, 3> dc_a;
static const std::array<float, 3> dc_b;
static const std::array<float, 3> dc_a;
static constexpr FloatType MAX_DC_ERROR = 0.2;
static constexpr float MAX_DC_ERROR = 0.2;
FloatType min_est_ = 0.0;
FloatType max_est_ = 0.0;
FloatType min_cutoff_ = 0.0;
FloatType max_cutoff_ = 0.0;
FloatType min_var_ = 0.0;
FloatType max_var_ = 0.0;
float min_est_ = 0.0;
float max_est_ = 0.0;
float min_cutoff_ = 0.0;
float max_cutoff_ = 0.0;
float min_var_ = 0.0;
float max_var_ = 0.0;
size_t min_count_ = 0;
size_t max_count_ = 0;
FloatType deviation_ = 0.0;
FloatType offset_ = 0.0;
FloatType error_ = 0.0;
FloatType idev_ = 1.0;
float deviation_ = 0.0;
float offset_ = 0.0;
float error_ = 0.0;
float idev_ = 1.0;
sample_filter_t dc_filter_{dc_b, dc_a};
public:
@ -63,7 +62,7 @@ public:
max_cutoff_ = 0.0;
}
void sample(FloatType sample)
void sample(float sample)
{
if (sample < 1.5 * min_est_)
{
@ -76,7 +75,7 @@ public:
{
min_count_ += 1;
min_est_ += sample;
FloatType var = (min_est_ / min_count_) - sample;
float var = (min_est_ / min_count_) - sample;
min_var_ += var * var;
}
else if (sample > 1.5 * max_est_)
@ -90,7 +89,7 @@ public:
{
max_count_ += 1;
max_est_ += sample;
FloatType var = (max_est_ / max_count_) - sample;
float var = (max_est_ / max_count_) - sample;
max_var_ += var * var;
}
}
@ -104,8 +103,8 @@ public:
void update()
{
if (max_count_ < 2 || min_count_ < 2) return;
FloatType max_ = max_est_ / max_count_;
FloatType min_ = min_est_ / min_count_;
float max_ = max_est_ / max_count_;
float min_ = min_est_ / min_count_;
deviation_ = (max_ - min_) / 6.0;
offset_ = dc_filter_(std::max(std::min(max_ + min_, deviation_ * MAX_DC_ERROR), deviation_ * -MAX_DC_ERROR));
error_ = (std::sqrt(max_var_ / (max_count_ - 1)) + std::sqrt(min_var_ / (min_count_ - 1))) * 0.5;
@ -120,10 +119,10 @@ public:
min_var_ = 0.0;
}
FloatType deviation() const { return deviation_; }
FloatType offset() const { return offset_; }
FloatType error() const { return error_; }
FloatType idev() const { return idev_; }
float deviation() const { return deviation_; }
float offset() const { return offset_; }
float error() const { return error_; }
float idev() const { return idev_; }
};
} // mobilinkd

View File

@ -11,15 +11,15 @@
namespace mobilinkd
{
template <typename FloatType, size_t N = 32>
template <size_t N = 32>
struct FrequencyError
{
using float_type = FloatType;
using array_t = std::array<FloatType, N>;
using filter_type = BaseIirFilter<FloatType, 3>;
using float_type = float;
using array_t = std::array<float, N>;
using filter_type = BaseIirFilter<3>;
static constexpr std::array<FloatType, 3> evm_b{0.02008337, 0.04016673, 0.02008337};
static constexpr std::array<FloatType, 3> evm_a{1.0, -1.56101808, 0.64135154};
static constexpr std::array<float, 3> evm_b{0.02008337, 0.04016673, 0.02008337};
static constexpr std::array<float, 3> evm_a{1.0, -1.56101808, 0.64135154};
array_t samples_{0};
size_t index_ = 0;
@ -33,10 +33,10 @@ struct FrequencyError
{
samples_.fill(0.0);
}
auto operator()(float_type sample)
{
FloatType evm = 0;
float evm = 0;
bool use = true;
if (sample > 2)

View File

@ -16,7 +16,7 @@ namespace mobilinkd
namespace detail
{
static const auto rrc_taps = std::array<double, 79>{
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,
@ -39,8 +39,8 @@ static const auto rrc_taps = std::array<double, 79>{
-0.001125978562075172, -0.006136551625729697, -0.009265784007800534
};
static const auto evm_b = std::array<double, 3>{0.02008337, 0.04016673, 0.02008337};
static const auto evm_a = std::array<double, 3>{1.0, -1.56101808, 0.64135154};
static const auto evm_b = std::array<float, 3>{0.02008337, 0.04016673, 0.02008337};
static const auto evm_a = std::array<float, 3>{1.0, -1.56101808, 0.64135154};
} // detail
struct Fsk4Demod
@ -48,17 +48,17 @@ struct Fsk4Demod
using demod_result_t = std::tuple<double, double, int, double>;
using result_t = std::tuple<double, double, int, double, double, double, double>;
BaseFirFilter<double, std::tuple_size<decltype(detail::rrc_taps)>::value> rrc = makeFirFilter(detail::rrc_taps);
PhaseEstimator<double> phase = PhaseEstimator<double>(48000, 4800);
DeviationError<double> deviation;
FrequencyError<double, 32> frequency;
SymbolEvm<double, std::tuple_size<decltype(detail::evm_b)>::value> symbol_evm = makeSymbolEvm(makeIirFilter(detail::evm_b, detail::evm_a));
BaseFirFilter<std::tuple_size<decltype(detail::rrc_taps)>::value> rrc = makeFirFilter(detail::rrc_taps);
PhaseEstimator phase = PhaseEstimator(48000, 4800);
DeviationError<10> deviation;
FrequencyError<32> frequency;
SymbolEvm<std::tuple_size<decltype(detail::evm_b)>::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<double, 3> samples{0};
std::array<float, 3> samples{0};
double t = 0;
double dt = symbol_rate / sample_rate;
double ideal_dt = dt;

View File

@ -10,43 +10,43 @@
namespace mobilinkd
{
template <typename FloatType, size_t N>
struct BaseIirFilter : FilterBase<FloatType>
template <size_t N>
struct BaseIirFilter : FilterBase<float>
{
const std::array<FloatType, N>& numerator_;
const std::array<FloatType, N> denominator_;
std::array<FloatType, N> history_{0};
BaseIirFilter(const std::array<FloatType, N>& b, const std::array<FloatType, N>& a)
const std::array<float, N>& numerator_;
const std::array<float, N> denominator_;
std::array<float, N> history_{0};
BaseIirFilter(const std::array<float, N>& b, const std::array<float, N>& a)
: numerator_(b), denominator_(a)
{
history_.fill(0.0);
}
FloatType operator()(FloatType input) {
float operator()(float input) {
for (size_t i = N - 1; i != 0; i--) history_[i] = history_[i - 1];
history_[0] = input;
for (size_t i = 1; i != N; i++) {
history_[0] -= denominator_[i] * history_[i];
}
FloatType result = 0;
float result = 0;
for (size_t i = 0; i != N; i++) {
result += numerator_[i] * history_[i];
}
return result;
}
};
template <typename FloatType, size_t N>
BaseIirFilter<FloatType, N> makeIirFilter(
const std::array<FloatType, N>& b, const std::array<FloatType, N>& a)
template <size_t N>
BaseIirFilter<N> makeIirFilter(
const std::array<float, N>& b, const std::array<float, N>& a)
{
return std::move(BaseIirFilter<FloatType, N>(b, a));
return std::move(BaseIirFilter<N>(b, a));
}
} // mobilinkd

View File

@ -2,8 +2,7 @@
namespace mobilinkd {
template <>
const std::array<double, 150> M17Demodulator<double>::rrc_taps = std::array<double, 150>{
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,
@ -44,46 +43,453 @@ const std::array<double, 150> M17Demodulator<double>::rrc_taps = std::array<doub
0.0029364388513841593, 0.0
};
template <>
const std::array<float, 150> M17Demodulator<float>::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
};
void M17Demodulator::update_values(uint8_t index)
{
correlator.apply([this,index](float t){dev.sample(t);}, index);
dev.update();
sync_sample_index = index;
}
void M17Demodulator::dcd_on()
{
// Data carrier newly detected.
dcd_ = true;
sync_count = 0;
missing_sync_count = 0;
dev.reset();
framer.reset();
decoder.reset();
}
void M17Demodulator::dcd_off()
{
// Just lost data carrier.
dcd_ = false;
demodState = DemodState::UNLOCKED;
decoder.reset();
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);
}
}
void M17Demodulator::initialize(const float input)
{
auto filtered_sample = demod_filter(input);
correlator.sample(filtered_sample);
}
void M17Demodulator::update_dcd()
{
if (!dcd_ && dcd.dcd())
{
// fputs("\nAOS\n", stderr);
dcd_on();
need_clock_reset_ = true;
}
else if (dcd_ && !dcd.dcd())
{
// fputs("\nLOS\n", stderr);
dcd_off();
}
}
void M17Demodulator::do_unlocked()
{
// We expect to find the preamble immediately after DCD.
if (missing_sync_count < 1920)
{
missing_sync_count += 1;
auto sync_index = preamble_sync(correlator);
auto sync_updated = preamble_sync.updated();
if (sync_updated)
{
sync_count = 0;
missing_sync_count = 0;
need_clock_reset_ = true;
dev.reset();
update_values(sync_index);
sample_index = sync_index;
demodState = DemodState::LSF_SYNC;
}
return;
}
auto sync_index = lsf_sync(correlator);
auto sync_updated = lsf_sync.updated();
if (sync_updated)
{
sync_count = 0;
missing_sync_count = 0;
need_clock_reset_ = true;
dev.reset();
update_values(sync_index);
sample_index = sync_index;
demodState = DemodState::FRAME;
if (sync_updated < 0)
{
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
}
else
{
sync_word_type = M17FrameDecoder::SyncWordType::LSF;
}
return;
}
sync_index = packet_sync(correlator);
sync_updated = packet_sync.updated();
if (sync_updated < 0)
{
sync_count = 0;
missing_sync_count = 0;
need_clock_reset_ = true;
dev.reset();
update_values(sync_index);
sample_index = sync_index;
demodState = DemodState::FRAME;
sync_word_type = M17FrameDecoder::SyncWordType::BERT;
}
}
/**
* Check for LSF sync word. We only enter the DemodState::LSF_SYNC state
* if a preamble sync has been detected, which also means that sample_index
* has been initialized to a sane value for the baseband.
*/
void M17Demodulator::do_lsf_sync()
{
float sync_triggered = 0.;
float bert_triggered = 0.;
if (correlator.index() == sample_index)
{
sync_triggered = preamble_sync.triggered(correlator);
if (sync_triggered > 0.1)
{
return;
}
sync_triggered = lsf_sync.triggered(correlator);
bert_triggered = packet_sync.triggered(correlator);
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;
}
else if (std::abs(sync_triggered) > 0.1)
{
missing_sync_count = 0;
need_clock_update_ = true;
update_values(sample_index);
if (sync_triggered > 0)
{
demodState = DemodState::FRAME;
sync_word_type = M17FrameDecoder::SyncWordType::LSF;
}
else
{
demodState = DemodState::FRAME;
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
}
}
else if (++missing_sync_count > 192)
{
demodState = DemodState::UNLOCKED;
decoder.reset();
missing_sync_count = 0;
}
else
{
update_values(sample_index);
}
}
}
/**
* Check for a stream sync word (LSF sync word that is maximally negative).
* We can enter DemodState::STREAM_SYNC from either a valid LSF decode for
* an audio stream, or from a stream frame decode.
*
*/
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)
{
update_values(sync_index);
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
demodState = DemodState::FRAME;
}
return;
}
else if (sync_count > 87)
{
update_values(sync_index);
missing_sync_count += 1;
if (missing_sync_count < MAX_MISSING_SYNC)
{
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
demodState = DemodState::FRAME;
}
else
{
// fputs("\n!SYNC\n", stderr);
demodState = DemodState::LSF_SYNC;
}
}
}
/**
* Check for a packet sync word. DemodState::PACKET_SYNC can only be
* entered from a valid LSF frame decode with the data/packet type bit set.
*/
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;
update_values(sync_index);
sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
demodState = DemodState::FRAME;
}
else if (sync_count > 87)
{
missing_sync_count += 1;
if (missing_sync_count < MAX_MISSING_SYNC)
{
sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
demodState = DemodState::FRAME;
}
else
{
demodState = DemodState::UNLOCKED;
decoder.reset();
}
}
}
/**
* Check for a bert sync word.
*/
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;
update_values(sync_index);
sync_word_type = M17FrameDecoder::SyncWordType::BERT;
demodState = DemodState::FRAME;
}
else if (sync_count > 87)
{
missing_sync_count += 1;
if (missing_sync_count < MAX_MISSING_SYNC)
{
sync_word_type = M17FrameDecoder::SyncWordType::BERT;
demodState = DemodState::FRAME;
}
else
{
demodState = DemodState::UNLOCKED;
decoder.reset();
}
}
}
void M17Demodulator::do_frame(float filtered_sample)
{
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)
{
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;
if (cost_count > 75)
{
cost_count = 0;
demodState = DemodState::UNLOCKED;
decoder.reset();
// fputs("\nCOST\n", stderr);
return;
}
switch (decoder.state())
{
case M17FrameDecoder::State::STREAM:
demodState = DemodState::STREAM_SYNC;
break;
case M17FrameDecoder::State::LSF:
// If state == LSF, we need to recover LSF from LICH.
demodState = DemodState::STREAM_SYNC;
break;
case M17FrameDecoder::State::BERT:
demodState = DemodState::BERT_SYNC;
break;
default:
demodState = DemodState::PACKET_SYNC;
break;
}
sync_count = 0;
switch (valid)
{
case M17FrameDecoder::DecodeResult::FAIL:
break;
case M17FrameDecoder::DecodeResult::EOS:
demodState = DemodState::LSF_SYNC;
break;
case M17FrameDecoder::DecodeResult::OK:
break;
case M17FrameDecoder::DecodeResult::INCOMPLETE:
break;
case M17FrameDecoder::DecodeResult::PACKET_INCOMPLETE:
break;
}
}
}
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]]
{
--initializing;
initialize(input);
count_ = 0;
return;
}
if (!dcd_)
{
if (count_ % (BLOCK_SIZE * 2) == 0)
{
update_dcd();
dcd.update();
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);
}
count_ = 0;
}
return;
}
auto filtered_sample = demod_filter(input);
correlator.sample(filtered_sample);
if (correlator.index() == 0)
{
if (need_clock_reset_)
{
clock_recovery.reset();
need_clock_reset_ = false;
}
else if (need_clock_update_) // must avoid update immediately after reset.
{
clock_recovery.update();
uint8_t clock_index = clock_recovery.sample_index();
uint8_t clock_diff = std::abs(sample_index - clock_index);
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.
need_clock_update_ = false;
}
}
clock_recovery(filtered_sample);
if (demodState != DemodState::UNLOCKED && correlator.index() == sample_index)
{
dev.sample(filtered_sample);
}
switch (demodState)
{
case DemodState::UNLOCKED:
// In this state, the sample_index is unknown. We need to find
// a sync word to find the proper sample_index. We only leave
// this state if we believe that we have a valid sample_index.
do_unlocked();
break;
case DemodState::LSF_SYNC:
do_lsf_sync();
break;
case DemodState::STREAM_SYNC:
do_stream_sync();
break;
case DemodState::PACKET_SYNC:
do_packet_sync();
break;
case DemodState::BERT_SYNC:
do_bert_sync();
break;
case DemodState::FRAME:
do_frame(filtered_sample);
break;
}
if (count_ % (BLOCK_SIZE * 5) == 0)
{
update_dcd();
count_ = 0;
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);
}
dcd.update();
}
}
} // mobilinkd

View File

@ -26,7 +26,6 @@ namespace detail
} // detail
template <typename FloatType>
struct M17Demodulator
{
static const uint16_t SAMPLE_RATE = 48000;
@ -34,15 +33,15 @@ struct M17Demodulator
static const uint16_t SAMPLES_PER_SYMBOL = SAMPLE_RATE / SYMBOL_RATE;
static const uint16_t BLOCK_SIZE = 192;
static constexpr FloatType sample_rate = SAMPLE_RATE;
static constexpr FloatType symbol_rate = SYMBOL_RATE;
static constexpr float sample_rate = SAMPLE_RATE;
static constexpr float symbol_rate = SYMBOL_RATE;
static const uint8_t MAX_MISSING_SYNC = 8;
using collelator_t = Correlator<FloatType>;
using collelator_t = Correlator;
using sync_word_t = SyncWord<collelator_t>;
using callback_t = M17FrameDecoder::callback_t;
using diagnostic_callback_t = std::function<void(bool, FloatType, FloatType, FloatType, int, FloatType, int, int, int, int)>;
using diagnostic_callback_t = std::function<void(bool, float, float, float, int, float, int, int, int, int)>;
enum class DemodState {
UNLOCKED,
@ -53,16 +52,16 @@ struct M17Demodulator
FRAME
};
DataCarrierDetect<FloatType, SAMPLE_RATE, 500> dcd{2500, 4000, 1.0, 4.0};
ClockRecovery<FloatType, SAMPLE_RATE, SYMBOL_RATE> clock_recovery;
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)
FreqDevEstimator<FloatType> dev;
FloatType idev;
FreqDevEstimator dev;
float idev;
size_t count_ = 0;
int8_t polarity = 1;
@ -91,14 +90,14 @@ struct M17Demodulator
void dcd_on();
void dcd_off();
void initialize(const FloatType input);
void initialize(const float input);
void update_dcd();
void do_unlocked();
void do_lsf_sync();
void do_packet_sync();
void do_stream_sync();
void do_bert_sync();
void do_frame(FloatType filtered_sample);
void do_frame(float filtered_sample);
bool locked() const
{
@ -118,472 +117,11 @@ struct M17Demodulator
void update_values(uint8_t index);
void operator()(const FloatType input);
void operator()(const float input);
private:
static const std::array<FloatType, 150> rrc_taps;
BaseFirFilter<FloatType, rrc_taps.size()> demod_filter{rrc_taps};
static const std::array<float, 150> rrc_taps;
BaseFirFilter<rrc_taps.size()> demod_filter{rrc_taps};
};
template <typename FloatType>
void M17Demodulator<FloatType>::update_values(uint8_t index)
{
correlator.apply([this,index](FloatType t){dev.sample(t);}, index);
dev.update();
sync_sample_index = index;
}
template <typename FloatType>
void M17Demodulator<FloatType>::dcd_on()
{
// Data carrier newly detected.
dcd_ = true;
sync_count = 0;
missing_sync_count = 0;
dev.reset();
framer.reset();
decoder.reset();
}
template <typename FloatType>
void M17Demodulator<FloatType>::dcd_off()
{
// Just lost data carrier.
dcd_ = false;
demodState = DemodState::UNLOCKED;
decoder.reset();
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);
}
}
template <typename FloatType>
void M17Demodulator<FloatType>::initialize(const FloatType input)
{
auto filtered_sample = demod_filter(input);
correlator.sample(filtered_sample);
}
template <typename FloatType>
void M17Demodulator<FloatType>::update_dcd()
{
if (!dcd_ && dcd.dcd())
{
// fputs("\nAOS\n", stderr);
dcd_on();
need_clock_reset_ = true;
}
else if (dcd_ && !dcd.dcd())
{
// fputs("\nLOS\n", stderr);
dcd_off();
}
}
template <typename FloatType>
void M17Demodulator<FloatType>::do_unlocked()
{
// We expect to find the preamble immediately after DCD.
if (missing_sync_count < 1920)
{
missing_sync_count += 1;
auto sync_index = preamble_sync(correlator);
auto sync_updated = preamble_sync.updated();
if (sync_updated)
{
sync_count = 0;
missing_sync_count = 0;
need_clock_reset_ = true;
dev.reset();
update_values(sync_index);
sample_index = sync_index;
demodState = DemodState::LSF_SYNC;
}
return;
}
auto sync_index = lsf_sync(correlator);
auto sync_updated = lsf_sync.updated();
if (sync_updated)
{
sync_count = 0;
missing_sync_count = 0;
need_clock_reset_ = true;
dev.reset();
update_values(sync_index);
sample_index = sync_index;
demodState = DemodState::FRAME;
if (sync_updated < 0)
{
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
}
else
{
sync_word_type = M17FrameDecoder::SyncWordType::LSF;
}
return;
}
sync_index = packet_sync(correlator);
sync_updated = packet_sync.updated();
if (sync_updated < 0)
{
sync_count = 0;
missing_sync_count = 0;
need_clock_reset_ = true;
dev.reset();
update_values(sync_index);
sample_index = sync_index;
demodState = DemodState::FRAME;
sync_word_type = M17FrameDecoder::SyncWordType::BERT;
}
}
/**
* Check for LSF sync word. We only enter the DemodState::LSF_SYNC state
* if a preamble sync has been detected, which also means that sample_index
* has been initialized to a sane value for the baseband.
*/
template <typename FloatType>
void M17Demodulator<FloatType>::do_lsf_sync()
{
FloatType sync_triggered = 0.;
FloatType bert_triggered = 0.;
if (correlator.index() == sample_index)
{
sync_triggered = preamble_sync.triggered(correlator);
if (sync_triggered > 0.1)
{
return;
}
sync_triggered = lsf_sync.triggered(correlator);
bert_triggered = packet_sync.triggered(correlator);
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;
}
else if (std::abs(sync_triggered) > 0.1)
{
missing_sync_count = 0;
need_clock_update_ = true;
update_values(sample_index);
if (sync_triggered > 0)
{
demodState = DemodState::FRAME;
sync_word_type = M17FrameDecoder::SyncWordType::LSF;
}
else
{
demodState = DemodState::FRAME;
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
}
}
else if (++missing_sync_count > 192)
{
demodState = DemodState::UNLOCKED;
decoder.reset();
missing_sync_count = 0;
}
else
{
update_values(sample_index);
}
}
}
/**
* Check for a stream sync word (LSF sync word that is maximally negative).
* We can enter DemodState::STREAM_SYNC from either a valid LSF decode for
* an audio stream, or from a stream frame decode.
*
*/
template <typename FloatType>
void M17Demodulator<FloatType>::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)
{
update_values(sync_index);
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
demodState = DemodState::FRAME;
}
return;
}
else if (sync_count > 87)
{
update_values(sync_index);
missing_sync_count += 1;
if (missing_sync_count < MAX_MISSING_SYNC)
{
sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
demodState = DemodState::FRAME;
}
else
{
// fputs("\n!SYNC\n", stderr);
demodState = DemodState::LSF_SYNC;
}
}
}
/**
* Check for a packet sync word. DemodState::PACKET_SYNC can only be
* entered from a valid LSF frame decode with the data/packet type bit set.
*/
template <typename FloatType>
void M17Demodulator<FloatType>::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;
update_values(sync_index);
sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
demodState = DemodState::FRAME;
}
else if (sync_count > 87)
{
missing_sync_count += 1;
if (missing_sync_count < MAX_MISSING_SYNC)
{
sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
demodState = DemodState::FRAME;
}
else
{
demodState = DemodState::UNLOCKED;
decoder.reset();
}
}
}
/**
* Check for a bert sync word.
*/
template <typename FloatType>
void M17Demodulator<FloatType>::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;
update_values(sync_index);
sync_word_type = M17FrameDecoder::SyncWordType::BERT;
demodState = DemodState::FRAME;
}
else if (sync_count > 87)
{
missing_sync_count += 1;
if (missing_sync_count < MAX_MISSING_SYNC)
{
sync_word_type = M17FrameDecoder::SyncWordType::BERT;
demodState = DemodState::FRAME;
}
else
{
demodState = DemodState::UNLOCKED;
decoder.reset();
}
}
}
template <typename FloatType>
void M17Demodulator<FloatType>::do_frame(FloatType filtered_sample)
{
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<FloatType, 4>(sample);
int8_t* tmp;
auto len = framer(n, &tmp);
if (len != 0)
{
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;
if (cost_count > 75)
{
cost_count = 0;
demodState = DemodState::UNLOCKED;
decoder.reset();
// fputs("\nCOST\n", stderr);
return;
}
switch (decoder.state())
{
case M17FrameDecoder::State::STREAM:
demodState = DemodState::STREAM_SYNC;
break;
case M17FrameDecoder::State::LSF:
// If state == LSF, we need to recover LSF from LICH.
demodState = DemodState::STREAM_SYNC;
break;
case M17FrameDecoder::State::BERT:
demodState = DemodState::BERT_SYNC;
break;
default:
demodState = DemodState::PACKET_SYNC;
break;
}
sync_count = 0;
switch (valid)
{
case M17FrameDecoder::DecodeResult::FAIL:
break;
case M17FrameDecoder::DecodeResult::EOS:
demodState = DemodState::LSF_SYNC;
break;
case M17FrameDecoder::DecodeResult::OK:
break;
case M17FrameDecoder::DecodeResult::INCOMPLETE:
break;
case M17FrameDecoder::DecodeResult::PACKET_INCOMPLETE:
break;
}
}
}
template <typename FloatType>
void M17Demodulator<FloatType>::operator()(const FloatType 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]]
{
--initializing;
initialize(input);
count_ = 0;
return;
}
if (!dcd_)
{
if (count_ % (BLOCK_SIZE * 2) == 0)
{
update_dcd();
dcd.update();
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);
}
count_ = 0;
}
return;
}
auto filtered_sample = demod_filter(input);
correlator.sample(filtered_sample);
if (correlator.index() == 0)
{
if (need_clock_reset_)
{
clock_recovery.reset();
need_clock_reset_ = false;
}
else if (need_clock_update_) // must avoid update immediately after reset.
{
clock_recovery.update();
uint8_t clock_index = clock_recovery.sample_index();
uint8_t clock_diff = std::abs(sample_index - clock_index);
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.
need_clock_update_ = false;
}
}
clock_recovery(filtered_sample);
if (demodState != DemodState::UNLOCKED && correlator.index() == sample_index)
{
dev.sample(filtered_sample);
}
switch (demodState)
{
case DemodState::UNLOCKED:
// In this state, the sample_index is unknown. We need to find
// a sync word to find the proper sample_index. We only leave
// this state if we believe that we have a valid sample_index.
do_unlocked();
break;
case DemodState::LSF_SYNC:
do_lsf_sync();
break;
case DemodState::STREAM_SYNC:
do_stream_sync();
break;
case DemodState::PACKET_SYNC:
do_packet_sync();
break;
case DemodState::BERT_SYNC:
do_bert_sync();
break;
case DemodState::FRAME:
do_frame(filtered_sample);
break;
}
if (count_ % (BLOCK_SIZE * 5) == 0)
{
update_dcd();
count_ = 0;
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);
}
dcd.update();
}
}
} // mobilinkd

View File

@ -595,7 +595,7 @@ public:
static baseband_t symbols_to_baseband(const symbols_t& symbols)
{
// Generated using scikit-commpy
static const auto rrc_taps = std::array<double, 79>{
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,
@ -617,7 +617,7 @@ public:
0.01679337935001369, 0.01505751553351295, 0.01071805138282269, 0.004891777252042491,
-0.001125978562075172, -0.006136551625729697, -0.009265784007800534
};
static BaseFirFilter<double, std::tuple_size<decltype(rrc_taps)>::value> rrc = makeFirFilter(rrc_taps);
static BaseFirFilter<std::tuple_size<decltype(rrc_taps)>::value> rrc = makeFirFilter(rrc_taps);
std::array<int16_t, 1920> baseband;
baseband.fill(0);

View File

@ -17,33 +17,32 @@ namespace mobilinkd
* these errors have not affected the performance of clock
* recovery.
*/
template <typename FloatType>
struct PhaseEstimator
{
using float_type = FloatType;
using samples_t = std::array<FloatType, 3>; // 3 samples in length
using float_type = float;
using samples_t = std::array<float, 3>; // 3 samples in length
float_type dx_;
PhaseEstimator(FloatType sample_rate, FloatType symbol_rate)
PhaseEstimator(float sample_rate, float symbol_rate)
: dx_(2.0 * symbol_rate / sample_rate)
{}
/**
* This performs a rolling estimate of the phase.
*
*
* @param samples are three samples centered around the current sample point
* (t-1, t, t+1).
*/
float_type operator()(const samples_t& samples)
{
assert(dx_ > 0.0);
auto ratio = ((samples.at(2) - samples.at(0)) / 3.0) / dx_;
float ratio = ((samples.at(2) - samples.at(0)) / 3.0) / dx_;
// Clamp +/-5.
ratio = std::min(FloatType(5.0), ratio);
ratio = std::max(FloatType(-5.0), ratio);
ratio = std::min(float(5.0), ratio);
ratio = std::max(float(-5.0), ratio);
return ratio;
}
};

View File

@ -17,18 +17,18 @@ namespace mobilinkd
* Eric Jacobsen, 2015-04-23
* https://www.dsprelated.com/showarticle/776.php
*/
template <typename FloatType, size_t SampleRate, size_t Frequency, size_t Accuracy = 1000>
template <size_t SampleRate, size_t Frequency, size_t Accuracy = 1000>
class SlidingDFT
{
using ComplexType = std::complex<FloatType>;
using ComplexType = std::complex<float>;
static constexpr size_t N = SampleRate / Accuracy;
static constexpr FloatType pi2 = M_PI * 2.0;
static constexpr FloatType kth = FloatType(Frequency) / FloatType(SampleRate);
static constexpr float pi2 = M_PI * 2.0;
static constexpr float kth = float(Frequency) / float(SampleRate);
// We'd like this to be static constexpr, but std::exp is not a constexpr.
const ComplexType coeff_;
std::array<FloatType, N> samples_;
std::array<float, N> samples_;
ComplexType result_{0,0};
size_t index_ = 0;
size_t prev_index_ = N - 1;
@ -40,15 +40,15 @@ public:
coeff_ = std::exp(-ComplexType{0, 1} * pi2 * kth);
}
ComplexType operator()(FloatType sample)
ComplexType operator()(float sample)
{
auto index = index_;
index_ += 1;
if (index_ == N) index_ = 0;
FloatType delta = sample - samples_[index];
float delta = sample - samples_[index];
ComplexType result = (result_ + delta) * coeff_;
result_ = result * FloatType(0.999999999999999);
result_ = result * float(0.999999999999999);
samples_[index] = sample;
prev_index_ = index;
return result;
@ -62,21 +62,21 @@ public:
* Eric Jacobsen, 2015-04-23
* https://www.dsprelated.com/showarticle/776.php
*
* @tparam FloatType is the floating point type to use.
* @tparam float is the floating point type to use.
* @tparam SampleRate is the sample rate of the incoming data.
* @tparam N is the length of the DFT. Frequency resolution is SampleRate / N.
* @tparam K is the number of frequencies whose DFT will be calculated.
*/
template <typename FloatType, size_t SampleRate, size_t N, size_t K>
template <size_t SampleRate, size_t N, size_t K>
class NSlidingDFT
{
using ComplexType = std::complex<FloatType>;
using ComplexType = std::complex<float>;
static constexpr FloatType pi2 = M_PI * 2.0;
static constexpr float pi2 = M_PI * 2.0;
// We'd like this to be static constexpr, but std::exp is not a constexpr.
const std::array<ComplexType, K> coeff_;
std::array<FloatType, N> samples_;
std::array<float, N> samples_;
std::array<ComplexType, K> result_{0,0};
size_t index_ = 0;
size_t prev_index_ = N - 1;
@ -88,7 +88,7 @@ class NSlidingDFT
std::array<ComplexType, K> result;
for (size_t i = 0; i != K; ++i)
{
FloatType k = FloatType(frequencies[i]) / FloatType(SampleRate);
float k = float(frequencies[i]) / float(SampleRate);
result[i] = std::exp(-j * pi2 * k);
}
return result;
@ -115,13 +115,13 @@ public:
* constructor. The result is only valid after at least N samples
* have been cycled in.
*/
result_type operator()(FloatType sample)
result_type operator()(float sample)
{
auto index = index_;
index_ += 1;
if (index_ == N) index_ = 0;
FloatType delta = sample - samples_[index];
float delta = sample - samples_[index];
for (size_t i = 0; i != K; ++i)
{

View File

@ -13,23 +13,23 @@
namespace mobilinkd
{
template <typename FloatType, size_t N>
template <size_t N>
struct SymbolEvm
{
using filter_type = BaseIirFilter<FloatType, N>;
using filter_type = BaseIirFilter<float, N>;
using symbol_t = int;
using result_type = std::tuple<symbol_t, FloatType>;
using result_type = std::tuple<symbol_t, float>;
filter_type filter_;
FloatType erasure_limit_;
FloatType evm_ = 0.0;
float erasure_limit_;
float evm_ = 0.0;
SymbolEvm(filter_type&& filter, FloatType erasure_limit = 0.0) :
SymbolEvm(filter_type&& filter, float erasure_limit = 0.0) :
filter_(std::forward<filter_type>(filter)),
erasure_limit_(erasure_limit)
{}
FloatType evm() const { return evm_; }
float evm() const { return evm_; }
/**
* Decode a normalized sample into a symbol. Symbols
@ -37,10 +37,10 @@ struct SymbolEvm
* is set, symbols outside this limit are 'erased' and
* returned as 0.
*/
result_type operator()(FloatType sample)
result_type operator()(float sample)
{
symbol_t symbol;
FloatType evm;
float evm;
sample = std::min(3.0, std::max(-3.0, sample));
@ -73,13 +73,13 @@ struct SymbolEvm
}
};
template <typename FloatType, size_t N>
SymbolEvm<FloatType, N> makeSymbolEvm(
BaseIirFilter<FloatType, N>&& filter,
FloatType erasure_limit = 0.0f
template <size_t N>
SymbolEvm<N> makeSymbolEvm(
BaseIirFilter<N>&& filter,
float erasure_limit = 0.0f
)
{
return std::move(SymbolEvm<FloatType, N>(std::move(filter), erasure_limit));
return std::move(SymbolEvm<float, N>(std::move(filter), erasure_limit));
}
} // mobilinkd

View File

@ -59,19 +59,19 @@ constexpr size_t llr_size()
return llr_limit<N>() * 6 + 1;
}
template<typename FloatType, size_t LLR>
constexpr std::array<std::tuple<FloatType, std::tuple<int8_t, int8_t>>, llr_size<LLR>()> make_llr_map()
template<size_t LLR>
constexpr std::array<std::tuple<float, std::tuple<int8_t, int8_t>>, llr_size<LLR>()> make_llr_map()
{
constexpr size_t size = llr_size<LLR>();
std::array<std::tuple<FloatType, std::tuple<int8_t, int8_t>>, size> result;
std::array<std::tuple<float, std::tuple<int8_t, int8_t>>, size> result;
constexpr int8_t limit = llr_limit<LLR>();
constexpr FloatType inc = 1.0 / FloatType(limit);
constexpr float inc = 1.0 / float(limit);
int8_t i = limit;
int8_t j = limit;
// Output must be ordered by k, ascending.
FloatType k = -3.0 + inc;
float k = -3.0 + inc;
for (size_t index = 0; index != size; ++index)
{
auto& a = result[index];
@ -124,17 +124,17 @@ inline int from_4fsk(int symbol)
}
}
template <typename FloatType, size_t LLR>
auto llr(FloatType sample)
template <size_t LLR>
auto llr(float sample)
{
static auto symbol_map = detail::make_llr_map<FloatType, LLR>();
static constexpr FloatType MAX_VALUE = 3.0;
static constexpr FloatType MIN_VALUE = -3.0;
static auto symbol_map = detail::make_llr_map<LLR>();
static constexpr float MAX_VALUE = 3.0;
static constexpr float MIN_VALUE = -3.0;
FloatType s = std::min(MAX_VALUE, std::max(MIN_VALUE, sample));
float s = std::min(MAX_VALUE, std::max(MIN_VALUE, sample));
auto it = std::lower_bound(symbol_map.begin(), symbol_map.end(), s,
[](std::tuple<FloatType, std::tuple<int8_t, int8_t>> const& e, FloatType s){
[](std::tuple<float, std::tuple<int8_t, int8_t>> const& e, float s){
return std::get<0>(e) < s;
});

View File

@ -96,7 +96,7 @@ private:
bool m_noiseBlanker;
struct CODEC2 *m_codec2;
static M17DemodProcessor *m_this;
mobilinkd::M17Demodulator<float> m_demod;
mobilinkd::M17Demodulator m_demod;
AudioFifo *m_audioFifo;
bool m_audioMute;
AudioVector m_audioBuffer;