-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsensor_fusion.cpp
More file actions
83 lines (69 loc) · 3.59 KB
/
Copy pathsensor_fusion.cpp
File metadata and controls
83 lines (69 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Weighted average across three temperature sensors with disparate ranges.
//
// Demonstrates:
// - Per-sensor fixed-point grids (different lower/upper/notch each)
// - `will_conversion_overflow` predicate as a soft outlier filter
// - Rational-weighted average to keep the fused value exact
// - `clamp | round_nearest` on the output type to snap the fused value onto
// the output grid (no explicit `clamp_round` cast)
#include <iostream>
#include "bound/bound.hpp"
#include "bound/io.hpp"
#include "bound/predicates.hpp"
using namespace bnd;
// Three sensors covering overlapping but distinct ranges.
// Each has a different precision matched to its hardware.
using outdoor_t = bound<{{-40, 60}, notch<1, 2>}, round_nearest>; // 0.5 °C
using indoor_t = bound<{{0, 50}, notch<1, 10>}, round_nearest>; // 0.1 °C
using ground_t = bound<{{-10, 30}, notch<1, 4>}, round_nearest>; // 0.25 °C
// Output: a coarse fused grid, integer °C. `clamp | round_nearest` lets
// `fused_t{raw_fused}` saturate AND round the rational raw quotient in
// one step — no explicit `clamp_round<fused_t>(...)` cast needed.
using fused_t = bound<{-40, 60}, clamp | round_nearest>;
int main()
{
// Three readings — one outdoor reading is a clear outlier (-50, below
// grid). will_conversion_overflow rejects it before construction.
double raw[] = { 22.5, 21.7, -50.0, 22.0, 23.25 };
// Weights per sensor, in 1/8 step — could be tuned by confidence.
using weight_t = bound<{{0, 1}, notch<1, 8>}, round_nearest>;
weight_t w_outdoor{0.5};
weight_t w_indoor {0.375};
weight_t w_ground{0.125};
std::cout << "raw accepted? notes\n";
// Accumulate the weighted sum and total weight in fixed-point bounds — no
// rational. The 1/160 accumulator notch holds every sensor·weight product
// (outdoor 1/16, indoor 1/80, ground 1/32) exactly, so the running sum stays
// lossless; the weight accumulator keeps the weights' 1/8 notch.
using acc_t = bound<{{-200, 200}, notch<1, 160>}, round_nearest>;
using wsum_t = bound<{{0, 8}, notch<1, 8>}, round_nearest>;
acc_t weighted_sum{0};
wsum_t weight_sum{0};
auto accept = [&]<typename B>(double r, weight_t w, auto tag, auto predicate) {
bool ok = predicate(r);
std::cout << r << " " << (ok ? "yes" : "no ") << " " << tag << "\n";
if (!ok) return;
B reading{r};
weighted_sum = acc_t{weighted_sum + reading * w}; // bound-space, exact
weight_sum = wsum_t{weight_sum + w};
};
accept.template operator()<outdoor_t>(raw[0], w_outdoor, "outdoor",
[](double v){ return !will_conversion_overflow<outdoor_t>(v); });
accept.template operator()<indoor_t >(raw[1], w_indoor, "indoor ",
[](double v){ return !will_conversion_overflow<indoor_t>(v); });
accept.template operator()<outdoor_t>(raw[2], w_outdoor, "outdoor",
[](double v){ return !will_conversion_overflow<outdoor_t>(v); });
accept.template operator()<ground_t >(raw[3], w_ground, "ground ",
[](double v){ return !will_conversion_overflow<ground_t>(v); });
accept.template operator()<indoor_t >(raw[4], w_indoor, "indoor ",
[](double v){ return !will_conversion_overflow<indoor_t>(v); });
// Divide in bound-space: the weight grid includes 0, so `/` yields an
// optional<bound>; the fused_t ctor unwraps it and clamp-rounds in one step.
fused_t fused{0};
if (weight_sum != 0)
fused = fused_t{weighted_sum / weight_sum};
std::cout << "\nweighted sum: " << weighted_sum
<< ", total weight: " << weight_sum << "\n";
std::cout << "fused (clamp+round into fused_t): " << fused << "\n";
return 0;
}