SensESP 3.0.1
Universal Signal K sensor toolkit ESP32
Loading...
Searching...
No Matches
curveinterpolator.cpp
Go to the documentation of this file.
1#include "sensesp.h"
2
3#include "curveinterpolator.h"
4
5namespace sensesp {
6
8
9CurveInterpolator::Sample::Sample(float input, float output)
10 : input_{input}, output_{output} {}
11
13 : input_{obj["input"]}, output_{obj["output"]} {}
14
23CurveInterpolator::CurveInterpolator(std::set<Sample>* defaults,
24 const String& config_path)
25 : FloatTransform(config_path) {
26 // Load default values if no configuration present...
27 if (defaults != NULL) {
28 samples_.clear();
29 samples_ = *defaults;
30 }
31
32 load();
33}
34
35void CurveInterpolator::set(const float& input) {
36 if (samples_.empty()) {
37 output_ = 0; // or any default output if no samples are available
38 notify();
39 return;
40 }
41
42 std::set<Sample>::iterator it = samples_.begin();
43 float x0 = it->input_;
44 float y0 = it->output_;
45
46 // Check if the input is below the lowest sample point
47 if (input < x0) {
48 // Need to extrapolate below the first point
49 if (samples_.size() > 1) {
50 auto second_it = std::next(it);
51 float x1 = second_it->input_;
52 float y1 = second_it->output_;
53 float const gradient = (y1 - y0) / (x1 - x0);
54
55 output_ = y0 + gradient *
56 (input -
57 x0); // Extrapolate using the first segment's gradient
58 } else {
59 output_ = y0; // Only one sample, output its value
60 }
61 notify();
62 return;
63 }
64
65 // Search for the correct interval or the last sample point
66 while (it != samples_.end()) {
67 if (input > it->input_) {
68 x0 = it->input_;
69 y0 = it->output_;
70 ++it;
71 } else {
72 break;
73 }
74 }
75
76 // Interpolate or extrapolate above the highest point
77 if (it != samples_.end()) {
78 float x1 = it->input_;
79 float y1 = it->output_;
80 output_ = (y0 * (x1 - input) + y1 * (input - x0)) / (x1 - x0);
81 } else {
82 // Hit the end of the table with no match, calculate output using the
83 // gradient from the last two points
84 auto last = samples_.rbegin();
85 auto second_last = std::next(last);
86 float x1 = last->input_;
87 float y1 = last->output_;
88 float x2 = second_last->input_;
89 float y2 = second_last->output_;
90 float const gradient = (y1 - y2) / (x1 - x2);
91
92 // Extrapolate using the gradient
93 output_ = y1 + gradient * (input - x1);
94 }
95
96 notify();
97}
98
99bool CurveInterpolator::to_json(JsonObject& doc) {
100 JsonArray json_samples = doc["samples"].to<JsonArray>();
101 for (auto& sample : samples_) {
102 // Add a new JsonObject to the array
103 JsonObject entry = json_samples.add<JsonObject>();
104 if (entry.isNull()) {
105 ESP_LOGE(__FILENAME__, "No memory for sample");
106 return false;
107 }
108 entry["input"] = sample.input_;
109 entry["output"] = sample.output_;
110 }
111 return true;
112}
113
114bool CurveInterpolator::from_json(const JsonObject& doc) {
115 String const expected[] = {"samples"};
116 for (auto str : expected) {
117 if (!doc[str].is<JsonVariant>()) {
118 ESP_LOGE(
119 __FILENAME__,
120 "Can not set CurveInterpolator configuration: missing json field "
121 "%s\n",
122 str.c_str());
123 return false;
124 }
125 }
126
127 JsonArray arr = doc["samples"];
128 if (arr.size() > 0) {
129 samples_.clear();
130 for (auto jentry : arr) {
131 auto json_object = jentry.as<JsonObject>();
132 Sample sample(json_object);
133 samples_.insert(sample);
134 }
135 }
136
137 return true;
138}
139
141
143 auto* sample_copy = new Sample(sample);
144 samples_.insert(*sample_copy);
145}
146
147} // namespace sensesp
void set(const float &input) override
virtual bool to_json(JsonObject &doc) override
void add_sample(const Sample &new_sample)
CurveInterpolator(std::set< Sample > *defaults=NULL, const String &config_path="")
Construct a new CurveInterpolator object.
virtual bool from_json(const JsonObject &doc) override
virtual bool load() override
Load and populate the object from a persistent storage.
Definition saveable.cpp:8