Avionics
Core avionics package for CURE flight computers
Loading...
Searching...
No Matches
VerticalVelocityEstimator.cpp
Go to the documentation of this file.
1#include <array>
2#include <cmath>
3#include <cstdio>
4
6
7
9 : state_alt(0.0F),
10 state_vel(0.0F),
11 lastTimestamp_ms(0),
12 initialized(false),
13 accelNoiseVariance(noise.accelNoiseVar),
14 altimeterNoiseVariance(noise.altimeterNoiseVar),
15 verticalAxis(0),
16 verticalDirection(0),
17 verticalAxisDetermined(false),
18 inertialVerticalAcceleration(0.0F)
19{
20 // Initialize the covariance matrix P with moderate initial uncertainty.
21 P[0][0] = 1.0F; P[0][1] = 0.0F;
22 P[1][0] = 0.0F; P[1][1] = 1.0F;
23}
24
26 state_alt = initialState.initialAltitude;
27 state_vel = 0.0F;
28 lastTimestamp_ms = initialState.initialTimestamp;
29 initialized = true;
30
31 // Reset vertical axis determination.
32 verticalAxisDetermined = false;
33 verticalAxis = 0;
34 verticalDirection = 0;
35
36 // Reset covariance matrix.
37 P[0][0] = 1.0F; P[0][1] = 0.0F;
38 P[1][0] = 0.0F; P[1][1] = 1.0F;
39}
40
41void VerticalVelocityEstimator::determineVerticalAxis(const std::array<float, 3>& rawAcl) {
42 // Check the magnitude of each axis reading.
43 std::array<float, 3> mag = { std::fabs(rawAcl[0]), std::fabs(rawAcl[1]), std::fabs(rawAcl[2]) };
44
45 // Find the index of the largest magnitude.
46 verticalAxis = 0; // Start with X
47 if (mag[1] > mag[verticalAxis]) {
48 verticalAxis = 1; // Y
49 }
50 if (mag[2] > mag[verticalAxis]) {
51 verticalAxis = 2; // Z
52 }
53
54 // Determine if it's positive or negative relative to 'up'.
55 verticalDirection = (rawAcl[verticalAxis] > 0.0F) ? 1 : -1;
56}
57
58// NOLINTBEGIN(readability-identifier-length)
60{
61 // Use the altimeter timestamp as the reference for this update.
62 const uint32_t currentTimestamp_ms = altimeter.timestamp_ms;
63
64 // If not initialized, do so with the altimeter reading.
65 if (!initialized) {
66 const InitialState initialState = { altimeter.data, currentTimestamp_ms };
67 init(initialState);
68 return;
69 }
70
71 // Determine which axis is vertical if not done yet.
72 std::array<float, 3> rawAcl = { accel.x.data, accel.y.data, accel.z.data};
73 if (!verticalAxisDetermined) {
74 determineVerticalAxis(rawAcl);
75 verticalAxisDetermined = true;
76 }
77
78 // Compute time step in seconds (dt). Use a small default if timestamps are identical.
79 const float dt = (currentTimestamp_ms > lastTimestamp_ms)
80 ? (static_cast<float>(currentTimestamp_ms - lastTimestamp_ms)) * MILLISECONDS_TO_SECONDS
82
83 // Subtract gravity from the measured acceleration on the identified vertical axis.
84 inertialVerticalAcceleration = (rawAcl[verticalAxis] * static_cast<float>(verticalDirection)) - g;
85
86 // --- Prediction Step ---
87 // State prediction:
88 // predicted_alt = alt + vel * dt + 0.5 * a * dt^2
89 // predicted_vel = vel + a * dt
90 const float predicted_alt = state_alt + state_vel * dt + 0.5F * inertialVerticalAcceleration * dt * dt;
91 const float predicted_vel = state_vel + inertialVerticalAcceleration * dt;
92
93 // Process noise covariance Q (derived from accelNoiseVariance).
94 const float dt2 = dt * dt;
95 const float dt3 = dt2 * dt;
96 const float dt4 = dt3 * dt;
97
98 const float Q00 = 0.25F * dt4 * accelNoiseVariance; // var in position
99 const float Q01 = 0.5F * dt3 * accelNoiseVariance; // covar pos-vel
100 const float Q10 = 0.5F * dt3 * accelNoiseVariance; // covar vel-pos
101 const float Q11 = dt2 * accelNoiseVariance; // var in velocity
102
103 // Predicted covariance: P' = F P F^T + Q
104 // F = [ [1, dt], [0, 1] ]
105 // so:
106 // P'[0][0] = P[0][0] + 2*dt*P[0][1] + dt^2*P[1][1] + Q00
107 // P'[0][1] = P[0][1] + dt*P[1][1] + Q01
108 // P'[1][0] = P[1][0] + dt*P[1][1] + Q10 (should be symmetric to P'[0][1])
109 // P'[1][1] = P[1][1] + Q11
110
111 // 2.0F comes from the derivative of the position state equation
112 const float P00 = P[0][0] + 2.0F * dt * P[0][1] + dt2 * P[1][1] + Q00; //NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
113 const float P01 = P[0][1] + dt * P[1][1] + Q01;
114 const float P10 = P[1][0] + dt * P[1][1] + Q10; // note: P[1][0] == P[0][1] if always kept symmetric
115 const float P11 = P[1][1] + Q11;
116
117 // --- Measurement Update (using altimeter reading) ---
118 // Measurement z = altimeter altitude
119 const float z = altimeter.data;
120 // Innovation (residual): y = z - predicted_alt
121 const float y = z - predicted_alt;
122 // Innovation covariance: S = H P' H^T + R
123 // H = [1, 0], so S = P'[0][0] + altimeterNoiseVariance
124 const float S = P00 + altimeterNoiseVariance;
125
126 // Kalman gain: K = P' H^T / S = [ P'[0][0], P'[1][0] ] / S
127 const float K0 = P00 / S;
128 const float K1 = P10 / S;
129
130 // Update state with measurement
131 state_alt = predicted_alt + K0 * y;
132 state_vel = predicted_vel + K1 * y;
133
134 // Update covariance: P = (I - K H) P'
135 // (I - K H) = [ [1 - K0, 0], [-K1, 1] ]
136 // So:
137 // P[0][0] = (1 - K0)*P00
138 // P[0][1] = (1 - K0)*P01
139 // P[1][0] = P10 - K1*P00
140 // P[1][1] = P11 - K1*P01
141 P[0][0] = (1.0F - K0) * P00;
142 P[0][1] = (1.0F - K0) * P01;
143 P[1][0] = P10 - K1 * P00;
144 P[1][1] = P11 - K1 * P01;
145
146 // Update time
147 lastTimestamp_ms = currentTimestamp_ms;
148}
149// NOLINTEND(readability-identifier-length)
150
152 return state_alt;
153}
154
156 return state_vel;
157}
158
160 return inertialVerticalAcceleration;
161}
162
164 return verticalAxis;
165}
166
168 return verticalDirection;
169}
170
172 return lastTimestamp_ms;
173}
constexpr float MILLISECONDS_TO_SECONDS
constexpr float MINIMUM_DELTA_T_S
Timestamped float measurement container.
Definition DataPoint.h:11
float data
Definition DataPoint.h:14
uint32_t timestamp_ms
Definition DataPoint.h:13
virtual int8_t getVerticalDirection() const
virtual uint32_t getTimestamp() const
VerticalVelocityEstimator(NoiseVariances noise={1.0f, 0.1f})
void update(const AccelerationTriplet &accel, const DataPoint &altimeter)
virtual float getInertialVerticalAcceleration() const
void init(InitialState initialState)