Avionics
Core avionics package for CURE flight computers
Loading...
Searching...
No Matches
ApogeePredictor.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cmath>
5#include <cstdint>
6#include <cstdio>
7
8namespace {
9
10// Clamp utility
11template <typename T>
12constexpr const T& clamp(const T& value, const T& lower, const T& upper) {
13 return (value < lower) ? lower : (value > upper) ? upper : value;
14}
15
16// Constants
17constexpr float MIN_VALID_DECEL = 0.000001F;
18constexpr float MS_TO_S_FACTOR = 1000.0F;
19constexpr float MAGIC_HALF = 0.5F;
20constexpr float GRAVITY_MS2 = 9.80665F;
21constexpr uint32_t MAX_WARMUPS = 1000;
22
23} // namespace
24
26 float accelFilterAlpha, // caution: two floats here are swappable //NOLINT(bugprone-easily-swappable-parameters)
27 float minimumClimbVelocity_ms) //NOLINT(bugprone-easily-swappable-parameters)
28 : vve_(velocityEstimator),
29 filteredDecel_(0.0F),
30 alpha_(clamp(accelFilterAlpha, 0.0F, 1.0F)),
31 minClimbVel_(minimumClimbVelocity_ms),
32 valid_(false),
33 tToApogee_(0.0F),
34 predApogeeTs_(0),
35 predApogeeAlt_(0.0F),
36 lastTs_(0),
37 lastVel_(0.0F),
38 numWarmups_(0) {}
39
41 const uint32_t currentTimestamp = vve_.getTimestamp();
42 const float velocity = vve_.getEstimatedVelocity();
43 const float acceleration = vve_.getInertialVerticalAcceleration();
44
45 // Raw deceleration estimate
46 float decelSample = std::max(0.0F, -acceleration);
47
48 if (currentTimestamp > lastTs_) {
49 const auto deltaTime = static_cast<float>(currentTimestamp - lastTs_);
50 const float deltaVelocity = velocity - lastVel_;
51 float estimatedDecel = (deltaTime > 0.0F) ? std::max(0.0F, -deltaVelocity / deltaTime) : 0.0F;
52 decelSample = MAGIC_HALF * (decelSample + estimatedDecel);
53 }
54
55 filteredDecel_ = alpha_ * decelSample + (1.0F - alpha_) * filteredDecel_;
56
57 if (velocity > minClimbVel_ && filteredDecel_ > MIN_VALID_DECEL) {
58 tToApogee_ = velocity / filteredDecel_;
59 predApogeeTs_ = currentTimestamp + static_cast<uint32_t>(tToApogee_ * MS_TO_S_FACTOR);
60
61 const float altitude = vve_.getEstimatedAltitude();
62 predApogeeAlt_ = altitude + velocity * tToApogee_ -
63 MAGIC_HALF * filteredDecel_ * tToApogee_ * tToApogee_;
64
65 valid_ = true;
66 } else {
67 valid_ = false;
68 }
69
70 lastTs_ = currentTimestamp;
71 lastVel_ = velocity;
72 numWarmups_ = std::min(numWarmups_ + 1, MAX_WARMUPS);
73}
74
76 const uint32_t currentTimestamp = vve_.getTimestamp();
77 const float velocity = vve_.getEstimatedVelocity();
78 const float acceleration = vve_.getInertialVerticalAcceleration();
79
80 float kEstimate = 0.0F;
81 if (std::fabs(velocity) > 1.0F) {
82 kEstimate = std::max(0.0F, -(acceleration + GRAVITY_MS2)) / (velocity * velocity);
83 }
84
85 filteredDecel_ = alpha_ * kEstimate + (1.0F - alpha_) * filteredDecel_;
86 const float dragToMassRatio = filteredDecel_;
87
88 if (velocity > minClimbVel_ && dragToMassRatio > MIN_VALID_DECEL) {
89 const float terminalVelocity = std::sqrt(GRAVITY_MS2 / dragToMassRatio);
90 tToApogee_ = (terminalVelocity / GRAVITY_MS2) * std::atan(velocity / terminalVelocity);
91 const float deltaAltitude = (terminalVelocity * terminalVelocity / (2.0F * GRAVITY_MS2)) *
92 std::log1p((velocity * velocity) /
93 (terminalVelocity * terminalVelocity));
94
95 predApogeeTs_ = currentTimestamp + static_cast<uint32_t>(tToApogee_ * MS_TO_S_FACTOR);
96 predApogeeAlt_ = vve_.getEstimatedAltitude() + deltaAltitude;
97 valid_ = true;
98 } else {
99 valid_ = false;
100 }
101
102 lastTs_ = currentTimestamp;
103 lastVel_ = velocity;
104}
106 const uint32_t currentTimestamp_ms = vve_.getTimestamp();
107 const float altitude_m = vve_.getEstimatedAltitude();
108 const float velocity_ms = vve_.getEstimatedVelocity();
109 const float acceleration_ms2 = vve_.getInertialVerticalAcceleration();
110
111
112
113 const size_t featureCount = 10;
114
115 // Polynomial Regression Coefficients for C++
116 const float coeffs[] = {
117 /* 1 */ 0.00000000,
118 /* vertical_velocity */ 5.06108448,
119 /* vertical_acceleration */ 63.94744144,
120 /* delta_h_simple */ 0.52115350,
121 /* vertical_velocity^2 */ 0.01494354,
122 /* vertical_velocity vertical_acceleration */ 0.46012269,
123 /* vertical_velocity delta_h_simple */ 0.01274390,
124 /* vertical_acceleration^2 */ 3.27864634,
125 /* vertical_acceleration delta_h_simple */ -0.00747177,
126 /* delta_h_simple^2 */ -0.00208120,
127 };
128 const float intercept = 308.64734694;
129
130 // ───────────────────────────────────────────────────────
131 // Compute delta_h_simple = v^2 / (2 * decel), with decel > 0
132 double decel = std::fabs(acceleration_ms2);
133 double delta_h_simple = (velocity_ms * velocity_ms) / (2.0 * decel);
134
135 // ───────────────────────────────────────────────────────
136 // Evaluate the regression model
137 const double inputs[featureCount] = {
138 1.0,
139 velocity_ms, // vertical_velocity
140 acceleration_ms2, // vertical_acceleration
141 delta_h_simple,
142 velocity_ms * velocity_ms, // vertical_velocity^2
143 velocity_ms * acceleration_ms2, // vertical_velocity vertical_acceleration
144 velocity_ms * delta_h_simple, // vertical_velocity delta_h_simple
145 acceleration_ms2 * acceleration_ms2, // vertical_acceleration^2
146 acceleration_ms2 * delta_h_simple, // vertical_acceleration delta_h_simple
147 delta_h_simple * delta_h_simple, // delta_h_simple^2
148 };
149
150 double apogeeRemaining_m = intercept;
151 for (size_t i = 0; i < featureCount; ++i) {
152 apogeeRemaining_m += coeffs[i] * inputs[i];
153 }
154
155 // ───────────────────────────────────────────────────────
156 // Combine with current altitude to compute predicted apogee
157 predApogeeAlt_ = altitude_m + apogeeRemaining_m;
158
159 // Estimate time to apogee using kinematic model
160 tToApogee_ = velocity_ms / decel;
161 predApogeeTs_ = currentTimestamp_ms + static_cast<uint32_t>(tToApogee_ * MS_TO_S_FACTOR);
162
163 valid_ = true;
164
165 lastTs_ = currentTimestamp_ms;
166 lastVel_ = velocity_ms;
167 numWarmups_ = std::min(numWarmups_ + 1, MAX_WARMUPS);
168
169 printf("Current Timestamp: %u, Altitude: %.2f, Velocity: %.2f, Acceleration: %.2f, Predicted Apogee Remaining: %.2f, Delta H: %.2f\n",
170 currentTimestamp_ms, altitude_m, velocity_ms, acceleration_ms2, apogeeRemaining_m, delta_h_simple);
171}
172
173
174
175// Simple getters
176bool ApogeePredictor::isPredictionValid() const { return valid_; }
177
179 return valid_ ? tToApogee_ : 0.0F;
180}
181
183 return valid_ ? predApogeeTs_ : 0;
184}
185
187 return valid_ ? predApogeeAlt_ : 0.0F;
188}
189
191 return filteredDecel_;
192}
float getPredictedApogeeAltitude_m() const
uint32_t getPredictedApogeeTimestamp_ms() const
bool isPredictionValid() const
ApogeePredictor(const VerticalVelocityEstimator &velocityEstimator, float accelFilterAlpha=0.2F, float minimumClimbVelocity_ms=1.0F)
float getFilteredDeceleration() const
float getTimeToApogee_s() const
1D Kalman filter fusing altimeter and accelerometer data.