example_waste_level.c
Go to the documentation of this file.
1 // Copyright (c) Acconeer AB, 2024-2025
2 // All rights reserved
3 // This file is subject to the terms and conditions defined in the file
4 // 'LICENSES/license_acconeer.txt', (BSD 3-Clause License) which is part
5 // of this source code package.
6
7 #include <complex.h>
8 #include <inttypes.h>
9 #include <math.h>
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <string.h>
15
16 #include "acc_algorithm.h"
17 #include "acc_config.h"
18 #include "acc_config_subsweep.h"
19 #include "acc_definitions_common.h"
20 #include "acc_integration.h"
21 #include "acc_integration_log.h"
22 #include "acc_processing.h"
23 #include "example_waste_level.h"
24
25 #define MODULE "example_waste_level"
26
27 struct waste_level_handle
28 {
29 /** State. Affects output between frames */
30 struct
31 {
32 /** Ring buffer for distance estimations. Has capacity processing_config.median_filter_len */
33 float *distance_history_points;
34
35 /** How many valid elements there are in distance_history_points (starting at 0) */
36 uint16_t distance_history_len;
37
38 /** Write index for the distance_history_points */
39 uint16_t distance_history_write_idx;
40 } state;
41
42 /** Float scratch. Has length == max(sweeps_per_frame, median_filter_len) */
43 float *scratch;
44 };
45
46 /**
47 * @brief Validate an app_config (processing & sensor config).
48 *
49 * @param[in] app_config The app config
50 * @return True if config was valid, false otherwise.
51 */
52 static bool validate_app_config(const waste_level_app_config_t *app_config);
53
54 /**
55 * @brief Helper function for validation
56 *
57 * @param[in] sensor_config The sensor config
58 * @return True if all subsweeps' start_points are strictly increasing, false otherwise
59 */
60 static bool subsweep_start_points_strictly_increases(const acc_config_t *sensor_config);
61
62 /**
63 * @brief Helper function for validation
64 *
65 * @param[in] sensor_config The sensor config
66 * @param[out] offending_subsweep The subsweep index of a subsweep that overlaps into
67 * the subsweep after it
68 * @return True if sensor config has overlapping (in distance) subsweeps, false otherwise
69 */
70 static bool has_overlapping_subsweeps(const acc_config_t *sensor_config, uint8_t *offending_subsweep);
71
72 /**
73 * @brief Helper function for validation
74 *
75 * @param[in] sensor_config The sensor config
76 * @return True if start_point for subsweep 0 is invalid
77 */
78 static bool is_invalid_start_point(const acc_config_t *sensor_config);
79
80 /**
81 * @brief Helper function for validation
82 *
83 * @param[in] sensor_config The sensor config
84 * @param[in] distance_m The distance to check
85 * @return True if distance_m is outside the interval measured by sensor_config, false otherwise
86 */
87 static bool is_outside_measuring_interval(const acc_config_t *sensor_config, float distance_m);
88
89 /**
90 * @brief Helper function for validation
91 *
92 * @param[in] sensor_config The sensor config
93 * @param[in] subsweep_idx The distance to check
94 * @return The approximate distance to the first point measured in subsweep with index subsweep_idx
95 */
96 static float subsweep_approx_start_m(const acc_config_t *sensor_config, uint8_t subsweep_idx);
97
98 /**
99 * @brief Helper function for validation
100 *
101 * @param[in] sensor_config The sensor config
102 * @param[in] subsweep_idx The distance to check
103 * @return The approximate distance to the last point measured in subsweep with index subsweep_idx
104 */
105 static float subsweep_approx_end_m(const acc_config_t *sensor_config, uint8_t subsweep_idx);
106
107 /**
108 * @brief Calculate a point's corresponding distance in meters
109 *
110 * @param[in] sensor_config The sensor config
111 * @param[in] metadata The processing metadata
112 * @param[in] point_idx The index in a sweep of the point to get distance for
113 * @return The distance of point at point_idx
114 */
115 static float point_in_sweep_to_distance_m(const acc_config_t *sensor_config, const acc_processing_metadata_t *metadata, uint16_t point_idx);
116
117 /**
118 * @brief Write next value into a ring buffer
119 *
120 * @param[in, out] buffer The circular buffer
121 * @param[in] capacity The allocated number of elements of buffer
122 * @param[in, out] length The buffer length (number of valid elements)
123 * @param[in, out] write_idx The next index in buffer to write to
124 * @param[in] elem The new element to write
125 */
126 static void write_to_ring_buffer(float *buffer, uint16_t capacity, uint16_t *length, uint16_t *write_idx, float elem);
127
128 /**
129 * @brief Copy non-NaN floats between buffers
130 *
131 * @param[in] in Source buffer of floats which can contain NaNs
132 * @param[out] out The destination buffer. Will not contain NaNs
133 * @param[in] length The allocated size of in & out
134 * @return The number of elements written to out
135 */
136 static uint16_t copy_non_nan_floats(const float *in, float *out, uint16_t length);
137
138 /**
139 * @brief Populate a waste level result with its human-readable entries
140 *
141 * @param[in] filtered_distance The median filtered distance
142 * @param[in] processing_config The waste processing config
143 * @param[out] result The waste result
144 */
145 static void set_level_numbers(float filtered_distance, const waste_level_processing_config_t *processing_config, waste_level_result_t *result);
146
147 waste_level_app_config_t *waste_level_app_config_create(void)
148 {
149 waste_level_app_config_t *app_config = acc_integration_mem_alloc(sizeof(*app_config));
150 bool status = app_config != NULL;
151
152 if (status)
153 {
154 app_config->sensor_config = acc_config_create();
155 status = app_config->sensor_config != NULL;
156 }
157
158 if (status)
159 {
160 waste_level_app_config_set_preset(WASTE_LEVEL_PRESET_PLASTIC_WASTE_BIN, app_config);
161 }
162
163 return app_config;
164 }
165
166 void waste_level_app_config_destroy(waste_level_app_config_t *app_config)
167 {
168 if (app_config != NULL)
169 {
170 if (app_config->sensor_config != NULL)
171 {
172 acc_config_destroy(app_config->sensor_config);
173 }
174
175 acc_integration_mem_free(app_config);
176 }
177 }
178
179 void waste_level_app_config_set_preset(waste_level_preset_t preset, waste_level_app_config_t *app_config)
180 {
181 bool status = app_config != NULL;
182
183 if (status)
184 {
185 switch (preset)
186 {
187 case WASTE_LEVEL_PRESET_PLASTIC_WASTE_BIN:
188 app_config->processing_config.bin_start_m = 0.15f;
189 app_config->processing_config.bin_end_m = 1.00f;
190 app_config->processing_config.threshold = 0.30f;
191 app_config->processing_config.distance_sequence_len = 4U;
192 app_config->processing_config.median_filter_len = 5U;
193
194 acc_config_frame_rate_set(app_config->sensor_config, 5.0f);
195 acc_config_sweeps_per_frame_set(app_config->sensor_config, 32U);
196 acc_config_num_subsweeps_set(app_config->sensor_config, 2U);
197
198 acc_config_subsweep_start_point_set(app_config->sensor_config, 40U, 0U);
199 acc_config_subsweep_step_length_set(app_config->sensor_config, 8U, 0U);
200 acc_config_subsweep_num_points_set(app_config->sensor_config, 14U, 0U);
201 acc_config_subsweep_profile_set(app_config->sensor_config, ACC_CONFIG_PROFILE_1, 0U);
202 acc_config_subsweep_hwaas_set(app_config->sensor_config, 4U, 0U);
203
204 acc_config_subsweep_start_point_set(app_config->sensor_config, 150U, 1U);
205 acc_config_subsweep_step_length_set(app_config->sensor_config, 12U, 1U);
206 acc_config_subsweep_num_points_set(app_config->sensor_config, 23U, 1U);
207 acc_config_subsweep_profile_set(app_config->sensor_config, ACC_CONFIG_PROFILE_3, 1U);
208 acc_config_subsweep_hwaas_set(app_config->sensor_config, 8U, 1U);
209 break;
210 default:
211 case WASTE_LEVEL_PRESET_NONE:
212 break;
213 }
214 }
215 }
216
217 waste_level_handle_t *waste_level_handle_create(const waste_level_app_config_t *app_config)
218 {
219 uint16_t sweeps_per_frame = 0U;
220 uint16_t median_filter_len = 0U;
221 waste_level_handle_t *handle = NULL;
222
223 bool status = validate_app_config(app_config);
224
225 if (status)
226 {
227 handle = acc_integration_mem_alloc(sizeof(*handle));
228 status = handle != NULL;
229 }
230
231 if (status)
232 {
233 sweeps_per_frame = acc_config_sweeps_per_frame_get(app_config->sensor_config);
234 median_filter_len = app_config->processing_config.median_filter_len;
235
236 handle->state.distance_history_len = 0U;
237 handle->state.distance_history_write_idx = 0U;
238 }
239
240 if (status)
241 {
242 uint16_t num_elem = sweeps_per_frame > median_filter_len ? sweeps_per_frame : median_filter_len;
243 handle->scratch = (float *)acc_integration_mem_alloc(num_elem * sizeof(*handle->scratch));
244 status = handle->scratch != NULL;
245 }
246
247 if (status)
248 {
249 handle->state.distance_history_points =
250 (float *)acc_integration_mem_alloc(median_filter_len * sizeof(*handle->state.distance_history_points));
251 status = handle->state.distance_history_points != NULL;
252 }
253
254 return handle;
255 }
256
257 void waste_level_handle_destroy(waste_level_handle_t *handle)
258 {
259 if (handle != NULL)
260 {
261 if (handle->state.distance_history_points != NULL)
262 {
263 acc_integration_mem_free(handle->state.distance_history_points);
264 }
265
266 if (handle->scratch != NULL)
267 {
268 acc_integration_mem_free(handle->scratch);
269 }
270
271 acc_integration_mem_free(handle);
272 }
273 }
274
275 void waste_level_process(waste_level_handle_t *handle,
276 const waste_level_app_config_t *app_config,
277 const acc_processing_metadata_t *metadata,
278 const acc_int16_complex_t *frame,
279 waste_level_result_t *waste_level_result)
280 {
281 if (handle != NULL && app_config != NULL && metadata != NULL && frame != NULL && waste_level_result != NULL)
282 {
283 uint16_t sweep_length = metadata->sweep_data_length;
284 uint16_t sweeps_per_frame = acc_config_sweeps_per_frame_get(app_config->sensor_config);
285 uint16_t distance_seq_len = app_config->processing_config.distance_sequence_len;
286 uint16_t median_filter_len = app_config->processing_config.median_filter_len;
287 float threshold_squared = app_config->processing_config.threshold * app_config->processing_config.threshold;
288
289 uint16_t phase_vars_under_threshold = 0U;
290
291 bool point_of_waste_found = false;
292 uint16_t point_of_waste = 0U;
293
294 for (uint16_t point_idx = 0U; point_idx < sweep_length; point_idx++)
295 {
296 float complex column_sum;
297
298 acc_algorithm_sum_sweep(frame, sweep_length, sweeps_per_frame, point_idx, point_idx + 1U, &column_sum);
299
300 acc_algorithm_conj_f32(&column_sum, 1U);
301
302 for (uint16_t sweep_idx = 0U; sweep_idx < sweeps_per_frame; sweep_idx++)
303 {
304 uint16_t frame_idx = sweep_idx * sweep_length + point_idx;
305 acc_int16_complex_t point = frame[frame_idx];
306 float complex float_point = (float)point.real + (float)point.imag * I;
307
308 handle->scratch[sweep_idx] = cargf(float_point * column_sum);
309 }
310
311 float point_phase_variance = acc_algorithm_variance_f32(handle->scratch, sweeps_per_frame);
312
313 if (point_phase_variance < threshold_squared)
314 {
315 phase_vars_under_threshold++;
316 }
317 else
318 {
319 phase_vars_under_threshold = 0U;
320 }
321
322 if (phase_vars_under_threshold >= distance_seq_len)
323 {
324 point_of_waste = point_idx - (phase_vars_under_threshold - 1U);
325 point_of_waste_found = true;
326 break;
327 }
328 }
329
330 float new_distance = !point_of_waste_found ? NAN : point_in_sweep_to_distance_m(app_config->sensor_config, metadata, point_of_waste);
331
332 write_to_ring_buffer(handle->state.distance_history_points,
333 median_filter_len,
334 &handle->state.distance_history_len,
335 &handle->state.distance_history_write_idx,
336 new_distance);
337
338 // copy non-NaN distance history points as median sorts inplace
339 uint16_t num_normal = copy_non_nan_floats(handle->state.distance_history_points, handle->scratch, handle->state.distance_history_len);
340 if (num_normal > 0U)
341 {
342 waste_level_result->level_found = true;
343
344 float median_distance = acc_algorithm_median_f32(handle->scratch, num_normal);
345 set_level_numbers(median_distance, &app_config->processing_config, waste_level_result);
346 }
347 else
348 {
349 waste_level_result->level_found = false;
350 }
351 }
352 }
353
354 void waste_level_processing_config_log(const waste_level_processing_config_t *config)
355 {
356 printf("Waste level config:\n");
357 printf("bin_start_m: %" PRIfloat "\n", ACC_LOG_FLOAT_TO_INTEGER(config->bin_start_m));
358 printf("bin_end_m: %" PRIfloat "\n", ACC_LOG_FLOAT_TO_INTEGER(config->bin_end_m));
359 printf("threshold: %" PRIfloat "\n", ACC_LOG_FLOAT_TO_INTEGER(config->threshold));
360 printf("distance_sequence_len: %" PRIu16 "\n", config->distance_sequence_len);
361 printf("median_filter_len: %" PRIu16 "\n", config->median_filter_len);
362 }
363
364 static bool validate_app_config(const waste_level_app_config_t *app_config)
365 {
366 const waste_level_processing_config_t *processing_config;
367 const acc_config_t *sensor_config;
368
369 bool status = app_config != NULL;
370
371 if (status)
372 {
373 sensor_config = app_config->sensor_config;
374 processing_config = &app_config->processing_config;
375 status = sensor_config != NULL && processing_config != NULL;
376 }
377
378 if (status && processing_config->bin_start_m < 0.08f)
379 {
380 fprintf(stderr, "Bin start needs to be greater or equal than 0.08m\n");
381 status = false;
382 }
383
384 if (status && processing_config->bin_end_m < processing_config->bin_start_m)
385 {
386 fprintf(stderr, "Bin end needs to be greater than bin start\n");
387 status = false;
388 }
389
390 if (status && (processing_config->threshold < 0.0f || processing_config->threshold > 4.0f))
391 {
392 fprintf(stderr, "Threshold shoud be in the range [0.0, 4.0], was %" PRIfloat "\n", ACC_LOG_FLOAT_TO_INTEGER(processing_config->threshold));
393 status = false;
394 }
395
396 if (status && (processing_config->distance_sequence_len > 10U || processing_config->distance_sequence_len < 1U))
397 {
398 fprintf(stderr, "Distance sequence length shoud be in the range [1, 10], was %" PRIu16 "\n", processing_config->distance_sequence_len);
399 status = false;
400 }
401
402 if (status && (processing_config->median_filter_len > 10U || processing_config->median_filter_len < 1U))
403 {
404 fprintf(stderr, "Median filter length shoud be in the range [1, 10], was %" PRIu16 "\n", processing_config->median_filter_len);
405 status = false;
406 }
407
408 if (status && !subsweep_start_points_strictly_increases(sensor_config))
409 {
410 fprintf(stderr, "Subsweeps with higher indexes should measure farther away than those with lower indexes\n");
411 status = false;
412 }
413
414 if (status)
415 {
416 uint8_t subsweep_idx;
417
418 if (has_overlapping_subsweeps(sensor_config, &subsweep_idx))
419 {
420 fprintf(stderr, "Disallowed range overlap between susweeps %" PRIu16 " and %" PRIu16 "\n", subsweep_idx, subsweep_idx + 1U);
421 status = false;
422 }
423 }
424
425 if (status && is_invalid_start_point(sensor_config))
426 {
427 fprintf(stderr, "Start point must be greater or equal than 32\n");
428 status = false;
429 }
430
431 if (status && is_outside_measuring_interval(sensor_config, processing_config->bin_start_m))
432 {
433 fprintf(stderr, "Bin start is outside measuring interval\n");
434 status = false;
435 }
436
437 if (status && is_outside_measuring_interval(sensor_config, processing_config->bin_end_m))
438 {
439 fprintf(stderr, "Bin end is outside measuring interval\n");
440 status = false;
441 }
442
443 if (status && acc_config_sweeps_per_frame_get(sensor_config) <= 3)
444 {
445 fprintf(stderr, "Sweeps per frame needs to be greater than 3\n");
446 status = false;
447 }
448
449 return status;
450 }
451
452 static bool subsweep_start_points_strictly_increases(const acc_config_t *sensor_config)
453 {
454 bool increasing = true;
455
456 uint8_t num_subsweeps = acc_config_num_subsweeps_get(sensor_config);
457
458 for (uint8_t subsweep_idx = 1U; subsweep_idx < num_subsweeps; subsweep_idx++)
459 {
460 int32_t prev_start = acc_config_subsweep_start_point_get(sensor_config, subsweep_idx - 1U);
461 int32_t curr_start = acc_config_subsweep_start_point_get(sensor_config, subsweep_idx);
462
463 if (curr_start <= prev_start)
464 {
465 increasing = false;
466 break;
467 }
468 }
469
470 return increasing;
471 }
472
473 static bool has_overlapping_subsweeps(const acc_config_t *sensor_config, uint8_t *offending_subsweep)
474 {
475 bool overlap_found = false;
476 uint8_t num_subsweeps = acc_config_num_subsweeps_get(sensor_config);
477
478 for (uint8_t subsweep_idx = 0U; subsweep_idx < num_subsweeps - 1U; subsweep_idx++)
479 {
480 float curr_end = subsweep_approx_end_m(sensor_config, subsweep_idx);
481 float next_start = subsweep_approx_start_m(sensor_config, subsweep_idx + 1U);
482
483 if (curr_end >= next_start)
484 {
485 *offending_subsweep = subsweep_idx;
486 overlap_found = true;
487 break;
488 }
489 }
490
491 return overlap_found;
492 }
493
494 static bool is_invalid_start_point(const acc_config_t *sensor_config)
495 {
496 const int32_t minimum_allowed_start_point = 32;
497
498 int32_t min_start_point = acc_config_subsweep_start_point_get(sensor_config, 0U);
499
500 return min_start_point < minimum_allowed_start_point;
501 }
502
503 static bool is_outside_measuring_interval(const acc_config_t *sensor_config, float distance_m)
504 {
505 float min_measured_point_m = 1000.0f;
506 float max_measured_point_m = -1000.0f;
507
508 uint8_t num_subsweeps = acc_config_num_subsweeps_get(sensor_config);
509
510 for (uint8_t subsweep_idx = 0U; subsweep_idx < num_subsweeps; subsweep_idx++)
511 {
512 float current_begin_m = subsweep_approx_start_m(sensor_config, subsweep_idx);
513 float current_end_m = subsweep_approx_end_m(sensor_config, subsweep_idx);
514
515 if (current_begin_m < min_measured_point_m)
516 {
517 min_measured_point_m = current_begin_m;
518 }
519
520 if (current_end_m > max_measured_point_m)
521 {
522 max_measured_point_m = current_end_m;
523 }
524 }
525
526 return distance_m < min_measured_point_m || distance_m > max_measured_point_m;
527 }
528
529 static float subsweep_approx_start_m(const acc_config_t *sensor_config, uint8_t subsweep_idx)
530 {
531 return acc_algorithm_get_distance_m(acc_config_subsweep_step_length_get(sensor_config, subsweep_idx),
532 acc_config_subsweep_start_point_get(sensor_config, subsweep_idx),
533 ACC_APPROX_BASE_STEP_LENGTH_M,
534 0U);
535 }
536
537 static float subsweep_approx_end_m(const acc_config_t *sensor_config, uint8_t subsweep_idx)
538 {
539 return acc_algorithm_get_distance_m(acc_config_subsweep_step_length_get(sensor_config, subsweep_idx),
540 acc_config_subsweep_start_point_get(sensor_config, subsweep_idx),
541 ACC_APPROX_BASE_STEP_LENGTH_M,
542 acc_config_subsweep_num_points_get(sensor_config, subsweep_idx) - 1U);
543 }
544
545 static float point_in_sweep_to_distance_m(const acc_config_t *sensor_config, const acc_processing_metadata_t *metadata, uint16_t point_idx)
546 {
547 uint8_t num_subsweeps = acc_config_num_subsweeps_get(sensor_config);
548 float distance_m = 0.0f;
549
550 for (uint8_t subsweep_idx = 0U; subsweep_idx < num_subsweeps; subsweep_idx++)
551 {
552 uint16_t start = metadata->subsweep_data_offset[subsweep_idx];
553 uint16_t end = start + metadata->subsweep_data_length[subsweep_idx];
554
555 if (point_idx >= start && point_idx < end)
556 {
557 distance_m = acc_algorithm_get_distance_m(acc_config_subsweep_step_length_get(sensor_config, subsweep_idx),
558 acc_config_subsweep_start_point_get(sensor_config, subsweep_idx),
559 acc_processing_points_to_meter(1),
560 point_idx - start);
561 break;
562 }
563 }
564
565 return distance_m;
566 }
567
568 static void write_to_ring_buffer(float *buffer, uint16_t capacity, uint16_t *length, uint16_t *write_idx, float elem)
569 {
570 buffer[*write_idx] = elem;
571
572 *write_idx = (*write_idx + 1U) % capacity;
573
574 if (*length < capacity)
575 {
576 *length = *length + 1U;
577 }
578 }
579
580 static uint16_t copy_non_nan_floats(const float *in, float *out, uint16_t length)
581 {
582 uint16_t num_normal = 0U;
583
584 for (uint16_t i = 0U; i < length; i++)
585 {
586 if (fpclassify(in[i]) != FP_NAN)
587 {
588 out[num_normal] = in[i];
589 num_normal++;
590 }
591 }
592
593 return num_normal;
594 }
595
596 static void set_level_numbers(float filtered_distance, const waste_level_processing_config_t *processing_config, waste_level_result_t *result)
597 {
598 float bin_end_m = processing_config->bin_end_m;
599 float bin_start_m = processing_config->bin_start_m;
600
601 float fill_level_m = bin_end_m - filtered_distance;
602 float fill_level_percent_f = acc_algorithm_clip_f32(100.0f * fill_level_m / (bin_end_m - bin_start_m), 0.0f, 100.0f);
603
604 fill_level_percent_f += 0.5f; // for rounding
605 uint8_t fill_level_percent = (uint8_t)fill_level_percent_f;
606
607 result->level_m = fill_level_m;
608 result->level_percent = fill_level_percent;
609 }
waste_level_processing_config_t::distance_sequence_len
uint16_t distance_sequence_len
Definition: example_waste_level.h:36
waste_level_app_config_destroy
void waste_level_app_config_destroy(waste_level_app_config_t *app_config)
Destroy a waste level app config.
Definition: example_waste_level.c:166
waste_level_handle::distance_history_write_idx
uint16_t distance_history_write_idx
Definition: example_waste_level.c:39
waste_level_preset_t
waste_level_preset_t
Waste level preset.
Definition: example_waste_level.h:54
acc_processing_metadata_t::subsweep_data_offset
uint16_t subsweep_data_offset[(4U)]
Definition: acc_processing.h:43
waste_level_handle::distance_history_len
uint16_t distance_history_len
Definition: example_waste_level.c:36
acc_config_subsweep_num_points_get
uint16_t acc_config_subsweep_num_points_get(const acc_config_t *config, uint8_t index)
Get the number of data points to measure.
has_overlapping_subsweeps
static bool has_overlapping_subsweeps(const acc_config_t *sensor_config, uint8_t *offending_subsweep)
Helper function for validation.
Definition: example_waste_level.c:473
buffer
void * buffer
Definition: i2c_example_cargo.c:40
waste_level_app_config_set_preset
void waste_level_app_config_set_preset(waste_level_preset_t preset, waste_level_app_config_t *app_config)
Apply a preset to an app config.
Definition: example_waste_level.c:179
acc_config_num_subsweeps_get
uint8_t acc_config_num_subsweeps_get(const acc_config_t *config)
Get the number of subsweeps to use.
acc_algorithm_get_distance_m
float acc_algorithm_get_distance_m(uint16_t step_length, uint16_t start_point, float base_step_length_m, uint16_t idx)
Calculate distance for a point at an index.
Definition: acc_algorithm.c:1118
waste_level_handle
Definition: example_waste_level.c:27
acc_int16_complex_t
Data type for interger-based representation of complex numbers.
Definition: acc_definitions_common.h:40
waste_level_process
void waste_level_process(waste_level_handle_t *handle, const waste_level_app_config_t *app_config, const acc_processing_metadata_t *metadata, const acc_int16_complex_t *frame, waste_level_result_t *waste_level_result)
Process Sparse IQ data.
Definition: example_waste_level.c:275
acc_config_sweeps_per_frame_set
void acc_config_sweeps_per_frame_set(acc_config_t *config, uint16_t sweeps)
Set sweeps per frame.
acc_config_destroy
void acc_config_destroy(acc_config_t *config)
Destroy a configuration freeing any resources allocated.
waste_level_handle::state
struct waste_level_handle::@7 state
waste_level_result_t
Result type.
Definition: example_waste_level.h:63
acc_config_subsweep.h
is_invalid_start_point
static bool is_invalid_start_point(const acc_config_t *sensor_config)
Helper function for validation.
Definition: example_waste_level.c:494
cargo_result_t::level_m
float level_m
Definition: example_cargo.h:123
acc_config_create
acc_config_t * acc_config_create(void)
Create a configuration.
waste_level_processing_config_t::median_filter_len
uint16_t median_filter_len
Definition: example_waste_level.h:39
copy_non_nan_floats
static uint16_t copy_non_nan_floats(const float *in, float *out, uint16_t length)
Copy non-NaN floats between buffers.
Definition: example_waste_level.c:580
acc_integration.h
config
cargo_config_t config
Definition: i2c_example_cargo.c:34
subsweep_approx_end_m
static float subsweep_approx_end_m(const acc_config_t *sensor_config, uint8_t subsweep_idx)
Helper function for validation.
Definition: example_waste_level.c:537
acc_config_frame_rate_set
void acc_config_frame_rate_set(acc_config_t *config, float frame_rate)
Set the frame rate.
cargo_result_t::level_percent
float level_percent
Definition: example_cargo.h:128
acc_integration_mem_alloc
void * acc_integration_mem_alloc(size_t size)
Allocate dynamic memory.
Definition: acc_integration_stm32.c:592
acc_processing_metadata_t
Metadata that will be populated by the processing module during creation.
Definition: acc_processing.h:36
waste_level_app_config_t
Definition: example_waste_level.h:42
is_outside_measuring_interval
static bool is_outside_measuring_interval(const acc_config_t *sensor_config, float distance_m)
Helper function for validation.
Definition: example_waste_level.c:503
subsweep_approx_start_m
static float subsweep_approx_start_m(const acc_config_t *sensor_config, uint8_t subsweep_idx)
Helper function for validation.
Definition: example_waste_level.c:529
acc_config_sweeps_per_frame_get
uint16_t acc_config_sweeps_per_frame_get(const acc_config_t *config)
Get the number of sweeps per frame.
acc_processing_metadata_t::sweep_data_length
uint16_t sweep_data_length
Definition: acc_processing.h:41
waste_level_processing_config_t::bin_start_m
float bin_start_m
Definition: example_waste_level.h:27
set_level_numbers
static void set_level_numbers(float filtered_distance, const waste_level_processing_config_t *processing_config, waste_level_result_t *result)
Populate a waste level result with its human-readable entries.
Definition: example_waste_level.c:596
handle
cargo_handle_t * handle
Definition: i2c_example_cargo.c:35
result
cargo_result_t result
Definition: i2c_example_cargo.c:43
acc_int16_complex_t::real
int16_t real
Definition: acc_definitions_common.h:42
acc_config_t
struct acc_config acc_config_t
Definition: acc_config.h:24
printf
#define printf
Definition: printf.h:60
acc_config_subsweep_step_length_get
uint16_t acc_config_subsweep_step_length_get(const acc_config_t *config, uint8_t index)
Get the step length in a sweep.
acc_processing_points_to_meter
float acc_processing_points_to_meter(int32_t points)
Convert a distance or step length in points to meter.
write_to_ring_buffer
static void write_to_ring_buffer(float *buffer, uint16_t capacity, uint16_t *length, uint16_t *write_idx, float elem)
Write next value into a ring buffer.
Definition: example_waste_level.c:568
WASTE_LEVEL_PRESET_PLASTIC_WASTE_BIN
@ WASTE_LEVEL_PRESET_PLASTIC_WASTE_BIN
Definition: example_waste_level.h:57
acc_config_num_subsweeps_set
void acc_config_num_subsweeps_set(acc_config_t *config, uint8_t num_subsweeps)
Set the number of subsweeps to use.
acc_config_subsweep_start_point_set
void acc_config_subsweep_start_point_set(acc_config_t *config, int32_t start_point, uint8_t index)
Set the starting point of the sweep.
WASTE_LEVEL_PRESET_NONE
@ WASTE_LEVEL_PRESET_NONE
Definition: example_waste_level.h:56
waste_level_processing_config_t::threshold
float threshold
Definition: example_waste_level.h:33
waste_level_app_config_t::sensor_config
acc_config_t * sensor_config
Definition: example_waste_level.h:48
acc_config_subsweep_profile_set
void acc_config_subsweep_profile_set(acc_config_t *config, acc_config_profile_t profile, uint8_t index)
Set a profile.
subsweep_start_points_strictly_increases
static bool subsweep_start_points_strictly_increases(const acc_config_t *sensor_config)
Helper function for validation.
Definition: example_waste_level.c:452
ACC_APPROX_BASE_STEP_LENGTH_M
#define ACC_APPROX_BASE_STEP_LENGTH_M
Approximate minimum step length for a sensor measurement in meters.
Definition: acc_algorithm.h:19
waste_level_handle_destroy
void waste_level_handle_destroy(waste_level_handle_t *handle)
Destroy a waste level handle.
Definition: example_waste_level.c:257
acc_algorithm_conj_f32
void acc_algorithm_conj_f32(float complex *data, uint16_t data_length)
Inline calculate conjugate of all elements in an array.
Definition: acc_algorithm.c:576
waste_level_app_config_create
waste_level_app_config_t * waste_level_app_config_create(void)
Create a waste level app config (includes an acc_config_t)
Definition: example_waste_level.c:147
waste_level_handle_create
waste_level_handle_t * waste_level_handle_create(const waste_level_app_config_t *app_config)
Create a waste level handle.
Definition: example_waste_level.c:217
acc_algorithm_clip_f32
float acc_algorithm_clip_f32(float value, float min, float max)
Clip passed value into the interval [min, max].
Definition: acc_algorithm.c:1431
acc_integration_log.h
waste_level_processing_config_log
void waste_level_processing_config_log(const waste_level_processing_config_t *config)
Log a waste level config.
Definition: example_waste_level.c:354
waste_level_result_t::level_found
bool level_found
Definition: example_waste_level.h:68
ACC_LOG_FLOAT_TO_INTEGER
#define ACC_LOG_FLOAT_TO_INTEGER(a)
Definition: acc_integration_log.h:26
waste_level_handle::scratch
float * scratch
Definition: example_waste_level.c:43
acc_algorithm_median_f32
float acc_algorithm_median_f32(float *data, uint16_t length)
Calculate median of input data.
Definition: acc_algorithm.c:1342
acc_algorithm.h
acc_algorithm_variance_f32
float acc_algorithm_variance_f32(const float *data, uint16_t length)
Calculate variance.
Definition: acc_algorithm.c:1399
acc_integration_mem_free
void acc_integration_mem_free(void *ptr)
Free dynamic memory.
Definition: acc_integration_stm32.c:602
example_waste_level.h
acc_int16_complex_t::imag
int16_t imag
Definition: acc_definitions_common.h:43
acc_definitions_common.h
acc_config_subsweep_hwaas_set
void acc_config_subsweep_hwaas_set(acc_config_t *config, uint16_t hwaas, uint8_t index)
Set the hardware accelerated average samples (HWAAS)
acc_config_subsweep_step_length_set
void acc_config_subsweep_step_length_set(acc_config_t *config, uint16_t step_length, uint8_t index)
Set the step length in a sweep.
acc_config.h
waste_level_processing_config_t
Configuration for waste_level.
Definition: example_waste_level.h:24
validate_app_config
static bool validate_app_config(const waste_level_app_config_t *app_config)
Validate an app_config (processing & sensor config).
Definition: example_waste_level.c:364
acc_config_subsweep_num_points_set
void acc_config_subsweep_num_points_set(acc_config_t *config, uint16_t num_points, uint8_t index)
Set the number of data points to measure.
PRIfloat
#define PRIfloat
Specifier for printing float type using integers.
Definition: acc_integration_log.h:31
ACC_CONFIG_PROFILE_1
@ ACC_CONFIG_PROFILE_1
Definition: acc_definitions_a121.h:52
waste_level_handle::distance_history_points
float * distance_history_points
Definition: example_waste_level.c:33
cargo_handle::state
struct cargo_handle::@6 state
acc_config_subsweep_start_point_get
int32_t acc_config_subsweep_start_point_get(const acc_config_t *config, uint8_t index)
Get the starting point of the sweep.
waste_level_processing_config_t::bin_end_m
float bin_end_m
Definition: example_waste_level.h:30
acc_algorithm_sum_sweep
void acc_algorithm_sum_sweep(const acc_int16_complex_t *frame, uint16_t num_points, uint16_t sweeps_per_frame, uint16_t start_point, uint16_t end_point, float complex *sum_sweep)
Calculate the sum of all sweeps in a frame, from start_point to end_point.
Definition: acc_algorithm.c:536
point_in_sweep_to_distance_m
static float point_in_sweep_to_distance_m(const acc_config_t *sensor_config, const acc_processing_metadata_t *metadata, uint16_t point_idx)
Calculate a point's corresponding distance in meters.
Definition: example_waste_level.c:545
acc_processing.h
ACC_CONFIG_PROFILE_3
@ ACC_CONFIG_PROFILE_3
Definition: acc_definitions_a121.h:54
acc_processing_metadata_t::subsweep_data_length
uint16_t subsweep_data_length[(4U)]
Definition: acc_processing.h:45
waste_level_app_config_t::processing_config
waste_level_processing_config_t processing_config
Definition: example_waste_level.h:45