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
7constexpr float VerticalVelocityEstimator::kGravity_mps2;
8
9
11 : stateAltitude_m_(0.0F),
12 stateVelocity_mps_(0.0F),
13 lastTimestamp_ms_(0),
14 initialized_(false),
15 accelNoiseVariance_(noise.accelNoiseVar),
16 altimeterNoiseVariance_(noise.altimeterNoiseVar),
17 verticalAxis_(0),
18 verticalDirection_(0),
19 verticalAxisDetermined_(false),
20 inertialVerticalAcceleration_(0.0F)
21{
22 // Initialize the covariance matrix P with moderate initial uncertainty.
23 P_[0][0] = 1.0F; P_[0][1] = 0.0F;
24 P_[1][0] = 0.0F; P_[1][1] = 1.0F;
25}
26
28 stateAltitude_m_ = initialState.initialAltitude;
29 stateVelocity_mps_ = 0.0F;
30 lastTimestamp_ms_ = initialState.initialTimestamp;
31 initialized_ = true;
32
33 // Reset vertical axis determination.
34 verticalAxisDetermined_ = false;
35 verticalAxis_ = 0;
36 verticalDirection_ = 0;
37
38 // Reset covariance matrix.
39 P_[0][0] = 1.0F; P_[0][1] = 0.0F;
40 P_[1][0] = 0.0F; P_[1][1] = 1.0F;
41}
42
43void VerticalVelocityEstimator::determineVerticalAxis(const std::array<float, 3>& rawAcl) {
44 // Check the magnitude of each axis reading.
45 std::array<float, 3> mag = { std::fabs(rawAcl[0]), std::fabs(rawAcl[1]), std::fabs(rawAcl[2]) };
46
47 // Find the index of the largest magnitude.
48 const std::size_t yAxis = 1U;
49 const std::size_t zAxis = 2U;
50 verticalAxis_ = 0; // Start with X
51 auto verticalAxisIndex = static_cast<std::size_t>(static_cast<uint8_t>(verticalAxis_));
52 if (mag[yAxis] > mag[verticalAxisIndex]) {
53 verticalAxis_ = 1; // Y
54 verticalAxisIndex = yAxis;
55 }
56 if (mag[zAxis] > mag[verticalAxisIndex]) {
57 verticalAxis_ = 2; // Z
58 verticalAxisIndex = zAxis;
59 }
60
61 // Determine if it's positive or negative relative to 'up'.
62 verticalDirection_ = (rawAcl[verticalAxisIndex] > 0.0F) ? 1 : -1;
63}
64
65// NOLINTBEGIN(readability-identifier-length)
67{
68 // Use the altimeter timestamp as the reference for this update.
69 const uint32_t currentTimestamp_ms = altitude.timestamp_ms;
70
71 // If not initialized, do so with the altimeter reading.
72 if (!initialized_) {
73 const InitialState initialState = { altitude.data, currentTimestamp_ms };
74 init(initialState);
75 return;
76 }
77
78 // Determine which axis is vertical if not done yet.
79 std::array<float, 3> rawAcl = { accel.x.data, accel.y.data, accel.z.data};
80 if (!verticalAxisDetermined_) {
81 determineVerticalAxis(rawAcl);
82 verticalAxisDetermined_ = true;
83 }
84
85
86
87
88
89 // Ensures the data is newer than the previous data and that is not the same as the last data
90 if (currentTimestamp_ms <= lastTimestamp_ms_)
91 {
92 return;
93 }
94 if(accel.x.timestamp_ms <= lastTimestamp_ms_)
95 {
96 return;
97 }
98
99 // Compute time step in seconds (dt).
100 const float dt = (static_cast<float>(currentTimestamp_ms - lastTimestamp_ms_)) * kMillisecondsToSeconds;
101
102 // Subtract gravity from the measured acceleration on the identified vertical axis.
103 const auto verticalAxisIndex = static_cast<std::size_t>(static_cast<uint8_t>(verticalAxis_));
104 inertialVerticalAcceleration_ =
105 (rawAcl[verticalAxisIndex] * static_cast<float>(verticalDirection_)) - kGravity_mps2;
106
107 // --- Prediction Step ---
108 // State prediction:
109 // predictedAltitude_m = alt + vel * dt + 0.5 * a * dt^2
110 // predictedVelocity_mps = vel + a * dt
111 const float predictedAltitude_m = stateAltitude_m_ + stateVelocity_mps_ * dt + 0.5F * inertialVerticalAcceleration_ * dt * dt;
112 const float predictedVelocity_mps = stateVelocity_mps_ + inertialVerticalAcceleration_ * dt;
113
114 // Process noise covariance Q (derived from acceleration noise variance).
115 const float dt2 = dt * dt;
116 const float dt3 = dt2 * dt;
117 const float dt4 = dt3 * dt;
118
119 const float q00 = 0.25F * dt4 * accelNoiseVariance_; // var in position
120 const float q01 = 0.5F * dt3 * accelNoiseVariance_; // covar pos-vel
121 const float q10 = 0.5F * dt3 * accelNoiseVariance_; // covar vel-pos
122 const float q11 = dt2 * accelNoiseVariance_; // var in velocity
123
124 // Predicted covariance: P' = F P F^T + Q
125 // F = [ [1, dt], [0, 1] ]
126 // so:
127 // P'[0][0] = P_[0][0] + 2*dt*P_[0][1] + dt^2*P_[1][1] + q00
128 // P'[0][1] = P_[0][1] + dt*P_[1][1] + q01
129 // P'[1][0] = P_[1][0] + dt*P_[1][1] + q10 (should be symmetric to P'[0][1])
130 // P'[1][1] = P_[1][1] + q11
131
132 // 2.0F comes from the derivative of the position state equation
133 const float predictedCov00 = P_[0][0] + 2.0F * dt * P_[0][1] + dt2 * P_[1][1] + q00; //NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
134 const float predictedCov01 = P_[0][1] + dt * P_[1][1] + q01;
135 const float predictedCov10 = P_[1][0] + dt * P_[1][1] + q10; // note: P_[1][0] == P_[0][1] if always kept symmetric
136 const float predictedCov11 = P_[1][1] + q11;
137
138 // --- Measurement Update (using altimeter reading) ---
139 // Measurement z = altimeter altitude
140 const float z = altitude.data;
141 // Innovation (residual): y = z - predictedAltitude_m
142 const float y = z - predictedAltitude_m;
143 // Innovation covariance: S = H P' H^T + R
144 // H = [1, 0], so S = P'[0][0] + altimeter noise variance.
145 const float innovationCovariance = predictedCov00 + altimeterNoiseVariance_;
146
147 // Kalman gain: K = P' H^T / S = [ P'[0][0], P'[1][0] ] / S
148 const float kalmanGain0 = predictedCov00 / innovationCovariance;
149 const float kalmanGain1 = predictedCov10 / innovationCovariance;
150
151 // Update state with measurement
152 stateAltitude_m_ = predictedAltitude_m + kalmanGain0 * y;
153 stateVelocity_mps_ = predictedVelocity_mps + kalmanGain1 * y;
154
155 // Update covariance: P = (I - K H) P'
156 // (I - K H) = [ [1 - K0, 0], [-K1, 1] ]
157 // So:
158 // P_[0][0] = (1 - K0)*P00
159 // P_[0][1] = (1 - K0)*P01
160 // P_[1][0] = P10 - K1*P00
161 // P_[1][1] = P11 - K1*P01
162 P_[0][0] = (1.0F - kalmanGain0) * predictedCov00;
163 P_[0][1] = (1.0F - kalmanGain0) * predictedCov01;
164 P_[1][0] = predictedCov10 - kalmanGain1 * predictedCov00;
165 P_[1][1] = predictedCov11 - kalmanGain1 * predictedCov01;
166
167 // Update time
168 lastTimestamp_ms_ = currentTimestamp_ms;
169}
170// NOLINTEND(readability-identifier-length)
171
173 return stateAltitude_m_;
174}
175
177 return stateVelocity_mps_;
178}
179
181 return inertialVerticalAcceleration_;
182}
183
185 return verticalAxis_;
186}
187
189 return verticalDirection_;
190}
191
193 return lastTimestamp_ms_;
194}
constexpr float kMillisecondsToSeconds
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})
virtual float getInertialVerticalAcceleration() const
void update(const AccelerationTriplet &accel, const DataPoint &altitude)
void init(InitialState initialState)