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 <array>
5#include <cmath>
6#include <cstdint>
7#include <cstdio>
8
9namespace {
10
11// Clamp utility
12template <typename T>
13constexpr const T& clamp(const T& value, const T& lower, const T& upper) {
14 return (value < lower) ? lower : (value > upper) ? upper : value;
15}
16
17// Constants
18constexpr float MIN_VALID_DECEL = 0.000001F;
19constexpr float MS_TO_S_FACTOR = 1000.0F;
20constexpr float MAGIC_HALF = 0.5F;
21constexpr float GRAVITY_MS2 = 9.80665F;
22constexpr uint32_t MAX_WARMUPS = 1000;
23
24float dragRatioFiltered_ = 0.0F; // k
25bool dragLocked_ = false;
26
27} // namespace
28
30 float accelFilterAlpha, // caution: two floats here are swappable //NOLINT(bugprone-easily-swappable-parameters)
31 float minimumClimbVelocity_ms) //NOLINT(bugprone-easily-swappable-parameters)
32 : vve_(velocityEstimator),
33 filteredDecel_(0.0F),
34 alpha_(clamp(accelFilterAlpha, 0.0F, 1.0F)),
35 minClimbVel_(minimumClimbVelocity_ms),
36 valid_(false),
37 tToApogee_(0.0F),
38 predApogeeTs_(0),
39 predApogeeAlt_(0.0F),
40 lastTs_(0),
41 lastVel_(0.0F),
42 numWarmups_(0) {}
43
45 const uint32_t currentTimestamp = vve_.getTimestamp();
46 const float velocity = vve_.getEstimatedVelocity();
47 const float acceleration = vve_.getInertialVerticalAcceleration();
48
49 // Raw deceleration estimate
50 float decelSample = std::max(0.0F, -acceleration);
51
52 if (currentTimestamp > lastTs_) {
53 const auto deltaTime = static_cast<float>(currentTimestamp - lastTs_);
54 const float deltaVelocity = velocity - lastVel_;
55 float estimatedDecel = (deltaTime > 0.0F) ? std::max(0.0F, -deltaVelocity / deltaTime) : 0.0F;
56 decelSample = MAGIC_HALF * (decelSample + estimatedDecel);
57 }
58
59 filteredDecel_ = alpha_ * decelSample + (1.0F - alpha_) * filteredDecel_;
60
61 if (velocity > minClimbVel_ && filteredDecel_ > MIN_VALID_DECEL) {
62 tToApogee_ = velocity / filteredDecel_;
63 predApogeeTs_ = currentTimestamp + static_cast<uint32_t>(tToApogee_ * MS_TO_S_FACTOR);
64
65 const float altitude = vve_.getEstimatedAltitude();
66 predApogeeAlt_ = altitude + velocity * tToApogee_ -
67 MAGIC_HALF * filteredDecel_ * tToApogee_ * tToApogee_;
68
69 valid_ = true;
70 } else {
71 valid_ = false;
72 }
73
74 lastTs_ = currentTimestamp;
75 lastVel_ = velocity;
76 numWarmups_ = std::min(numWarmups_ + 1, MAX_WARMUPS);
77}
78
80 const uint32_t currentTimestamp = vve_.getTimestamp();
81 const float velocity = vve_.getEstimatedVelocity();
82 const float acceleration = vve_.getInertialVerticalAcceleration();
83
84 float kEstimate = 0.0F;
85 if (std::fabs(velocity) > 1.0F) {
86 kEstimate = std::max(0.0F, -(acceleration + GRAVITY_MS2)) / (velocity * velocity);
87 }
88
89 filteredDecel_ = alpha_ * kEstimate + (1.0F - alpha_) * filteredDecel_;
90 const float dragToMassRatio = filteredDecel_;
91
92 if (velocity > minClimbVel_ && dragToMassRatio > MIN_VALID_DECEL) {
93 const float terminalVelocity = std::sqrt(GRAVITY_MS2 / dragToMassRatio);
94 tToApogee_ = (terminalVelocity / GRAVITY_MS2) * std::atan(velocity / terminalVelocity);
95 const float deltaAltitude = (terminalVelocity * terminalVelocity / (2.0F * GRAVITY_MS2)) *
96 std::log1p((velocity * velocity) /
97 (terminalVelocity * terminalVelocity));
98
99 predApogeeTs_ = currentTimestamp + static_cast<uint32_t>(tToApogee_ * MS_TO_S_FACTOR);
100 predApogeeAlt_ = vve_.getEstimatedAltitude() + deltaAltitude;
101 valid_ = true;
102 } else {
103 valid_ = false;
104 }
105
106 lastTs_ = currentTimestamp;
107 lastVel_ = velocity;
108}
110 const uint32_t currentTimestamp_ms = vve_.getTimestamp();
111 const float altitude_m = vve_.getEstimatedAltitude();
112 const float velocity_ms = vve_.getEstimatedVelocity();
113 const float acceleration_ms2 = vve_.getInertialVerticalAcceleration();
114
115 constexpr size_t FEATURE_COUNT = 10; // NOLINT(cppcoreguidelines-init-variables)
116
117 // Polynomial Regression Coefficients for C++
118 const std::array<float, FEATURE_COUNT> coeffs = {
119 /* 1 */ 0.00000000,
120 /* vertical_velocity */ 5.06108448,
121 /* vertical_acceleration */ 63.94744144,
122 /* delta_h_simple */ 0.52115350,
123 /* vertical_velocity^2 */ 0.01494354,
124 /* vertical_velocity vertical_acceleration */ 0.46012269,
125 /* vertical_velocity delta_h_simple */ 0.01274390,
126 /* vertical_acceleration^2 */ 3.27864634,
127 /* vertical_acceleration delta_h_simple */ -0.00747177,
128 /* delta_h_simple^2 */ -0.00208120,
129 };
130 const float intercept = 308.64734694;
131
132 // ───────────────────────────────────────────────────────
133 // Compute delta_h_simple = v^2 / (2 * decel), with decel > 0
134 const float decel = std::fabs(acceleration_ms2);
135 const float delta_h_simple = MAGIC_HALF * (velocity_ms * velocity_ms) / decel;
136
137 // ───────────────────────────────────────────────────────
138 // Evaluate the regression model
139 const std::array<float, FEATURE_COUNT> inputs = {
140 1.0F,
141 velocity_ms, // vertical_velocity
142 acceleration_ms2, // vertical_acceleration
143 delta_h_simple,
144 velocity_ms * velocity_ms, // vertical_velocity^2
145 velocity_ms * acceleration_ms2, // vertical_velocity vertical_acceleration
146 velocity_ms * delta_h_simple, // vertical_velocity delta_h_simple
147 acceleration_ms2 * acceleration_ms2, // vertical_acceleration^2
148 acceleration_ms2 * delta_h_simple, // vertical_acceleration delta_h_simple
149 delta_h_simple * delta_h_simple, // delta_h_simple^2
150 };
151
152 float apogeeRemaining_m = intercept;
153 for (size_t i = 0; i < FEATURE_COUNT; ++i) { // NOLINT(cppcoreguidelines-init-variables)
154 apogeeRemaining_m += coeffs[i] * inputs[i];
155 }
156
157 // ───────────────────────────────────────────────────────
158 // Combine with current altitude to compute predicted apogee
159 predApogeeAlt_ = altitude_m + apogeeRemaining_m;
160
161 // Estimate time to apogee using kinematic model
162 tToApogee_ = velocity_ms / decel;
163 predApogeeTs_ = currentTimestamp_ms + static_cast<uint32_t>(tToApogee_ * MS_TO_S_FACTOR);
164
165 valid_ = true;
166
167 lastTs_ = currentTimestamp_ms;
168 lastVel_ = velocity_ms;
169 numWarmups_ = std::min(numWarmups_ + 1, MAX_WARMUPS);
170
171 // printf("Current Timestamp: %u, Altitude: %.2f, Velocity: %.2f, Acceleration: %.2f, Predicted Apogee Remaining: %.2f, Delta H: %.2f\n",
172 // currentTimestamp_ms, altitude_m, velocity_ms, acceleration_ms2, apogeeRemaining_m, delta_h_simple);
173}
174
175
177{
178 //gets the gravity constant and the current velocity and altitude of the rocket
179 const float gravity = 9.80665F;
180 const float velocity = vve_.getEstimatedVelocity();
181 const float height = vve_.getEstimatedAltitude();
182
183
184 //variables for analytical calculation
185 const float kVelocityEpsilon = 0.001F;
186 const float kVelocityScaleForAlpha = 150.0F;
187 const float kAlphaMin = 0.02F;
188 const float kAlphaMax = 0.25F;
189 const float kMinDragCoefficient = 0.00001F;
190 const float kApogeeFactor = 0.5F;
191 const float kBallisticDenominator = 2.0F;
192
193
194 //if the velocity is less than or equal to zero, the rocket has already reach apogee and the apogee is the current altitude
195 if (velocity <= 0.0F)
196 {
197 predApogeeAlt_ = height;
198 valid_ = true;
199 return;
200 }
201
202 //gets the current acceleration of the rocket
203 const float acceleration = vve_.getInertialVerticalAcceleration();
204
205 //calculates the measured drag coefficient
206const float kMeasured = -(acceleration + gravity) /
207 (velocity * velocity + kVelocityEpsilon);
208
209 if (kMeasured > 0.0F && kMeasured < 1.0F)
210 {
211 float alpha = clamp(std::fabs(velocity) / kVelocityScaleForAlpha,
212 kAlphaMin,
213 kAlphaMax);
214 currentDragCoefficient = (1.0F - alpha) * currentDragCoefficient + alpha * kMeasured;
215 }
216
217 // Analytic apogee calculation
218 float apogee = 0.0F;
219
220 if (currentDragCoefficient > kMinDragCoefficient)
221 {
222 apogee = height + (kApogeeFactor / currentDragCoefficient) *
223 logf((gravity + currentDragCoefficient * velocity * velocity) / gravity);
224 }
225 else
226 {
227 // fallback if drag unknown
228 apogee = height + (velocity * velocity) /
229 (kBallisticDenominator * gravity);
230 }
231
232 predApogeeAlt_ = apogee;
233 valid_ = true;
234}
235
236// Simple getters
237bool ApogeePredictor::isPredictionValid() const { return valid_; }
238
240 return valid_ ? tToApogee_ : 0.0F;
241}
242
244 return valid_ ? predApogeeTs_ : 0;
245}
246
248 return valid_ ? predApogeeAlt_ : 0.0F;
249}
250
252 return filteredDecel_;
253}
254
256 return currentDragCoefficient;
257}
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 getdragCoefficient() const
float getFilteredDeceleration() const
float getTimeToApogee_s() const
1D Kalman filter fusing altimeter and accelerometer data.