diff --git a/src/shared/algorithms/MEA/MEA.cpp b/src/shared/algorithms/MEA/MEA.cpp
index eeedf74c0eafe389a45b247a84d0e80da822c8d5..399ee03ee6c06a36c2b8cd91e64af6f83215f895 100644
--- a/src/shared/algorithms/MEA/MEA.cpp
+++ b/src/shared/algorithms/MEA/MEA.cpp
@@ -1,5 +1,5 @@
-/* Copyright (c) 2023 Skyward Experimental Rocketry
- * Author: Matteo Pignataro
+/* Copyright (c) 2024 Skyward Experimental Rocketry
+ * Author: Davide Mor
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -24,42 +24,170 @@
 
 #include <drivers/timer/TimestampTimer.h>
 
-namespace Boardcore
+using namespace Boardcore;
+using namespace Eigen;
+
+MEA::Step::Step(float mainValveOpen) : mainValveOpen{mainValveOpen} {}
+
+void MEA::Step::withCCPressure(PressureData ccPressure)
 {
+    withCCPressure(ccPressure.pressure);
+}
 
-MEA::MEA(const KalmanFilter::KalmanConfig kalmanConfig)
-    : filter(kalmanConfig), state()
+void MEA::Step::withCCPressure(float ccPressure)
 {
-    updateState();
+    this->ccPressure = ccPressure;
+    hasCCPressure    = true;
 }
 
-void MEA::update(const float feedValvePosition, const float pressure)
+void MEA::Step::withAcceleration(AccelerometerData acceleration)
 {
-    // Update the Kalman filter
-    filter.predictWithControl(KalmanFilter::CVectorM{feedValvePosition});
-    filter.correct(KalmanFilter::CVectorP{pressure});
+    withAcceleration(acceleration);
+}
 
-    // Compute the derived values for useful data
+void MEA::Step::withAcceleration(Eigen::Vector<float, 3> acceleration)
+{
+    this->acceleration = acceleration;
+    hasAcceleration    = true;
+}
+
+void MEA::Step::withSpeedAndAlt(float verticalSpeed, float mslAltitude)
+{
+    this->verticalSpeed = verticalSpeed;
+    this->mslAltitude   = mslAltitude;
+    hasSpeedAndAlt      = true;
+}
+
+MEA::MEA(const Config &config)
+    : F{config.F}, Q{config.Q}, G{config.G}, baroH{config.baroH},
+      baroR{config.baroR}, P{config.P}, x{0, 0, config.initialMass},
+      accelThresh{config.accelThresh}, speedThresh{config.speedThresh},
+      Kt{config.Kt}, alpha{config.alpha}, c{config.c}, coeffs{config.coeffs},
+      crossSection{config.crossSection}, ae{config.ae}, p0{config.p0}
+{
+}
+
+void MEA::update(const Step &step)
+{
+    // First run the prediction step
+    predict(step);
+
+    // Compute applied force/mach/CD/rho
+    computeForce(step);
+
+    // Run cc pressure correction
+    correctBaro(step);
+
+    // Run accelerometer correction
+    correctAccel(step);
+
+    // Run apogee prediction
+    computeApogee(step);
+
+    // Finally update state
     updateState();
 }
 
 MEAState MEA::getState() { return state; }
 
-void MEA::setKalmanConfig(KalmanFilter::KalmanConfig config)
+void MEA::predict(const Step &step)
 {
-    filter.setConfig(config);
+    x = F * x + G * step.mainValveOpen;
+    P = F * P * F.transpose() + Q;
 }
 
-void MEA::updateState()
+void MEA::computeForce(const Step &step)
+{
+    if (!step.hasSpeedAndAlt)
+        return;
+
+    // NOTE: Here we assume that verticalSpeed roughly equals the total speed,
+    // so that we don't depend on N/E speed components, which can be quite
+    // unreliable in case of no GPS fix
+    mach = Aeroutils::computeMach(-step.mslAltitude, step.verticalSpeed,
+                                  Constants::MSL_TEMPERATURE);
+
+    cd  = Aeroutils::computeCd(coeffs, mach);
+    rho = Aeroutils::computeRho(-step.mslAltitude, Constants::MSL_TEMPERATURE);
+
+    // Dynamic pressure
+    q = 0.5f * rho * (step.verticalSpeed * step.verticalSpeed);
+
+    // Aerodynamic force component
+    force = q * crossSection * cd;
+
+    if (step.mslAltitude > 800)
+    {
+        // At high altitudes we need to compensate for low external pressure
+
+        // External pressure
+        float p = Aeroutils::relPressure(step.mslAltitude);
+        force -= (p0 - p) * ae;
+    }
+}
+
+void MEA::correctBaro(const Step &step)
 {
-    const auto filterState  = filter.getState();
-    const auto filterOutput = filter.getOutput();
-
-    state.timestamp         = TimestampTimer::getTimestamp();
-    state.correctedPressure = filterOutput(0);
-    state.x0                = filterState(0);
-    state.x1                = filterState(1);
-    state.x2                = filterState(2);
+    if (!step.hasCCPressure)
+        return;
+
+    float S               = baroH * P * baroH.transpose() + baroR;
+    Matrix<float, 3, 1> K = (P * baroH.transpose()) / S;
+
+    P = (Matrix<float, 3, 3>::Identity() - K * baroH) * P;
+    x = x + K * (step.ccPressure - baroH * x);
 }
 
-}  // namespace Boardcore
+void MEA::correctAccel(const Step &step)
+{
+    if (!step.hasAcceleration || !step.hasSpeedAndAlt)
+        return;
+
+    // Do not correct at low speed/acceleration
+    if (step.acceleration.norm() < accelThresh ||
+        step.verticalSpeed < speedThresh)
+        return;
+
+    float y = (Kt * (float)(baroH * x) - force) / x(2);
+
+    Matrix<float, 1, 3> accelH =
+        Vector<float, 3>{Kt * baroH(0) / x(2), Kt * baroH(1) / x(2),
+                         -(Kt * (float)(baroH * x) - force) / (x(2) * x(2))}
+            .transpose();
+
+    float accelR = alpha * q + c;
+
+    float S               = accelH * P * accelH.transpose() + accelR;
+    Matrix<float, 3, 1> K = (P * accelH.transpose()) / S;
+
+    P = (Matrix<float, 3, 3>::Identity() - K * accelH) * P;
+    x = x + K * (step.acceleration.x() - y);
+}
+
+void MEA::computeApogee(const Step &step)
+{
+    if (!step.hasSpeedAndAlt)
+        return;
+
+    float mass = x(2);
+
+    // TODO: Matlab uses CD_correction_shutDown, should we?
+    // Holy fuck, massive formula, split this for the love of god
+    apogee = step.mslAltitude +
+             1 / (2 * ((0.5f * rho * cd * crossSection) / mass)) *
+                 log1p(1 + (step.verticalSpeed * step.verticalSpeed *
+                            ((0.5f * rho * cd * crossSection) / mass)) /
+                               Constants::g);
+}
+
+void MEA::updateState()
+{
+    state.timestamp = TimestampTimer::getTimestamp();
+
+    state.x0 = x(0);
+    state.x1 = x(1);
+
+    state.estimatedMass     = x(2);
+    state.estimatedPressure = baroH * x;
+    state.estimatedApogee   = apogee;
+}
\ No newline at end of file
diff --git a/src/shared/algorithms/MEA/MEA.h b/src/shared/algorithms/MEA/MEA.h
index d69bc5bb01b660d5cf8aea51e4a3cc5ebe936ee6..56467767a1ca4bd22a51456129a62f42cdf42baf 100644
--- a/src/shared/algorithms/MEA/MEA.h
+++ b/src/shared/algorithms/MEA/MEA.h
@@ -1,5 +1,5 @@
-/* Copyright (c) 2023 Skyward Experimental Rocketry
- * Author: Matteo Pignataro
+/* Copyright (c) 2024 Skyward Experimental Rocketry
+ * Author: Davide Mor
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -22,53 +22,124 @@
 
 #pragma once
 
-#include <algorithms/Kalman/Kalman.h>
+#include <algorithms/MEA/MEAData.h>
+#include <sensors/SensorData.h>
+#include <utils/AeroUtils/AeroUtils.h>
 
-#include "MEAData.h"
+#include <Eigen/Dense>
 
 namespace Boardcore
 {
 
-/**
- * @brief MEA stands for Mass Estimation Algorithm.
- *
- * It represents a kalman filter which by performing a prediction with the
- * current feed valve state and a correction with the pressure in combustion
- * chamber, estimates the remaining mass of the rocket.
- */
 class MEA
 {
 public:
-    using KalmanFilter = Kalman<float, 3, 1, 1>;
+    struct Config
+    {
+        Eigen::Matrix<float, 3, 3> F;  //< State propagation matrix
+        Eigen::Matrix<float, 3, 3> Q;  //< Model variance matrix
+        Eigen::Vector<float, 3> G;     //< Input vector
 
-    explicit MEA(const KalmanFilter::KalmanConfig kalmanConfig);
+        Eigen::Vector<float, 3> baroH;  //< Barometer output matrix
+        float baroR;                    //< Barometer measurement variance
 
-    /**
-     * @brief Update the Kalman filter.
-     *
-     * @param feedValvePosition Position reference of the feed valve [0-1]
-     * @param pressure Measured pressure inside the combustion chamber [Pa]
-     */
-    void update(const float feedValvePosition, const float pressure);
+        Eigen::Matrix<float, 3, 3> P;  //< Error covariance matrix
 
-    /**
-     * @brief Returns the MEA data including the estimated mass.
-     */
-    MEAState getState();
+        float initialMass;  //< Initial mass of the rocket
+
+        float accelThresh;  //< Minimum required acceleration to trigger accel
+                            // correction.
+        float speedThresh;  //< Minumum required speed to trigger accel
+                            // correction.
+
+        float Kt;     //< TODO: What is this?
+        float alpha;  //< TODO: What is this?
+        float c;      //< TODO: What is this?
+
+        Aeroutils::AerodynamicCoeff coeffs;  //< Aerodynamic coefficients.
+        float crossSection;                  //< Cross section of the rocket.
+
+        float ae;  //< TODO: What is this?
+        float p0;  //< Pressure at nozzle exit
+    };
+
+    struct Step
+    {
+        float mainValveOpen =
+            0.0f;  //< How open is the main valve? (between 0-1)
+
+        float ccPressure =
+            0.0f;  //< What is the current combustion chamber pressure? [bar]
+        bool hasCCPressure = false;  //< Is ccPressure valid?
+
+        Eigen::Vector<float, 3> acceleration =
+            Eigen::Vector<float, 3>::Zero();  //< Acceleration [m/s^2]
+        bool hasAcceleration = false;         //< Is acceleration valid?
+
+        float verticalSpeed =
+            0.0f;  //< Vertical component of speed [up positive] [m/s]
+        float mslAltitude = 0.0f;  //< Mean sea level altitude [m]
+        bool hasSpeedAndAlt =
+            false;  //< Are verticalSpeed and mslAltitude valid?
+
+        explicit Step(float mainValveOpen);
+
+        void withCCPressure(PressureData ccPressure);
+        void withCCPressure(float ccPressure);
+
+        void withAcceleration(AccelerometerData acceleration);
+        void withAcceleration(Eigen::Vector<float, 3> acceleration);
 
-    /**
-     * @brief Sets the Kalman filter configuration.
-     */
-    void setKalmanConfig(KalmanFilter::KalmanConfig config);
+        void withSpeedAndAlt(float verticalSpeed, float mslAltitude);
+    };
+
+    explicit MEA(const Config &config);
+
+    void update(const Step &step);
+
+    MEAState getState();
 
 private:
-    /**
-     * @brief Computes useful data from the kalman current state.
-     */
+    void predict(const Step &step);
+    void computeForce(const Step &step);
+    void correctBaro(const Step &step);
+    void correctAccel(const Step &step);
+    void computeApogee(const Step &step);
     void updateState();
 
-    KalmanFilter filter;
+    Eigen::Matrix<float, 3, 3> F;  //< State propagation matrix
+    Eigen::Matrix<float, 3, 3> Q;  //< Model variance matrix
+
+    Eigen::Vector<float, 3> G;  //< Input Vector
+
+    Eigen::Matrix<float, 1, 3> baroH;
+    float baroR;
+
+    Eigen::Matrix<float, 3, 3> P;  //< Error covariance matrix
+    Eigen::Vector<float, 3> x;     //< State vector
+
+    float mach  = 0.0f;  //< Latest computed mach
+    float cd    = 0.0f;  //< Latest computed CD
+    float rho   = 0.0f;  //< Latest computed rho
+    float q     = 0.0f;  //< Latest computed dynamic pressure
+    float force = 0.0f;  //< Latest computed force
+
+    float apogee = 0.0f;  //< Latest computed apogee
+
+    float accelThresh;
+    float speedThresh;
+
+    float Kt;
+    float alpha;
+    float c;
+
+    Aeroutils::AerodynamicCoeff coeffs;
+    float crossSection;
+
+    float ae;
+    float p0;
+
     MEAState state;
 };
 
-}  // namespace Boardcore
+}  // namespace Boardcore
\ No newline at end of file
diff --git a/src/shared/algorithms/MEA/MEAData.h b/src/shared/algorithms/MEA/MEAData.h
index 1a3dc708642c4c1dbfd246e494a7f7f297c5cabc..6164a8b7a9c2c1a85f8b5828e2128d653a66ce23 100644
--- a/src/shared/algorithms/MEA/MEAData.h
+++ b/src/shared/algorithms/MEA/MEAData.h
@@ -1,5 +1,5 @@
-/* Copyright (c) 2023 Skyward Experimental Rocketry
- * Author: Matteo Pignataro
+/* Copyright (c) 2024 Skyward Experimental Rocketry
+ * Author: Davide Mor
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,7 @@
 
 #pragma once
 
+#include <cstdint>
 #include <ostream>
 
 namespace Boardcore
@@ -30,21 +31,25 @@ namespace Boardcore
 struct MEAState
 {
     uint64_t timestamp;
-    float correctedPressure;
+
+    float estimatedPressure;
+    float estimatedMass;
+    float estimatedApogee;
+
     float x0;
     float x1;
-    float x2;
 
     static std::string header()
     {
-        return "timestamp,correctedPressure,x0,x1,x2\n";
+        return "timestamp,estimatedPressure,estimatedMass,estimatedApogee,x0,"
+               "x1\n";
     }
 
-    void print(std::ostream& os) const
+    void print(std::ostream &os) const
     {
-        os << timestamp << "," << correctedPressure << "," << x0 << "," << x1
-           << "," << x2 << "\n";
+        os << timestamp << "," << estimatedPressure << "," << estimatedMass
+           << "," << estimatedApogee << "," << x0 << "," << x1 << "\n";
     }
 };
 
-}  // namespace Boardcore
+}  // namespace Boardcore
\ No newline at end of file
diff --git a/src/shared/utils/AeroUtils/AeroUtils.cpp b/src/shared/utils/AeroUtils/AeroUtils.cpp
index 1ec63ecabc80c730de76342e82940fb388c69370..b1592df1e325c99ad024ff50f8a2ad0ae4aa7540 100644
--- a/src/shared/utils/AeroUtils/AeroUtils.cpp
+++ b/src/shared/utils/AeroUtils/AeroUtils.cpp
@@ -119,6 +119,13 @@ float computePitotAirspeed(float pressureTotal, float pressureStatic, float d,
     return M * c;
 }
 
+float computeCd(const AerodynamicCoeff& coeff, float mach)
+{
+    return coeff.n000 + coeff.n100 * mach + coeff.n200 * powf(mach, 2) +
+           coeff.n300 * powf(mach, 3) + coeff.n400 * powf(mach, 4) +
+           coeff.n500 * powf(mach, 5) + coeff.n600 * powf(mach, 6);
+}
+
 }  // namespace Aeroutils
 
 }  // namespace Boardcore
diff --git a/src/shared/utils/AeroUtils/AeroUtils.h b/src/shared/utils/AeroUtils/AeroUtils.h
index 96223d359399355a3a2f83621e647b9bfe0477c1..cc4a91d453744d790983144cf05cb2b0bf132e2b 100644
--- a/src/shared/utils/AeroUtils/AeroUtils.h
+++ b/src/shared/utils/AeroUtils/AeroUtils.h
@@ -231,6 +231,20 @@ float computePitotMach(float pressureTotal, float pressureStatic);
 float computePitotAirspeed(float pressureTotal, float pressureStatic, float d,
                            float t0);
 
+struct AerodynamicCoeff
+{
+    float n000, n100, n200, n300, n400, n500, n600;
+};
+
+/**
+ * @brief Computes the CD from aerodynamic coefficients and mach.
+ *
+ * @param coeff Aerodynamic coefficients.
+ * @param mach Mach value.
+ * @return The computed CD.
+ */
+float computeCd(const AerodynamicCoeff& coeff, float mach);
+
 }  // namespace Aeroutils
 
 }  // namespace Boardcore
diff --git a/src/tests/catch/test-MEA.cpp b/src/tests/catch/test-MEA.cpp
index 6a4d64a68fb871a2d4173a7457f1e21756f1f445..257a52cf0cce2985d258745655b996987cf6c61e 100644
--- a/src/tests/catch/test-MEA.cpp
+++ b/src/tests/catch/test-MEA.cpp
@@ -30,6 +30,7 @@
 using namespace Boardcore;
 using namespace Eigen;
 
+/*
 constexpr float sensorNoiseVariance = 0.36f;
 constexpr float modelNoiseVariance  = 0.1f;
 constexpr float initialRocketMass   = 35.01f;
@@ -40,17 +41,43 @@ MEA::KalmanFilter::KalmanConfig getMEAKalmanConfig()
 
     // clang-format off
     config.F = MEA::KalmanFilter::MatrixNN({
-        {1.435871191228868, -0.469001276508780,  0.f}, 
+        {1.435871191228868, -0.469001276508780,  0.f},
         {1.f,                0.f,                0.f},
         {-0.002045309260755, 0.001867496708935,  1.f}});
-    
+
     config.H = {1.780138883879285,-1.625379384370081,0.f};
 
     config.P    = MEA::KalmanFilter::MatrixNN::Zero();
-    config.Q    = modelNoiseVariance * MEA::KalmanFilter::CVectorN({1, 1, 1}).asDiagonal();
-    config.R[0] = sensorNoiseVariance;
-    config.G    = MEA::KalmanFilter::MatrixNM{{4}, {0}, {0}};
-    config.x    = {0, 0, initialRocketMass};
+    config.Q    = modelNoiseVariance * MEA::KalmanFilter::CVectorN({1, 1,
+1}).asDiagonal(); config.R[0] = sensorNoiseVariance; config.G    =
+MEA::KalmanFilter::MatrixNM{{4}, {0}, {0}}; config.x    = {0, 0,
+initialRocketMass};
+    // clang-format on
+
+    return config;
+}
+*/
+
+MEA::Config getMEAConfig()
+{
+    MEA::Config config;
+
+    // clang-format off
+    config.F = Matrix<float, 3, 3>({
+        {1.435871191228868, -0.469001276508780,  0.f}, 
+        {1.f,                0.f,                0.f},
+        {-0.002045309260755, 0.001867496708935,  1.f}});
+    config.Q = Matrix<float, 3, 3>({
+        {0.1f, 0.0f, 0.0f}, 
+        {0.0f, 0.1f, 0.0f},
+        {0.0f, 0.0f, 0.1f}});
+    config.G = Matrix<float, 3, 1>{{4}, {0}, {0}};
+    
+    config.baroH = {1.780138883879285,-1.625379384370081,0.f};
+    config.baroR = 0.36f;
+
+    config.P           = Matrix<float, 3, 3>::Zero();
+    config.initialMass = 35.01f;
     // clang-format on
 
     return config;
@@ -58,31 +85,36 @@ MEA::KalmanFilter::KalmanConfig getMEAKalmanConfig()
 
 TEST_CASE("MEA Update Test")
 {
-    MEA mea(getMEAKalmanConfig());
+    MEA mea(getMEAConfig());
     MEAState state;
 
-    std::cout << "pressure,estimatedPressure,estimatedMass,command"
-              << std::endl;
+    std::cout << MEAState::header();
 
     for (unsigned i = 1; i < PRESSURE.size(); i++)
     {
         // Update the kalman
-        mea.update(COMMAND[i - 1], PRESSURE[i]);
+        MEA::Step step{COMMAND[i - 1]};
+        step.withCCPressure(PRESSURE[i]);
+
+        mea.update(step);
 
         // Get the results
         state = mea.getState();
 
-        if (state.x2 != Approx(ESTIMATED_MASS[i]).epsilon(0.01))
+        state.print(std::cout);
+
+        if (state.estimatedMass != Approx(ESTIMATED_MASS[i]).epsilon(0.01))
         {
             FAIL("The estimated mass differs from the correct one ["
-                 << i << "]: " << state.x2 << " != " << ESTIMATED_MASS[i]);
+                 << i << "]: " << state.estimatedMass
+                 << " != " << ESTIMATED_MASS[i]);
         }
 
-        if (state.correctedPressure !=
+        if (state.estimatedPressure !=
             Approx(ESTIMATED_PRESSURE[i]).epsilon(0.01))
         {
             FAIL("The estimated pressure differs from the correct one ["
-                 << i << "]: " << state.correctedPressure
+                 << i << "]: " << state.estimatedPressure
                  << " != " << ESTIMATED_PRESSURE[i]);
         }
     }