40 void push(
float value, uint16_t capacity) {
41 if (capacity == 0U ||
ring ==
nullptr) {
49 if (std::isfinite(value)) {
54 if (
index >= capacity) {
67 if (
ring !=
nullptr && capacity > 0U) {
68 std::memset(
ring, 0, capacity *
sizeof(
float));
78 : capacity_(other.capacity_),
80 profile_index_(other.profile_index_),
81 storage_(other.storage_),
82 lagged_(other.lagged_),
83 adjacent_(other.adjacent_),
84 hampel_state_(other.hampel_state_),
85 hampel_adjacent_(other.hampel_adjacent_) {
86 std::memcpy(profile_ring_, other.profile_ring_,
sizeof(profile_ring_));
87 std::memcpy(profile_len_, other.profile_len_,
sizeof(profile_len_));
89 other.profile_index_ = 0U;
90 other.storage_ =
nullptr;
97 capacity_ = other.capacity_;
98 std::memcpy(profile_ring_, other.profile_ring_,
sizeof(profile_ring_));
99 std::memcpy(profile_len_, other.profile_len_,
sizeof(profile_len_));
101 profile_index_ = other.profile_index_;
102 storage_ = other.storage_;
103 lagged_ = other.lagged_;
104 adjacent_ = other.adjacent_;
105 hampel_state_ = other.hampel_state_;
106 hampel_adjacent_ = other.hampel_adjacent_;
107 other.capacity_ = 0U;
108 other.profile_index_ = 0U;
109 other.storage_ =
nullptr;
140 std::memset(profile_ring_, 0,
sizeof(profile_ring_));
141 std::memset(profile_len_, 0,
sizeof(profile_len_));
142 lagged_.clear(capacity_);
143 adjacent_.clear(capacity_);
147 hampel_state_.threshold, hampel_state_.enabled);
149 hampel_state_.threshold, hampel_state_.enabled);
153 void process(
const float *amplitudes, uint8_t amplitude_count) {
155 amplitudes !=
nullptr && amplitude_count >= 2U
161 void process(
const float *amplitudes, uint8_t amplitude_count,
162 float amplitude_mean) {
163 if (capacity_ == 0U) {
168 const float *reference = profile_ring_[profile_index_];
169 const uint8_t reference_len = profile_len_[profile_index_];
172 const uint16_t previous_index =
173 profile_index_ > 0U ?
static_cast<uint16_t
>(profile_index_ - 1U)
174 :
static_cast<uint16_t
>(lag_ - 1U);
175 const float *previous = profile_ring_[previous_index];
176 const uint8_t previous_len = profile_len_[previous_index];
177 uint8_t profile_len = 0U;
178 float lagged_value = std::numeric_limits<float>::quiet_NaN();
179 float adjacent_value = std::numeric_limits<float>::quiet_NaN();
181 if (amplitudes !=
nullptr && amplitude_count >= 2U &&
184 amplitudes, amplitude_count, amplitude_mean, profile);
185 const bool lagged = profile_len > 0U && reference_len == profile_len;
186 const bool adjacent = profile_len > 0U && previous_len == profile_len;
187 if (lagged || adjacent) {
189 float lagged_sum = 0.0f;
190 float adjacent_sum = 0.0f;
191 for (uint8_t i = 0U; i < profile_len; i++) {
193 lagged_sum += std::fabs(profile[i] - reference[i]);
196 adjacent_sum += std::fabs(profile[i] - previous[i]);
201 &hampel_state_, lagged_sum / profile_len);
205 &hampel_adjacent_, adjacent_sum / profile_len);
210 std::memcpy(profile_ring_[profile_index_], profile, profile_len *
sizeof(
float));
211 profile_len_[profile_index_] = profile_len;
213 if (profile_index_ >= lag_) {
216 lagged_.push(lagged_value, capacity_);
217 adjacent_.push(adjacent_value, capacity_);
221 const float missing = std::numeric_limits<float>::quiet_NaN();
222 for (uint32_t slot = 0U; slot <
count; ++slot) {
223 profile_len_[profile_index_] = 0U;
225 if (profile_index_ >= lag_) profile_index_ = 0U;
226 lagged_.push(missing, capacity_);
227 adjacent_.push(missing, capacity_);
231 uint16_t
count()
const {
return lagged_.count; }
232 float mean()
const {
return lagged_.mean(); }
243 const float adjacent_mean = adjacent_.mean();
244 if (lagged_.count == 0U || adjacent_.count == 0U || adjacent_mean <= 0.0f) {
247 return lagged_.mean() / adjacent_mean;
251 if (out ==
nullptr || capacity_ == 0U || lagged_.count == 0U) {
254 const uint16_t slots = lagged_.slots;
255 uint16_t source = slots < capacity_ ? 0U : lagged_.index;
256 uint16_t written = 0U;
257 for (uint16_t offset = 0U; offset < slots; ++offset) {
258 const float value = lagged_.ring[source];
259 if (std::isfinite(value)) out[written++] = value;
261 if (source >= capacity_) source = 0U;
267 void allocate_delta_ring_(uint16_t capacity) {
268 if (capacity == capacity_ && (capacity == 0U || storage_ !=
nullptr)) {
274 lagged_ = L1DeltaWindow{};
275 adjacent_ = L1DeltaWindow{};
276 if (capacity == 0U) {
280 float* block =
new (std::nothrow)
float[2U *
static_cast<size_t>(capacity)];
281 if (block ==
nullptr) {
285 capacity_ = capacity;
286 lagged_.
ring = block;
287 adjacent_.ring = block + capacity;
290 uint16_t capacity_{0U};
294 uint16_t profile_index_{0U};
295 float* storage_{
nullptr};
296 L1DeltaWindow lagged_{};
297 L1DeltaWindow adjacent_{};
L1DeltaTracker(const L1DeltaTracker &)=delete
void configure(uint16_t capacity, uint16_t lag=L1_DELTA_LAG)
uint16_t build_series(float *out) const
void process(const float *amplitudes, uint8_t amplitude_count)
L1DeltaTracker(L1DeltaTracker &&other) noexcept
L1DeltaTracker & operator=(L1DeltaTracker &&other) noexcept
void configure_hampel(bool enabled, uint8_t window_size=HAMPEL_TURBULENCE_WINDOW_DEFAULT, float threshold=HAMPEL_TURBULENCE_THRESHOLD_DEFAULT)
float delta_lag_ratio() const
Mean lagged displacement over mean adjacent displacement.
void advance_missing_slots(uint32_t count)
L1DeltaTracker & operator=(const L1DeltaTracker &)=delete
void process(const float *amplitudes, uint8_t amplitude_count, float amplitude_mean)
constexpr uint8_t L1_DELTA_LAG
float hampel_filter_turbulence(hampel_turbulence_state_t *state, float turbulence)
constexpr uint8_t HAMPEL_TURBULENCE_WINDOW_MIN
constexpr uint8_t HAMPEL_TURBULENCE_WINDOW_DEFAULT
constexpr uint8_t HT20_SELECTED_BAND_SIZE
constexpr uint16_t L1_DELTA_LAG_MAX
float calculate_mean(const float *values, size_t n)
Calculate mean of an array.
constexpr float HAMPEL_TURBULENCE_THRESHOLD_DEFAULT
hampel_turbulence_state_t hampel_filter_state_t
uint8_t normalize_amplitude_profile(const float *amplitudes, uint8_t count, float mean, float *out)
Write the mean-normalized amplitude profile into out.
void hampel_turbulence_init(hampel_turbulence_state_t *state, uint8_t window_size, float threshold, bool enabled)
constexpr uint16_t DETECTOR_MAX_WINDOW_SIZE
One sliding window of displacements over a caller-owned ring.
void clear(uint16_t capacity)
void push(float value, uint16_t capacity)