Skyward boardcore
Loading...
Searching...
No Matches
MEA.cpp
Go to the documentation of this file.
1/* Copyright (c) 2024 Skyward Experimental Rocketry
2 * Author: Davide Mor
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "MEA.h"
24
26
27using namespace Boardcore;
28using namespace Eigen;
29
30MEA::Step::Step(float mainValveOpen) : mainValveOpen{mainValveOpen} {}
31
33{
34 withCCPressure(ccPressure.pressure);
35}
36
37void MEA::Step::withCCPressure(float ccPressure)
38{
39 this->ccPressure = ccPressure;
40 hasCCPressure = true;
41}
42
44{
45 withAcceleration(static_cast<Eigen::Vector<float, 3>>(acceleration));
46}
47
48void MEA::Step::withAcceleration(Eigen::Vector<float, 3> acceleration)
49{
50 this->acceleration = acceleration;
51 hasAcceleration = true;
52}
53
54void MEA::Step::withSpeedAndAlt(float verticalSpeed, float mslAltitude)
55{
56 this->verticalSpeed = verticalSpeed;
57 this->mslAltitude = mslAltitude;
58 hasSpeedAndAlt = true;
59}
60
61MEA::MEA(const Config& config)
62 : F{config.F}, Q{config.Q}, G{config.G}, baroH{config.baroH},
63 baroR{config.baroR}, P{config.P}, x{0, 0, config.initialMass},
64 mass{config.initialMass}, accelThresh{config.accelThresh},
65 speedThresh{config.speedThresh}, Kt{config.Kt}, alpha{config.alpha},
66 c{config.c}, coeffs{config.coeffs}, crossSection{config.crossSection},
67 ae{config.ae}, p0{config.p0}, minMass{config.minMass},
68 maxMass{config.maxMass}, cdCorrectionFactor(config.cdCorrectionFactor)
69{
70 updateState();
71}
72
73void MEA::update(const Step& step)
74{
75 // First run the prediction step
76 predict(step);
77
78 // Run cc pressure correction
79 correctBaro(step);
80
81 // Compute applied force/mach/CD/rho
82 computeForce(step);
83
84 // Run accelerometer correction
85 correctAccel(step);
86
87 // Compute current mass
88 computeMass();
89
90 // Run apogee prediction
91 computeApogee(step);
92
93 // Finally update state
94 updateState();
95}
96
97MEAState MEA::getState() { return state; }
98
99void MEA::predict(const Step& step)
100{
101 x = F * x + G * step.mainValveOpen;
102 P = F * P * F.transpose() + Q;
103}
104
105void MEA::computeForce(const Step& step)
106{
107 if (!step.hasSpeedAndAlt)
108 return;
109
110 // NOTE: Here we assume that verticalSpeed roughly equals the total speed,
111 // so that we don't depend on N/E speed components, which can be quite
112 // unreliable in case of no GPS fix or during powered ascent
113 mach = Aeroutils::computeMach(-step.mslAltitude, step.verticalSpeed,
114 Constants::MSL_TEMPERATURE);
115
116 cd = Aeroutils::computeCd(coeffs, mach);
117 rho = Aeroutils::computeRho(-step.mslAltitude, Constants::MSL_TEMPERATURE);
118
119 // Dynamic pressure
120 q = 0.5f * rho * (step.verticalSpeed * step.verticalSpeed);
121
122 // Aerodynamic force component
123 force = q * crossSection * (cd * cdCorrectionFactor);
124
125 if (step.mslAltitude > 800)
126 {
127 // At high altitudes we need to compensate for low external pressure
128
129 // External pressure
130 float p = Aeroutils::relPressure(step.mslAltitude);
131 force -= (p0 - p) * ae;
132 }
133}
134
135void MEA::correctBaro(const Step& step)
136{
137 if (!step.hasCCPressure)
138 return;
139
140 float S = baroH * P * baroH.transpose() + baroR;
141
142 // If not invertible, do not invert it
143 if (S < 1e-3)
144 return;
145
146 Matrix<float, 3, 1> K = (P * baroH.transpose()) / S;
147
148 P = (Matrix<float, 3, 3>::Identity() - K * baroH) * P;
149 x = x + K * (step.ccPressure - baroH * x);
150}
151
152void MEA::correctAccel(const Step& step)
153{
154 if (!step.hasAcceleration || !step.hasSpeedAndAlt)
155 return;
156
157 // Do not correct at low speed/acceleration
158 if (step.acceleration.norm() < accelThresh ||
159 step.verticalSpeed < speedThresh)
160 {
161 return;
162 }
163
164 float y = (Kt * (float)(baroH * x) - force) / x(2);
165
166 Matrix<float, 1, 3> accelH =
167 Vector<float, 3>{Kt * baroH(0) / x(2), Kt * baroH(1) / x(2),
168 -(Kt * (float)(baroH * x) - force) / (x(2) * x(2))}
169 .transpose();
170
171 float accelR = alpha * q + c;
172
173 float S = accelH * P * accelH.transpose() + accelR;
174
175 // If not invertible, do not invert it
176 if (S < 1e-3)
177 return;
178
179 Matrix<float, 3, 1> K = (P * accelH.transpose()) / S;
180
181 P = (Matrix<float, 3, 3>::Identity() - K * accelH) * P;
182 x = x + K * (step.acceleration.x() - y);
183}
184
185void MEA::computeMass()
186{
187 // Clamp the used mass, so that we don't use bogus values in case the filter
188 // fails BAD
189 mass = std::max(std::min(x(2), maxMass), minMass);
190}
191
192void MEA::computeApogee(const Step& step)
193{
194 if (!step.hasSpeedAndAlt)
195 return;
196
197 // Simplified massive formula for apogee estimation.
198 // Warning: log1p(x) = log(1 + x)
199 float temp = ((rho * cd * crossSection) / mass);
200 apogee = step.mslAltitude +
201 1 / temp *
202 log1p(0.5 * (step.verticalSpeed * step.verticalSpeed * temp) /
203 Constants::g);
204}
205
206void MEA::updateState()
207{
209
210 state.x0 = x(0);
211 state.x1 = x(1);
212 state.x2 = x(2);
213
214 state.estimatedMass = mass;
215 state.estimatedPressure = baroH * x;
216 state.estimatedApogee = apogee;
217 state.estimatedForce = force;
218}
MEA(const Config &config)
Definition MEA.cpp:61
void update(const Step &step)
Definition MEA.cpp:73
MEAState getState()
Definition MEA.cpp:97
float computeMach(float d, float vtot, float t0)
Computes the mach relative to the speed at a certain altitude.
float computeCd(const AerodynamicCoeff &coeff, float mach)
Computes the CD from aerodynamic coefficients and mach.
float relPressure(float altitude, float pressureRef, float temperatureRef)
Returns the pressure given the altitude with respect to a reference pressure and temperature,...
Definition AeroUtils.cpp:41
float computeRho(float d, float t0)
Computes the rho (air density) of air at the given altitude.
Definition AeroUtils.cpp:86
uint64_t getTimestamp()
Returns the current timer value in microseconds.
This file includes all the types the logdecoder script will decode.
Structure to handle accelerometer data.
Definition SensorData.h:121
void withCCPressure(PressureData ccPressure)
Definition MEA.cpp:32
void withSpeedAndAlt(float verticalSpeed, float mslAltitude)
Definition MEA.cpp:54
void withAcceleration(AccelerometerData acceleration)
Definition MEA.cpp:43
Step(float mainValveOpen)
Definition MEA.cpp:30
float x0
first kalman state
Definition MEAData.h:40
float estimatedMass
Estimated rocket mass [kg].
Definition MEAData.h:36
uint64_t timestamp
Definition MEAData.h:33
float estimatedPressure
Estimated pressure in combustion chamber [Pa].
Definition MEAData.h:35
float x2
third kalman state representing the mass
Definition MEAData.h:42
float estimatedApogee
Estimated apogee in msl [m].
Definition MEAData.h:37
float estimatedForce
Estimated drag force [N].
Definition MEAData.h:38
float x1
second kalman state
Definition MEAData.h:41