From 9c33e3f3eff30c53d23ce4d7a0da3c91fc73bfa1 Mon Sep 17 00:00:00 2001
From: Davide Basso <davide.basso@skywarder.eu>
Date: Sat, 14 Dec 2024 23:00:02 +0100
Subject: [PATCH] [Parafoil] Add AltitudeTrigger

Co-Authored-By: BearToCode <davidebasso03@gmail.com>
---
 .vscode/settings.json                         |   1 +
 src/Parafoil/Actuators/Actuators.cpp          |  14 ++-
 src/Parafoil/Actuators/Actuators.h            |   8 +-
 .../AltitudeTrigger/AltitudeTrigger.cpp       |  95 ++++++++++++++
 .../AltitudeTrigger/AltitudeTrigger.h         |  86 +++++++++++++
 src/Parafoil/BoardScheduler.h                 |   1 +
 src/Parafoil/Configs/WingConfig.h             | 119 ++++++++++++++++++
 src/Parafoil/parafoil-entry.cpp               |   9 +-
 src/Payload/AltitudeTrigger/AltitudeTrigger.h |   2 +-
 9 files changed, 323 insertions(+), 12 deletions(-)
 create mode 100644 src/Parafoil/AltitudeTrigger/AltitudeTrigger.cpp
 create mode 100644 src/Parafoil/AltitudeTrigger/AltitudeTrigger.h
 create mode 100644 src/Parafoil/Configs/WingConfig.h

diff --git a/.vscode/settings.json b/.vscode/settings.json
index c6fa1d36e..f43029172 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -152,6 +152,7 @@
         "GPIOF",
         "GPIOG",
         "hwmap",
+        "JESOLO",
         "KALM",
         "kalman",
         "Leds",
diff --git a/src/Parafoil/Actuators/Actuators.cpp b/src/Parafoil/Actuators/Actuators.cpp
index 7b25f46ec..02324701b 100644
--- a/src/Parafoil/Actuators/Actuators.cpp
+++ b/src/Parafoil/Actuators/Actuators.cpp
@@ -29,6 +29,7 @@
 using namespace std::chrono;
 using namespace miosix;
 using namespace Boardcore;
+using namespace Boardcore::Units::Angle;
 namespace config = Parafoil::Config::Actuators;
 
 namespace Parafoil
@@ -40,13 +41,13 @@ Actuators::Actuators()
         config::LeftServo::TIMER, config::LeftServo::PWM_CH,
         config::LeftServo::MIN_PULSE.count(),
         config::LeftServo::MAX_PULSE.count());
-    leftServo.fullRangeAngle = config::LeftServo::ROTATION.value();
+    leftServo.fullRangeAngle = config::LeftServo::ROTATION;
 
     rightServo.servo = std::make_unique<Servo>(
         config::RightServo::TIMER, config::RightServo::PWM_CH,
         config::RightServo::MIN_PULSE.count(),
         config::RightServo::MAX_PULSE.count());
-    rightServo.fullRangeAngle = config::RightServo::ROTATION.value();
+    rightServo.fullRangeAngle = config::RightServo::ROTATION;
 }
 
 bool Actuators::start()
@@ -80,7 +81,7 @@ bool Actuators::setServoPosition(ServosList servoId, float position)
     return true;
 }
 
-bool Actuators::setServoAngle(ServosList servoId, float angle)
+bool Actuators::setServoAngle(ServosList servoId, Degree angle)
 {
     auto actuator = getServoActuator(servoId);
     if (!actuator)
@@ -88,7 +89,8 @@ bool Actuators::setServoAngle(ServosList servoId, float angle)
 
     miosix::Lock<miosix::FastMutex> lock(actuator->mutex);
 
-    actuator->servo->setPosition(angle / actuator->fullRangeAngle);
+    actuator->servo->setPosition(
+        (angle / actuator->fullRangeAngle.value()).value());
 
     return true;
 }
@@ -104,11 +106,11 @@ float Actuators::getServoPosition(ServosList servoId)
     return actuator->servo->getPosition();
 }
 
-float Actuators::getServoAngle(ServosList servoId)
+Degree Actuators::getServoAngle(ServosList servoId)
 {
     auto actuator = getServoActuator(servoId);
     if (!actuator)
-        return -1.f;
+        return Degree(-1.f);
 
     miosix::Lock<miosix::FastMutex> lock(actuator->mutex);
 
diff --git a/src/Parafoil/Actuators/Actuators.h b/src/Parafoil/Actuators/Actuators.h
index 524a42ede..673b6bb81 100644
--- a/src/Parafoil/Actuators/Actuators.h
+++ b/src/Parafoil/Actuators/Actuators.h
@@ -24,6 +24,7 @@
 
 #include <actuators/Servo/Servo.h>
 #include <common/MavlinkOrion.h>
+#include <units/Angle.h>
 #include <utils/DependencyManager/DependencyManager.h>
 
 #include <utils/ModuleManager/ModuleManager.hpp>
@@ -41,7 +42,8 @@ public:
     struct ServoActuator
     {
         std::unique_ptr<Boardcore::Servo> servo;
-        float fullRangeAngle;  ///< The full range of the servo [degrees]
+        Units::Angle::Degree fullRangeAngle =
+            Units::Angle::Degree(0);  ///< The full range of the servo [degrees]
         miosix::FastMutex mutex;
     };
 
@@ -67,7 +69,7 @@ public:
      * @param angle Angle to set [degree].
      * @return True if the the angle was set.
      */
-    bool setServoAngle(ServosList servoId, float angle);
+    bool setServoAngle(ServosList servoId, Units::Angle::Degree angle);
 
     /**
      * @brief Wiggles the servo for few seconds.
@@ -112,7 +114,7 @@ public:
      * @return float current Servo angle in range [0-180], (-1) if the servoId
      * is invalid.
      */
-    float getServoAngle(ServosList servoId);
+    Units::Angle::Degree getServoAngle(ServosList servoId);
 
     /**
      * @brief Starts twirl (one servo is set to 0 and the other one is not).
diff --git a/src/Parafoil/AltitudeTrigger/AltitudeTrigger.cpp b/src/Parafoil/AltitudeTrigger/AltitudeTrigger.cpp
new file mode 100644
index 000000000..7806a1a6c
--- /dev/null
+++ b/src/Parafoil/AltitudeTrigger/AltitudeTrigger.cpp
@@ -0,0 +1,95 @@
+/* Copyright (c) 2024 Skyward Experimental Rocketry
+ * Authors: Federico Mandelli, Niccolò Betto, Davide Basso
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "AltitudeTrigger.h"
+
+#include <Parafoil/BoardScheduler.h>
+#include <Parafoil/Configs/WingConfig.h>
+#include <Parafoil/StateMachines/NASController/NASController.h>
+#include <common/Events.h>
+#include <events/EventBroker.h>
+
+using namespace Boardcore;
+using namespace Common;
+namespace config = Parafoil::Config;
+
+namespace Parafoil
+{
+
+bool AltitudeTrigger::start()
+{
+    auto& scheduler = getModule<BoardScheduler>()->altitudeTrigger();
+
+    auto task = scheduler.addTask([this] { update(); },
+                                  config::AltitudeTrigger::UPDATE_RATE);
+
+    if (task == 0)
+        return false;
+
+    started = true;
+    return true;
+}
+
+bool AltitudeTrigger::isStarted() { return started; }
+
+void AltitudeTrigger::enable()
+{
+    if (running)
+        return;
+
+    confidence = 0;
+    running    = true;
+}
+
+void AltitudeTrigger::disable() { running = false; }
+
+bool AltitudeTrigger::isEnabled() { return running; }
+
+void AltitudeTrigger::setDeploymentAltitude(float altitude)
+{
+    targetAltitude = altitude;
+}
+
+void AltitudeTrigger::update()
+{
+    if (!running)
+        return;
+
+    // NED frame, flip the D sign to get above-ground-level altitude
+    auto nasState  = getModule<NASController>()->getNasState();
+    float altitude = -nasState.d;
+
+    if (altitude < targetAltitude)
+        confidence++;
+    else
+        confidence = 0;
+
+    if (confidence >= config::AltitudeTrigger::CONFIDENCE)
+    {
+        confidence = 0;
+        EventBroker::getInstance().post(ALTITUDE_TRIGGER_ALTITUDE_REACHED,
+                                        TOPIC_ALT);
+        running = false;
+    }
+}
+
+}  // namespace Parafoil
diff --git a/src/Parafoil/AltitudeTrigger/AltitudeTrigger.h b/src/Parafoil/AltitudeTrigger/AltitudeTrigger.h
new file mode 100644
index 000000000..f5514b088
--- /dev/null
+++ b/src/Parafoil/AltitudeTrigger/AltitudeTrigger.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Matteo Pignataro
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <Parafoil/Configs/WingConfig.h>
+#include <utils/DependencyManager/DependencyManager.h>
+
+#include <atomic>
+#include <utils/ModuleManager/ModuleManager.hpp>
+
+namespace Parafoil
+{
+class BoardScheduler;
+class NASController;
+
+class AltitudeTrigger
+    : public Boardcore::InjectableWithDeps<BoardScheduler, NASController>
+{
+public:
+    /**
+     * @brief Adds the update() task to the task scheduler.
+     */
+    bool start();
+
+    /**
+     * @return If the AltitudeTrigger is started.
+     */
+    bool isStarted();
+
+    /**
+     * @brief Enable the AltitudeTrigger.
+     */
+    void enable();
+
+    /**
+     * @brief Disable the AltitudeTrigger.
+     */
+    void disable();
+
+    /**
+     * @return The status of the AltitudeTrigger
+     */
+    bool isEnabled();
+
+    /**
+     * @return Set the altitude of the AltitudeTrigger
+     */
+    void setDeploymentAltitude(float altitude);
+
+private:
+    // Update method that posts a FLIGHT_WING_ALT_PASSED when the correct
+    // altitude is reached
+    void update();
+
+    std::atomic<bool> started{false};
+    std::atomic<bool> running{false};
+
+    // Number of times that the algorithm detects to be below the fixed
+    // altitude
+    int confidence = 0;
+
+    std::atomic<float> targetAltitude{
+        Config::AltitudeTrigger::DEPLOYMENT_ALTITUDE.value()};
+};
+
+}  // namespace Parafoil
diff --git a/src/Parafoil/BoardScheduler.h b/src/Parafoil/BoardScheduler.h
index 75a8d06c7..4e8d51eef 100644
--- a/src/Parafoil/BoardScheduler.h
+++ b/src/Parafoil/BoardScheduler.h
@@ -55,6 +55,7 @@ public:
     Boardcore::TaskScheduler& nasController() { return critical; }
     Boardcore::TaskScheduler& sensors() { return high; }
     Boardcore::TaskScheduler& pinHandler() { return high; }
+    Boardcore::TaskScheduler& altitudeTrigger() { return medium; }
     Boardcore::TaskScheduler& actuators() { return low; }
 
     static Priority::PriorityLevel flightModeManagerPriority()
diff --git a/src/Parafoil/Configs/WingConfig.h b/src/Parafoil/Configs/WingConfig.h
new file mode 100644
index 000000000..68315dc5c
--- /dev/null
+++ b/src/Parafoil/Configs/WingConfig.h
@@ -0,0 +1,119 @@
+/* Copyright (c) 2024 Skyward Experimental Rocketry
+ * Authors: Federico Mandelli, Angelo Prete, Niccolò Betto, Davide Basso
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <units/Angle.h>
+#include <units/Frequency.h>
+#include <units/Length.h>
+#include <utils/Constants.h>
+
+#include <chrono>
+
+namespace Parafoil
+{
+namespace Config
+{
+namespace Wing
+{
+
+/* linter off */ using namespace std::chrono_literals;
+/* linter off */ using namespace Boardcore::Units::Frequency;
+/* linter off */ using namespace Boardcore::Units::Length;
+/* linter off */ using namespace Boardcore::Units::Angle;
+
+constexpr auto UPDATE_RATE             = 1_hz;
+constexpr auto STRAIGHT_FLIGHT_TIMEOUT = 15s;
+constexpr auto COMMAND_PERIOD          = 6s;
+constexpr auto WING_DECREMENT          = 30_deg;
+
+/**
+ * @brief The available algorithms for the wing controller.
+ */
+enum class AlgorithmId : size_t
+{
+    CLOSED_LOOP = 0,
+    EARLY_MANEUVER,
+    SEQUENCE,
+    ROTATION,
+    PROGRESSIVE_ROTATION,
+};
+
+namespace Default
+{
+
+#if defined(JESOLO)
+constexpr auto TARGET_LAT = 45.565264f;
+constexpr auto TARGET_LON = 12.577050f;
+#else  // Milan
+constexpr auto TARGET_LAT = 45.5013853;
+constexpr auto TARGET_LON = 9.1544219;
+#endif
+
+#if defined(CLOSED_LOOP)
+constexpr auto ALGORITHM = AlgorithmId::CLOSED_LOOP;
+#elif EARLY_MANEUVER
+constexpr auto ALGORITHM = AlgorithmId::EARLY_MANEUVER;
+#elif SEQUENCE
+constexpr auto ALGORITHM = AlgorithmId::SEQUENCE;
+#elif ROTATION
+constexpr auto ALGORITHM = AlgorithmId::ROTATION;
+#elif PROGRESSIVE_ROTATION
+constexpr auto ALGORITHM = AlgorithmId::PROGRESSIVE_ROTATION;
+#else
+constexpr auto ALGORITHM = AlgorithmId::CLOSED_LOOP;
+#endif
+
+}  // namespace Default
+
+namespace PI
+{
+constexpr auto SATURATION_MIN_LIMIT = -Boardcore::Constants::PI;
+constexpr auto SATURATION_MAX_LIMIT = Boardcore::Constants::PI;
+
+constexpr auto KP = 0.9f;
+constexpr auto KI = 0.05f;
+}  // namespace PI
+
+namespace Guidance
+{
+
+constexpr auto CONFIDENCE                = 15;  // [samples]
+constexpr auto M1_ALTITUDE_THRESHOLD     = 250_m;
+constexpr auto M2_ALTITUDE_THRESHOLD     = 150_m;
+constexpr auto TARGET_ALTITUDE_THRESHOLD = 50_m;
+}  // namespace Guidance
+
+}  // namespace Wing
+
+namespace AltitudeTrigger
+{
+/* linter off */ using namespace Boardcore::Units::Frequency;
+/* linter off */ using namespace Boardcore::Units::Length;
+
+constexpr auto DEPLOYMENT_ALTITUDE = 300_m;
+constexpr auto CONFIDENCE          = 10;  // [samples]
+constexpr auto UPDATE_RATE         = 10_hz;
+}  // namespace AltitudeTrigger
+
+}  // namespace Config
+}  // namespace Parafoil
diff --git a/src/Parafoil/parafoil-entry.cpp b/src/Parafoil/parafoil-entry.cpp
index da56cf303..971f6cf58 100644
--- a/src/Parafoil/parafoil-entry.cpp
+++ b/src/Parafoil/parafoil-entry.cpp
@@ -21,6 +21,7 @@
  */
 
 #include <Parafoil/Actuators/Actuators.h>
+#include <Parafoil/AltitudeTrigger/AltitudeTrigger.h>
 #include <Parafoil/BoardScheduler.h>
 #include <Parafoil/Buses.h>
 #include <Parafoil/PinHandler/PinHandler.h>
@@ -112,6 +113,9 @@ int main()
 
     // TODO: Radio
 
+    // Flight algorithms
+    auto altitudeTrigger = new AltitudeTrigger();
+    initResult &= depman.insert(altitudeTrigger);
     // TODO: Wing Algorithm
 
     // Actuators
@@ -129,6 +133,7 @@ int main()
     START_MODULE(pinHandler);
     // START_MODULE(radio) { miosix::led2On(); }
     START_MODULE(nasController);
+    START_MODULE(altitudeTrigger);
     // START_MODULE(wingController);
     START_MODULE(actuators);
 
@@ -141,12 +146,12 @@ int main()
         EventBroker::getInstance().post(FMM_INIT_OK, TOPIC_FMM);
         // Turn on the initialization led on the CU
         miosix::ledOn();
-        std::cout << "Payload initialization Ok!" << std::endl;
+        std::cout << "Parafoil initialization Ok!" << std::endl;
     }
     else
     {
         EventBroker::getInstance().post(FMM_INIT_ERROR, TOPIC_FMM);
-        std::cerr << "*** Payload initialization error ***" << std::endl;
+        std::cerr << "*** Parafoil initialization error ***" << std::endl;
     }
 
     std::cout << "Sensors status:" << std::endl;
diff --git a/src/Payload/AltitudeTrigger/AltitudeTrigger.h b/src/Payload/AltitudeTrigger/AltitudeTrigger.h
index 657cec82f..faa6e8d90 100644
--- a/src/Payload/AltitudeTrigger/AltitudeTrigger.h
+++ b/src/Payload/AltitudeTrigger/AltitudeTrigger.h
@@ -1,5 +1,5 @@
 /* Copyright (c) 2023-2024 Skyward Experimental Rocketry
- * Authors: Federico Mandelli, Nicclò Betto
+ * Authors: Federico Mandelli, Niccolò Betto
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
-- 
GitLab