diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 294bc9c2dc3a492a9241fe88a31a81b719d02f01..90f09ba85f011a3a6d38a38c56d334eb04901371 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -75,6 +75,7 @@ set(PAYLOAD_COMPUTER src/boards/Payload/CanHandler/CanHandler.cpp src/boards/Payload/FlightStatsRecorder/FlightStatsRecorder.cpp src/boards/Payload/Sensors/Sensors.cpp + src/boards/Payload/Sensors/HILSensors.cpp src/boards/Payload/BoardScheduler.cpp src/boards/Payload/PinHandler/PinHandler.cpp src/boards/Payload/Radio/Radio.cpp diff --git a/src/boards/Payload/Buses.h b/src/boards/Payload/Buses.h index d4f49813721bcd92a32e125a8547633c4bd8cf01..4dcbddab00e4251454ef7d91449f801e89c9aab4 100644 --- a/src/boards/Payload/Buses.h +++ b/src/boards/Payload/Buses.h @@ -48,7 +48,7 @@ public: : spi1(SPI1), spi3(SPI3), spi4(SPI4), spi6(SPI6), i2c1(I2C1, miosix::interfaces::i2c1::scl::getPin(), miosix::interfaces::i2c1::sda::getPin()), - usart1(USART1, 115200), usart2(USART2, 115200), uart4(UART4, 115200) + usart1(USART1, 115200), usart2(USART2, 115200, 1024), uart4(UART4, 115200) { } }; diff --git a/src/boards/Payload/Configs/HILSimulationConfig.h b/src/boards/Payload/Configs/HILSimulationConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..a395aab1e1338bf58f1bb1372d4b5782ae905548 --- /dev/null +++ b/src/boards/Payload/Configs/HILSimulationConfig.h @@ -0,0 +1,624 @@ +/* Copyright (c) 2023-2024 Skyward Experimental Rocketry + * Author: Emilio Corigliano + * + * 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 <Payload/Buses.h> +#include <Payload/StateMachines/FlightModeManager/FlightModeManager.h> +#include <common/Events.h> +#include <drivers/timer/TimestampTimer.h> +#include <drivers/usart/USART.h> +#include <events/EventBroker.h> +#include <math.h> +#include <sensors/HILSensors/IncludeHILSensors.h> +#include <sensors/SensorInfo.h> +#include <utils/Debug.h> +#include <utils/Stats/Stats.h> + +#include <list> +#include <utils/ModuleManager/ModuleManager.hpp> + +// NAS +#include <Payload/StateMachines/NASController/NASControllerData.h> +#include <algorithms/NAS/NASState.h> + +// WingController +#include <Payload/StateMachines/WingController/WingControllerData.h> + +namespace HILConfig +{ + +/** Period of simulation in milliseconds */ +constexpr int SIMULATION_PERIOD = 100; + +/** sample frequency of sensor (samples/second) */ +constexpr int ACCEL_FREQ = 100; +constexpr int GYRO_FREQ = 100; +constexpr int MAGN_FREQ = 100; +constexpr int IMU_FREQ = 100; +constexpr int BARO_FREQ = 50; +constexpr int BARO_CHAMBER_FREQ = 50; +constexpr int PITOT_FREQ = 20; +constexpr int TEMP_FREQ = 10; +constexpr int GPS_FREQ = 10; + +/** Number of samples per sensor at each simulator iteration */ +constexpr int N_DATA_ACCEL = + static_cast<int>((ACCEL_FREQ * SIMULATION_PERIOD) / 1000.0); +constexpr int N_DATA_GYRO = + static_cast<int>((GYRO_FREQ * SIMULATION_PERIOD) / 1000.0); +constexpr int N_DATA_MAGNETO = + static_cast<int>((MAGN_FREQ * SIMULATION_PERIOD) / 1000.0); +constexpr int N_DATA_IMU = + static_cast<int>((IMU_FREQ * SIMULATION_PERIOD) / 1000.0); +constexpr int N_DATA_BARO = + static_cast<int>((BARO_FREQ * SIMULATION_PERIOD) / 1000.0); +constexpr int N_DATA_BARO_CHAMBER = + static_cast<int>((BARO_CHAMBER_FREQ * SIMULATION_PERIOD) / 1000.0); +constexpr int N_DATA_PITOT = + static_cast<int>((PITOT_FREQ * SIMULATION_PERIOD) / 1000.0); +constexpr int N_DATA_GPS = + static_cast<int>((GPS_FREQ * SIMULATION_PERIOD) / 1000.0); +constexpr int N_DATA_TEMP = + static_cast<int>((TEMP_FREQ * SIMULATION_PERIOD) / 1000.0); + +struct FlagsHIL +{ + float flag_flight; + float flag_ascent; + float flag_burning; + float flag_airbrakes; + float flag_para1; + float flag_para2; + + FlagsHIL(float flag_flight, float flag_ascent, float flag_burning, + float flag_airbrakes, float flag_para1, float flag_para2) + : flag_flight(flag_flight), flag_ascent(flag_ascent), + flag_burning(flag_burning), flag_airbrakes(flag_airbrakes), + flag_para1(flag_para1), flag_para2(flag_para2) + { + } + + FlagsHIL() + : flag_flight(0.0f), flag_ascent(0.0f), flag_burning(0.0f), + flag_airbrakes(0.0f), flag_para1(0.0f), flag_para2(0.0f) + { + } + + void print() + { + printf( + "flag_flight: %f\n" + "flag_ascent: %f\n" + "flag_burning: %f\n" + "flag_airbrakes: %f\n" + "flag_para1: %f\n" + "flag_para2: %f\n", + flag_flight, flag_ascent, flag_burning, flag_airbrakes, flag_para1, + flag_para2); + } +}; + +/** + * @brief NAS data sent to the simulator + */ +struct NASStateHIL +{ + float n = 0; + float e = 0; + float d = 0; + + // Velocity [m/s] + float vn = 0; + float ve = 0; + float vd = 0; + + // Attitude as quaternion + float qx = 0; + float qy = 0; + float qz = 0; + float qw = 1; + + float updating = 0; // Flag if apogee is detected [bool] + + NASStateHIL() + : n(0), e(0), d(0), vn(0), ve(0), vd(0), qx(0), qy(0), qz(0), qw(0), + updating(0) + { + } + + NASStateHIL(Boardcore::NASState adaState, + Payload::NASControllerStatus adaStatus) + : n(adaState.n), e(adaState.e), d(adaState.d), vn(adaState.vn), + ve(adaState.ve), vd(adaState.vd), qx(adaState.qx), qy(adaState.qy), + qz(adaState.qz), qw(adaState.qw), + updating(adaStatus.state == Payload::NASControllerState::ACTIVE) + { + } + + void print() + { + printf( + "n: %+.3f\n" + "e: %+.3f\n" + "d: %+.3f\n" + "vn: %+.3f\n" + "ve: %+.3f\n" + "vd: %+.3f\n" + "qx: %+.3f\n" + "qy: %+.3f\n" + "qz: %+.3f\n" + "qw: %+.3f\n" + "updating: %+.3f\n", + n, e, d, vn, ve, vd, qx, qy, qz, qw, updating); + } +}; + +struct ActuatorsStateHIL +{ + float airbrakesPercentage = 0; // + float expulsionPercentage = 0; + float parafoilLeftPercentage = 0; + float parafoilRightPercentage = 0; + float mainValvePercentage = 0; // + float ventingValvePercentage = 0; // + float releaseValvePercentage = 0; + float fillingValvePercentage = 0; // + float disconnectValvePercentage = 0; // + + ActuatorsStateHIL() + : airbrakesPercentage(0.0f), expulsionPercentage(0.0f), + parafoilLeftPercentage(0.0f), parafoilRightPercentage(0.0f), + mainValvePercentage(0.0f), ventingValvePercentage(0.0f), + releaseValvePercentage(0.0f), fillingValvePercentage(0.0f), + disconnectValvePercentage(0.0f) + { + } + + ActuatorsStateHIL(float airbrakesPercentage, float expulsionPercentage, + float parafoilLeftPercentage, + float parafoilRightPercentage, float mainValvePercentage, + float ventingValvePercentage, + float releaseValvePercentage, + float fillingValvePercentage, + float disconnectValvePercentage) + : airbrakesPercentage(airbrakesPercentage), + expulsionPercentage(expulsionPercentage), + parafoilLeftPercentage(parafoilLeftPercentage), + parafoilRightPercentage(parafoilRightPercentage), + mainValvePercentage(mainValvePercentage), + ventingValvePercentage(ventingValvePercentage), + releaseValvePercentage(releaseValvePercentage), + fillingValvePercentage(fillingValvePercentage), + disconnectValvePercentage(disconnectValvePercentage) + { + } + + void print() + { + printf( + "airbrakes: %f perc\n" + "expulsion: %f perc\n" + "parafoilLeft: %f perc\n" + "parafoilRight: %f perc\n" + "mainValve: %f perc\n" + "ventingValve: %f perc\n" + "releaseValve: %f perc\n" + "fillingValve: %f perc\n" + "disconnectValve: %f perc\n", + airbrakesPercentage * 100, expulsionPercentage * 100, + parafoilLeftPercentage * 100, parafoilRightPercentage * 100, + mainValvePercentage * 100, ventingValvePercentage * 100, + releaseValvePercentage * 100, fillingValvePercentage * 100, + disconnectValvePercentage * 100); + } +}; + +struct WESDataHIL +{ + float windX; + float windY; + + WESDataHIL(Eigen::Vector2f wind) : windX(wind[0]), windY(wind[1]) {} + + WESDataHIL() : windX(0.0f), windY(0.0f) {} + + void print() { printf("wind: [%f,%f]\n", windX, windY); } +}; + +struct GuidanceDataHIL +{ + float psiRef; + float deltaA; + + GuidanceDataHIL(float psiRef, float deltaA) : psiRef(psiRef), deltaA(deltaA) + { + } + + GuidanceDataHIL() : psiRef(0.0f), deltaA(0.0f) {} + + void print() + { + printf( + "psiRef: %f\n" + "deltaA: %f\n", + psiRef, deltaA); + } +}; + +/** + * @brief Data structure used by the simulator in order to directly deserialize + * the data received + * + * This structure then is accessed by sensors and other components in order to + * get the data they need + */ +struct SimulatorData +{ + struct AccelerometerSimulatorData<N_DATA_ACCEL> accelerometer; + struct GyroscopeSimulatorData<N_DATA_GYRO> gyro; + struct MagnetometerSimulatorData<N_DATA_MAGNETO> magnetometer; + struct GPSSimulatorData<N_DATA_GPS> gps; + struct BarometerSimulatorData<N_DATA_BARO> barometer1, barometer2, + barometer3; + struct BarometerSimulatorData<N_DATA_BARO_CHAMBER> pressureChamber; + struct PitotSimulatorData<N_DATA_PITOT> pitot; + struct TemperatureSimulatorData<N_DATA_TEMP> temperature; + struct FlagsHIL flags; + + void print() + { + printf("%f,%f,%f\n", accelerometer.measures[0][0], + accelerometer.measures[0][1], accelerometer.measures[0][2]); + printf("%f,%f,%f\n", gyro.measures[0][0], + gyro.measures[0][1], gyro.measures[0][2]); + printf("%f,%f,%f\n", magnetometer.measures[0][0], + magnetometer.measures[0][1], magnetometer.measures[0][2]); + printf("%f\n", temperature.measures[0]); + flags.print(); + } +}; + +/** + * @brief Data strudcture expected by the simulator + */ +struct ActuatorData +{ + NASStateHIL nasState; + ActuatorsStateHIL actuatorsState; + WESDataHIL wesData; + GuidanceDataHIL guidanceData; + FlagsHIL flags; + + ActuatorData() + : nasState(), actuatorsState(), wesData(), guidanceData(), flags() + { + } + + ActuatorData(NASStateHIL nasState, ActuatorsStateHIL actuatorsState, + WESDataHIL wesData, GuidanceDataHIL guidanceData, + FlagsHIL flagsIn, Payload::FlightModeManager* fmm) + : nasState(nasState), actuatorsState(actuatorsState), wesData(wesData), + guidanceData(guidanceData) + { + flags.flag_flight = + (fmm->testState(&Payload::FlightModeManager::state_ascending) || + fmm->testState( + &Payload::FlightModeManager::state_drogue_descent) || + fmm->testState( + &Payload::FlightModeManager::state_wing_descent) + ? 1 + : 0); + flags.flag_ascent = + (fmm->testState(&Payload::FlightModeManager::state_ascending) ? 1 + : 0); + flags.flag_burning = flagsIn.flag_burning; + flags.flag_airbrakes = flagsIn.flag_airbrakes; + flags.flag_para1 = + (fmm->testState(&Payload::FlightModeManager::state_drogue_descent) + ? 1 + : 0); + flags.flag_para2 = + (fmm->testState(&Payload::FlightModeManager::state_wing_descent) + ? 1 + : 0); + } + + void print() + { + nasState.print(); + actuatorsState.print(); + wesData.print(); + guidanceData.print(); + flags.print(); + } +}; + +enum PayloadFlightPhases +{ + SIM_FLYING, + SIM_ASCENT, + SIM_BURNING, + SIM_AEROBRAKES, + SIM_PARA1, + SIM_PARA2, + SIMULATION_STARTED, + CALIBRATION, + CALIBRATION_OK, + ARMED, + LIFTOFF_PIN_DETACHED, + LIFTOFF, + AEROBRAKES, + APOGEE, + PARA1, + PARA2, + SIMULATION_STOPPED +}; + +using PayloadHILAccelerometer = HILAccelerometer<N_DATA_ACCEL>; +using PayloadHILGyroscope = HILGyroscope<N_DATA_GYRO>; +using PayloadHILMagnetometer = HILMagnetometer<N_DATA_MAGNETO>; +using PayloadHILGps = HILGps<N_DATA_GPS>; +using PayloadHILBarometer = HILBarometer<N_DATA_BARO>; +using PayloadHILBarometer = HILBarometer<N_DATA_BARO_CHAMBER>; +using PayloadHILPitot = HILPitot<N_DATA_PITOT>; +using PayloadHILTemperature = HILTemperature<N_DATA_TEMP>; + +using PayloadHILTransceiver = + HILTransceiver<PayloadFlightPhases, SimulatorData, ActuatorData>; +using PayloadHIL = HIL<PayloadFlightPhases, SimulatorData, ActuatorData>; + +class PayloadHILPhasesManager + : public HILPhasesManager<PayloadFlightPhases, SimulatorData, ActuatorData> +{ +public: + explicit PayloadHILPhasesManager( + std::function<Boardcore::TimedTrajectoryPoint()> getCurrentPosition) + : HILPhasesManager<PayloadFlightPhases, SimulatorData, ActuatorData>( + getCurrentPosition) + { + flagsFlightPhases = {{PayloadFlightPhases::SIM_FLYING, false}, + {PayloadFlightPhases::SIM_ASCENT, false}, + {PayloadFlightPhases::SIM_BURNING, false}, + {PayloadFlightPhases::SIM_AEROBRAKES, false}, + {PayloadFlightPhases::SIM_PARA1, false}, + {PayloadFlightPhases::SIM_PARA2, false}, + {PayloadFlightPhases::SIMULATION_STARTED, false}, + {PayloadFlightPhases::CALIBRATION, false}, + {PayloadFlightPhases::CALIBRATION_OK, false}, + {PayloadFlightPhases::ARMED, false}, + {PayloadFlightPhases::LIFTOFF_PIN_DETACHED, false}, + {PayloadFlightPhases::LIFTOFF, false}, + {PayloadFlightPhases::AEROBRAKES, false}, + {PayloadFlightPhases::APOGEE, false}, + {PayloadFlightPhases::PARA1, false}, + {PayloadFlightPhases::PARA2, false}, + {PayloadFlightPhases::SIMULATION_STOPPED, false}}; + + prev_flagsFlightPhases = flagsFlightPhases; + + auto& eventBroker = Boardcore::EventBroker::getInstance(); + eventBroker.subscribe(this, Common::TOPIC_ABK); + eventBroker.subscribe(this, Common::TOPIC_ADA); + eventBroker.subscribe(this, Common::TOPIC_DPL); + eventBroker.subscribe(this, Common::TOPIC_CAN); + eventBroker.subscribe(this, Common::TOPIC_FLIGHT); + eventBroker.subscribe(this, Common::TOPIC_FMM); + eventBroker.subscribe(this, Common::TOPIC_FSR); + eventBroker.subscribe(this, Common::TOPIC_NAS); + eventBroker.subscribe(this, Common::TOPIC_TMTC); + eventBroker.subscribe(this, Common::TOPIC_MOTOR); + eventBroker.subscribe(this, Common::TOPIC_TARS); + eventBroker.subscribe(this, Common::TOPIC_ALT); + } + + void processFlags(const SimulatorData& simulatorData) override + { + updateSimulatorFlags(simulatorData); + + std::vector<PayloadFlightPhases> changed_flags; + + // set true when the first packet from the simulator arrives + if (isSetTrue(PayloadFlightPhases::SIMULATION_STARTED)) + { + t_start = Boardcore::TimestampTimer::getTimestamp(); + + TRACE("[HIL] ------- SIMULATION STARTED ! ------- \n"); + changed_flags.push_back(PayloadFlightPhases::SIMULATION_STARTED); + } + + if (flagsFlightPhases[PayloadFlightPhases::SIM_FLYING]) + { + if (isSetTrue(PayloadFlightPhases::SIM_FLYING)) + { + registerOutcomes(PayloadFlightPhases::SIM_FLYING); + TRACE("[HIL] ------- SIMULATOR LIFTOFF ! ------- \n"); + changed_flags.push_back(PayloadFlightPhases::SIM_FLYING); + } + } + + /* calling the callbacks subscribed to the changed flags */ + for (unsigned int i = 0; i < changed_flags.size(); i++) + { + std::vector<TCallback> callbacksToCall = + callbacks[changed_flags[i]]; + for (unsigned int j = 0; j < callbacksToCall.size(); j++) + { + callbacksToCall[j](); + } + } + + prev_flagsFlightPhases = flagsFlightPhases; + } + + void printOutcomes() + { + printf("OUTCOMES: (times dt from liftoff)\n\n"); + printf("Simulation time: %.3f [sec]\n\n", + (double)(t_stop - t_start) / 1000000.0f); + + printf("Motor stopped burning (simulation flag): \n"); + outcomes[PayloadFlightPhases::SIM_BURNING].print(t_liftoff); + + printf("Airbrakes exit shadowmode: \n"); + outcomes[PayloadFlightPhases::AEROBRAKES].print(t_liftoff); + + printf("Apogee: \n"); + outcomes[PayloadFlightPhases::APOGEE].print(t_liftoff); + + printf("Parachute 1: \n"); + outcomes[PayloadFlightPhases::PARA1].print(t_liftoff); + + printf("Parachute 2: \n"); + outcomes[PayloadFlightPhases::PARA2].print(t_liftoff); + + printf("Simulation Stopped: \n"); + outcomes[PayloadFlightPhases::SIMULATION_STOPPED].print(t_liftoff); + + // auto cpuMeter = Boardcore::CpuMeter::getCpuStats(); + // printf("max cpu usage: %+.3f\n", cpuMeter.maxValue); + // printf("avg cpu usage: %+.3f\n", cpuMeter.mean); + // printf("min free heap: %+.3lu\n", cpuMeter.minFreeHeap); + // printf("max free stack:%+.3lu\n", cpuMeter.minFreeStack); + } + +private: + void handleEvent(const Boardcore::Event& e) override + { + std::vector<PayloadFlightPhases> changed_flags; + switch (e) + { + case Common::Events::FMM_INIT_ERROR: + printf("[HIL] ------- INIT FAILED ! ------- \n"); + case Common::Events::FMM_INIT_OK: + setFlagFlightPhase(PayloadFlightPhases::CALIBRATION, true); + TRACE("[HIL] ------- CALIBRATION ! ------- \n"); + changed_flags.push_back(PayloadFlightPhases::CALIBRATION); + break; + case Common::Events::FLIGHT_DISARMED: + setFlagFlightPhase(PayloadFlightPhases::CALIBRATION_OK, true); + TRACE("[HIL] CALIBRATION OK!\n"); + changed_flags.push_back(PayloadFlightPhases::CALIBRATION_OK); + break; + case Common::Events::FLIGHT_ARMED: + setFlagFlightPhase(PayloadFlightPhases::ARMED, true); + printf("[HIL] ------- READY TO LAUNCH ! ------- \n"); + changed_flags.push_back(PayloadFlightPhases::ARMED); + break; + case Common::Events::FLIGHT_LAUNCH_PIN_DETACHED: + setFlagFlightPhase(PayloadFlightPhases::LIFTOFF_PIN_DETACHED, + true); + TRACE("[HIL] ------- LIFTOFF PIN DETACHED ! ------- \n"); + changed_flags.push_back( + PayloadFlightPhases::LIFTOFF_PIN_DETACHED); + break; + case Common::Events::FLIGHT_LIFTOFF: + case Common::Events::TMTC_FORCE_LAUNCH: + t_liftoff = Boardcore::TimestampTimer::getTimestamp(); + printf("[HIL] ------- LIFTOFF -------: %f, %f \n", + getCurrentPosition().z, getCurrentPosition().vz); + changed_flags.push_back(PayloadFlightPhases::LIFTOFF); + break; + case Common::Events::FLIGHT_APOGEE_DETECTED: + case Common::Events::CAN_APOGEE_DETECTED: + case Common::Events::TMTC_FORCE_APOGEE: + setFlagFlightPhase(PayloadFlightPhases::AEROBRAKES, false); + registerOutcomes(PayloadFlightPhases::APOGEE); + printf("[HIL] ------- APOGEE DETECTED ! ------- %f, %f \n", + getCurrentPosition().z, getCurrentPosition().vz); + changed_flags.push_back(PayloadFlightPhases::APOGEE); + break; + case Common::Events::FLIGHT_DROGUE_DESCENT: + case Common::Events::TMTC_FORCE_EXPULSION: + setFlagFlightPhase(PayloadFlightPhases::PARA1, true); + registerOutcomes(PayloadFlightPhases::PARA1); + printf("[HIL] ------- PARA1 ! -------%f, %f \n", + getCurrentPosition().z, getCurrentPosition().vz); + changed_flags.push_back(PayloadFlightPhases::PARA1); + break; + case Common::Events::FLIGHT_WING_DESCENT: + case Common::Events::FLIGHT_DPL_ALT_DETECTED: + case Common::Events::TMTC_FORCE_DEPLOYMENT: + setFlagFlightPhase(PayloadFlightPhases::PARA1, false); + setFlagFlightPhase(PayloadFlightPhases::PARA2, true); + registerOutcomes(PayloadFlightPhases::PARA2); + printf("[HIL] ------- PARA2 ! ------- %f, %f \n", + getCurrentPosition().z, getCurrentPosition().vz); + changed_flags.push_back(PayloadFlightPhases::PARA2); + break; + case Common::Events::FLIGHT_LANDING_DETECTED: + case Common::Events::TMTC_FORCE_LANDING: + t_stop = Boardcore::TimestampTimer::getTimestamp(); + setFlagFlightPhase(PayloadFlightPhases::PARA2, false); + setFlagFlightPhase(PayloadFlightPhases::SIMULATION_STOPPED, + true); + changed_flags.push_back( + PayloadFlightPhases::SIMULATION_STOPPED); + registerOutcomes(PayloadFlightPhases::SIMULATION_STOPPED); + TRACE("[HIL] ------- SIMULATION STOPPED ! -------: %f \n\n\n", + (double)t_stop / 1000000.0f); + printOutcomes(); + break; + default: + TRACE("%s event\n", Common::getEventString(e).c_str()); + } + + /* calling the callbacks subscribed to the changed flags */ + for (unsigned int i = 0; i < changed_flags.size(); i++) + { + std::vector<TCallback> callbacksToCall = + callbacks[changed_flags[i]]; + for (unsigned int j = 0; j < callbacksToCall.size(); j++) + { + callbacksToCall[j](); + } + } + + prev_flagsFlightPhases = flagsFlightPhases; + } + + /** + * @brief Updates the flags of the object with the flags sent from matlab + * and checks for the apogee + */ + void updateSimulatorFlags(const SimulatorData& simulatorData) + { + flagsFlightPhases[PayloadFlightPhases::SIM_ASCENT] = + simulatorData.flags.flag_ascent; + flagsFlightPhases[PayloadFlightPhases::SIM_FLYING] = + simulatorData.flags.flag_flight; + flagsFlightPhases[PayloadFlightPhases::SIM_BURNING] = + simulatorData.flags.flag_burning; + flagsFlightPhases[PayloadFlightPhases::SIM_AEROBRAKES] = + simulatorData.flags.flag_airbrakes; + flagsFlightPhases[PayloadFlightPhases::SIM_PARA1] = + simulatorData.flags.flag_para1; + flagsFlightPhases[PayloadFlightPhases::SIM_PARA2] = + simulatorData.flags.flag_para2; + + flagsFlightPhases[PayloadFlightPhases::SIMULATION_STOPPED] = + isSetFalse(PayloadFlightPhases::SIM_FLYING) || + prev_flagsFlightPhases[PayloadFlightPhases::SIMULATION_STOPPED]; + } +}; + +} // namespace HILConfig \ No newline at end of file diff --git a/src/boards/Payload/Sensors/HILSensors.cpp b/src/boards/Payload/Sensors/HILSensors.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e57944ba95e34810a796145295c48119c9061a27 --- /dev/null +++ b/src/boards/Payload/Sensors/HILSensors.cpp @@ -0,0 +1,503 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Authors: Matteo Pignataro, Emilio Corigliano + * + * 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 "HILSensors.h" + +#include <Payload/Buses.h> +#include <Payload/Configs/HILSimulationConfig.h> +#include <Payload/Configs/SensorsConfig.h> +#include <common/ReferenceConfig.h> +#include <interfaces-impl/hwmapping.h> + +using namespace Boardcore; +using namespace std; +using namespace Payload::SensorsConfig; +using namespace HILConfig; + +namespace Payload +{ +LPS22DFData HILSensors::getLPS22DFLastSample() +{ + miosix::PauseKernelLock lock; + return (lps22df != nullptr + ? LPS22DFData{lps22df->getLastSample().pressureTimestamp, + lps22df->getLastSample().pressure, + temperature->getLastSample().temperature} + : LPS22DFData{}); +} +LPS28DFWData HILSensors::getLPS28DFW_1LastSample() +{ + miosix::PauseKernelLock lock; + return lps28dfw_1 != nullptr + ? LPS28DFWData{lps28dfw_1->getLastSample().pressureTimestamp, + lps28dfw_1->getLastSample().pressure, + temperature->getLastSample().temperatureTimestamp, + temperature->getLastSample().temperature} + : LPS28DFWData{}; +} +LPS28DFWData HILSensors::getLPS28DFW_2LastSample() +{ + miosix::PauseKernelLock lock; + return lps28dfw_2 != nullptr + ? LPS28DFWData{lps28dfw_2->getLastSample().pressureTimestamp, + lps28dfw_2->getLastSample().pressure, + temperature->getLastSample().temperatureTimestamp, + temperature->getLastSample().temperature} + : LPS28DFWData{}; +} +H3LIS331DLData HILSensors::getH3LIS331DLLastSample() +{ + miosix::PauseKernelLock lock; + return h3lis331dl != nullptr + ? H3LIS331DLData{static_cast<Boardcore::AccelerometerData>( + h3lis331dl->getLastSample())} + : H3LIS331DLData{}; +} +LIS2MDLData HILSensors::getLIS2MDLLastSample() +{ + miosix::PauseKernelLock lock; + return lis2mdl != nullptr + ? LIS2MDLData{static_cast<Boardcore::MagnetometerData>( + lis2mdl->getLastSample()), + static_cast<Boardcore::TemperatureData>( + temperature->getLastSample())} + : LIS2MDLData{}; +} +UBXGPSData HILSensors::getGPSLastSample() +{ + if (ubxgps == nullptr) + { + return UBXGPSData{}; + } + UBXGPSData lastSample; + + miosix::PauseKernelLock lock; + GPSData gps = static_cast<Boardcore::GPSData>(ubxgps->getLastSample()); + lastSample.ubxTime = UBXDateTime{}; + lastSample.gpsTimestamp = gps.gpsTimestamp; + lastSample.latitude = gps.latitude; + lastSample.longitude = gps.longitude; + lastSample.height = gps.height; + lastSample.velocityNorth = gps.velocityNorth; + lastSample.velocityEast = gps.velocityEast; + lastSample.velocityDown = gps.velocityDown; + lastSample.speed = gps.speed; + lastSample.track = gps.track; + lastSample.positionDOP = gps.positionDOP; + lastSample.satellites = gps.satellites; + lastSample.fix = gps.fix; + return lastSample; +} +LSM6DSRXData HILSensors::getLSM6DSRXLastSample() +{ + if (lsm6dsrx_accel == nullptr || lsm6dsrx_gyro == nullptr) + { + return LSM6DSRXData{}; + } + + LSM6DSRXData lastSample; + + HILAccelerometerData accelData; + HILGyroscopeData gyroData; + { + miosix::PauseKernelLock lock; + accelData = lsm6dsrx_accel->getLastSample(); + gyroData = lsm6dsrx_gyro->getLastSample(); + } + + lastSample.accelerationTimestamp = accelData.accelerationTimestamp; + lastSample.accelerationX = accelData.accelerationX; + lastSample.accelerationY = accelData.accelerationY; + lastSample.accelerationZ = accelData.accelerationZ; + + lastSample.angularSpeedTimestamp = gyroData.angularSpeedTimestamp; + lastSample.angularSpeedX = gyroData.angularSpeedX; + lastSample.angularSpeedY = gyroData.angularSpeedY; + lastSample.angularSpeedZ = gyroData.angularSpeedZ; + + return lastSample; +} +ADS131M08Data HILSensors::getADS131M08LastSample() +{ + miosix::PauseKernelLock lock; + return ads131m08 != nullptr ? ads131m08->getLastSample() : ADS131M08Data{}; + return ADS131M08Data{}; +} + +Boardcore::HSCMRNN015PAData HILSensors::getStaticPressureLastSample() +{ + Boardcore::HSCMRNN015PAData data; + miosix::PauseKernelLock lock; + + auto baro = getLPS22DFLastSample(); + data.pressureTimestamp = baro.pressureTimestamp; + data.pressure = baro.pressure; + + return data; +} + +Boardcore::SSCMRNN030PAData HILSensors::getDynamicPressureLastSample() +{ + Boardcore::SSCMRNN030PAData data; + miosix::PauseKernelLock lock; + + // data.pressureTimestamp = + // dynamicPressure->getLastSample().pressureTimestamp; data.pressure = + // dynamicPressure->getLastSample().pressure; + + return data; +} + +Boardcore::PitotData HILSensors::getPitotLastSample() +{ + Boardcore::PitotData data; + miosix::PauseKernelLock lock; + + auto sample = pitot->getLastSample(); + data.timestamp = sample.timestamp; + data.airspeed = sample.airspeed; + data.deltaP = sample.deltaP; + + return data; +} + +HILSensors::HILSensors(TaskScheduler* sched) : Payload::Sensors(sched) {} + +bool HILSensors::start() +{ + // Init all the sensors + temperatureInit(); + LOG_INFO(logger, "temperatureInit\n"); + lps22dfInit(); + LOG_INFO(logger, "lps22dfInit\n"); + lps28dfw_1Init(); + LOG_INFO(logger, "lps28dfw_1Init\n"); + lps28dfw_2Init(); + LOG_INFO(logger, "lps28dfw_2Init\n"); + h3lis331dlInit(); + LOG_INFO(logger, "h3lis331dlInit\n"); + lis2mdlInit(); + LOG_INFO(logger, "lis2mdlInit\n"); + ubxgpsInit(); + LOG_INFO(logger, "ubxgpsInit\n"); + lsm6dsrxInit(); + LOG_INFO(logger, "lsm6dsrxInit\n"); + // ads131m08Init(); + // LOG_INFO(logger, "ads131m08Init\n"); + staticPressureInit(); + LOG_INFO(logger, "staticPressureInit\n"); + dynamicPressureInit(); + LOG_INFO(logger, "dynamicPressureInit\n"); + pitotInit(); + LOG_INFO(logger, "pitotInit\n"); + imuInit(); + LOG_INFO(logger, "imuInit\n"); + + // Add the magnetometer calibration to the scheduler + size_t result = scheduler->addTask( + [&]() + { + // Gather the last sample data + MagnetometerData lastSample = getLIS2MDLLastSample(); + + // Feed the data to the calibrator inside a protected area. + // Contention is not high and the use of a mutex is suitable to + // avoid pausing the kernel for this calibration operation + { + miosix::Lock<FastMutex> l(calibrationMutex); + magCalibrator.feed(lastSample); + } + }, + MAG_CALIBRATION_PERIOD); + + // Create sensor manager with populated map and configured scheduler + manager = new SensorManager(sensorMap, scheduler); + return manager->start() && result != 0; +} + +void HILSensors::temperatureInit() +{ + temperature = + new PayloadHILTemperature(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->temperature); + + // Emplace the sensor inside the map + SensorInfo info("TEMP_PayloadHIL", 1000 / TEMP_FREQ); + + sensorMap.emplace(make_pair(temperature, info)); +} + +void HILSensors::lps22dfInit() +{ + // Create sensor instance with configured parameters + lps22df = new PayloadHILBarometer(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->barometer1); + + // Emplace the sensor inside the map + SensorInfo info("LPS22DF_PayloadHIL", LPS22DF_PERIOD, + bind(&HILSensors::lps22dfCallback, this)); + sensorMap.emplace(make_pair(lps22df, info)); +} + +void HILSensors::lps28dfw_1Init() +{ + // Create sensor instance with configured parameters + lps28dfw_1 = + new PayloadHILBarometer(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->barometer2); + + // Emplace the sensor inside the map + SensorInfo info("LPS28DFW_1_PayloadHIL", LPS28DFW_PERIOD, + bind(&HILSensors::lps28dfw_1Callback, this)); + sensorMap.emplace(make_pair(lps28dfw_1, info)); +} + +void HILSensors::lps28dfw_2Init() +{ + // Create sensor instance with configured parameters + lps28dfw_2 = + new PayloadHILBarometer(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->barometer3); + + // Emplace the sensor inside the map + SensorInfo info("LPS28DFW_2_PayloadHIL", LPS28DFW_PERIOD, + bind(&HILSensors::lps28dfw_2Callback, this)); + sensorMap.emplace(make_pair(lps28dfw_2, info)); +} + +void HILSensors::pitotInit() +{ + // create lambda function to read the pressure + pitot = new PayloadHILPitot(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->pitot); + + // Emplace the sensor inside the map + SensorInfo info("Pitot", 1000 / PITOT_FREQ, + bind(&HILSensors::pitotCallback, this)); + sensorMap.emplace(make_pair(pitot, info)); +} + +void HILSensors::h3lis331dlInit() +{ + // Create sensor instance with configured parameters + h3lis331dl = + new PayloadHILAccelerometer(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->accelerometer); + + // Emplace the sensor inside the map + SensorInfo info("H3LIS331DL_PayloadHIL", H3LIS331DL_PERIOD, + bind(&HILSensors::h3lis331dlCallback, this)); + sensorMap.emplace(make_pair(h3lis331dl, info)); +} + +void HILSensors::lis2mdlInit() +{ + // Create sensor instance with configured parameters + lis2mdl = + new PayloadHILMagnetometer(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->magnetometer); + + // Emplace the sensor inside the map + SensorInfo info("LIS2MDL_PayloadHIL", LIS2MDL_PERIOD, + bind(&HILSensors::lis2mdlCallback, this)); + sensorMap.emplace(make_pair(lis2mdl, info)); +} + +void HILSensors::ubxgpsInit() +{ + // Create sensor instance with configured parameters + ubxgps = new PayloadHILGps(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->gps); + + // Emplace the sensor inside the map + SensorInfo info("UBXGPS_PayloadHIL", UBXGPS_PERIOD, + bind(&HILSensors::ubxgpsCallback, this)); + sensorMap.emplace(make_pair(ubxgps, info)); +} + +void HILSensors::lsm6dsrxInit() +{ + // Create sensor instance with configured parameters + lsm6dsrx_accel = + new PayloadHILAccelerometer(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->accelerometer); + lsm6dsrx_gyro = + new PayloadHILGyroscope(&Boardcore::ModuleManager::getInstance() + .get<PayloadHIL>() + ->hilTransceiver->getSensorData() + ->gyro); + + // Emplace the sensor inside the map + SensorInfo info("LSM6DSRX", LSM6DSRX_PERIOD, + bind(&HILSensors::lsm6dsrxCallback, this)); + sensorMap.emplace(make_pair(lsm6dsrx_accel, info)); + sensorMap.emplace(make_pair(lsm6dsrx_gyro, info)); +} + +void HILSensors::ads131m08Init() +{ + // ModuleManager& modules = ModuleManager::getInstance(); + + // Configure the SPI + // SPIBusConfig config; + // config.clockDivider = SPI::ClockDivider::DIV_32; + + // // Configure the device + // ADS131M08::Config sensorConfig; + // sensorConfig.oversamplingRatio = ADS131M08_OVERSAMPLING_RATIO; + // sensorConfig.globalChopModeEnabled = ADS131M08_GLOBAL_CHOP_MODE; + + // // Create the sensor instance with configured parameters + // ads131m08 = new ADS131M08(modules.get<Buses>()->spi4, + // miosix::sensors::ADS131::cs::getPin(), config, + // sensorConfig); + + // // Emplace the sensor inside the map + // SensorInfo info("ADS131M08", ADS131M08_PERIOD, + // bind(&HILSensors::ads131m08Callback, this)); + // sensorMap.emplace(make_pair(ads131m08, info)); +} + +void HILSensors::staticPressureInit() +{ + // // Create sensor instance with configured parameters + // staticPressure = + // new PayloadHILBarometer(&Boardcore::ModuleManager::getInstance() + // .get<PayloadHIL>() + // ->hilTransceiver->getSensorData() + // ->staticPitot); + + // // Emplace the sensor inside the map + // SensorInfo info("StaticPitot", 1000 / PITOT_FREQ, + // bind(&HILSensors::staticPressureCallback, this)); + // sensorMap.emplace(make_pair(staticPressure, info)); +} + +void HILSensors::dynamicPressureInit() +{ + // // Create sensor instance with configured parameters + // dynamicPressure = new PayloadHILBarometer( + // N_DATA_PITOT, &Boardcore::ModuleManager::getInstance() + // .get<PayloadHIL>() + // ->hilTransceiver->getSensorData() + // ->dynamicPitot); + + // // Emplace the sensor inside the map + // SensorInfo info("DynamicPitot", 1000 / PITOT_FREQ, + // bind(&HILSensors::dynamicPressureCallback, this)); + // sensorMap.emplace(make_pair(dynamicPressure, info)); +} + +void HILSensors::imuInit() +{ + // Register the IMU as the fake sensor, passing as parameters the + // methods to retrieve real data. The sensor is not synchronized, but + // the sampling thread is always the same. + + imu = new RotatedIMU( + bind(&HILSensors::getLSM6DSRXLastSample, this), + bind(&HILSensors::getCalibratedMagnetometerLastSample, this), + bind(&HILSensors::getLSM6DSRXLastSample, this)); + + // Invert the Y axis on the magnetometer + // Eigen::Matrix3f m{{1, 0, 0}, {0, -1, 0}, {0, 0, 1}}; + // imu->addMagTransformation(m); + + // Emplace the sensor inside the map (TODO CHANGE PERIOD INTO NON MAGIC) + SensorInfo info("RotatedIMU", IMU_PERIOD, + bind(&HILSensors::imuCallback, this)); + sensorMap.emplace(make_pair(imu, info)); +} + +void HILSensors::dynamicPressureCallback() { return; } +void HILSensors::pitotCallback() { return; } +void HILSensors::lps22dfCallback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log(getLPS22DFLastSample()); +} +void HILSensors::lps28dfw_1Callback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log( + static_cast<LPS28DFW_1Data>(getLPS28DFW_1LastSample())); +} +void HILSensors::lps28dfw_2Callback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log( + static_cast<LPS28DFW_2Data>(getLPS28DFW_2LastSample())); +} +void HILSensors::h3lis331dlCallback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log(getH3LIS331DLLastSample()); +} +void HILSensors::lis2mdlCallback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log(getLIS2MDLLastSample()); +} +void HILSensors::ubxgpsCallback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log(getGPSLastSample()); +} +void HILSensors::lsm6dsrxCallback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log(getLSM6DSRXLastSample()); +} +void HILSensors::ads131m08Callback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log(getADS131M08LastSample()); +} +void HILSensors::staticPressureCallback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log(getStaticPressureLastSample()); +} +void HILSensors::imuCallback() +{ + miosix::PauseKernelLock lock; + Logger::getInstance().log(getIMULastSample()); +} +} // namespace Payload \ No newline at end of file diff --git a/src/boards/Payload/Sensors/HILSensors.h b/src/boards/Payload/Sensors/HILSensors.h new file mode 100644 index 0000000000000000000000000000000000000000..85830b74d5b83bad213ff26cbc87b3c4c89ee23d --- /dev/null +++ b/src/boards/Payload/Sensors/HILSensors.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Authors: Matteo Pignataro, Emilio Corigliano + * + * 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 <Payload/Configs/HILSimulationConfig.h> +#include <sensors/HILSensors/IncludeHILSensors.h> +#include <sensors/SensorManager.h> +#include <sensors/analog/Pitot/Pitot.h> + +#include <utils/ModuleManager/ModuleManager.hpp> + +#include "Sensors.h" + +namespace Payload +{ + +class HILSensors : public Sensors +{ +public: + explicit HILSensors(Boardcore::TaskScheduler* sched); + + [[nodiscard]] bool start() override; + + // Sensor getters + Boardcore::LPS22DFData getLPS22DFLastSample() override; + Boardcore::LPS28DFWData getLPS28DFW_1LastSample() override; + Boardcore::LPS28DFWData getLPS28DFW_2LastSample() override; + Boardcore::H3LIS331DLData getH3LIS331DLLastSample() override; + Boardcore::LIS2MDLData getLIS2MDLLastSample() override; + Boardcore::UBXGPSData getGPSLastSample() override; + Boardcore::LSM6DSRXData getLSM6DSRXLastSample() override; + Boardcore::ADS131M08Data getADS131M08LastSample() override; + + Boardcore::HSCMRNN015PAData getStaticPressureLastSample() override; + Boardcore::SSCMRNN030PAData getDynamicPressureLastSample() override; + Boardcore::PitotData getPitotLastSample() override; + +private: + // Init and callbacks methods + void temperatureInit(); + + void lps22dfInit() override; + void lps22dfCallback() override; + + void lps28dfw_1Init(); + void lps28dfw_1Callback(); + + void lps28dfw_2Init(); + void lps28dfw_2Callback(); + + void h3lis331dlInit() override; + void h3lis331dlCallback() override; + + void lis2mdlInit() override; + void lis2mdlCallback() override; + + void ubxgpsInit() override; + void ubxgpsCallback() override; + + void lsm6dsrxInit() override; + void lsm6dsrxCallback() override; + + void ads131m08Init() override; + void ads131m08Callback() override; + + void staticPressureInit() override; + void staticPressureCallback() override; + + void dynamicPressureInit() override; + void dynamicPressureCallback() override; + + void pitotInit() override; + void pitotCallback() override; + + void imuInit() override; + void imuCallback() override; + + // HILSensors instances + HILConfig::PayloadHILTemperature* temperature = nullptr; + HILConfig::PayloadHILBarometer* lps22df = nullptr; + HILConfig::PayloadHILBarometer* lps28dfw_1 = nullptr; + HILConfig::PayloadHILBarometer* lps28dfw_2 = nullptr; + HILConfig::PayloadHILAccelerometer* h3lis331dl = nullptr; + HILConfig::PayloadHILMagnetometer* lis2mdl = nullptr; + HILConfig::PayloadHILGps* ubxgps = nullptr; + HILConfig::PayloadHILAccelerometer* lsm6dsrx_accel = nullptr; + HILConfig::PayloadHILGyroscope* lsm6dsrx_gyro = nullptr; + HILConfig::PayloadHILPitot* pitot = nullptr; + HILConfig::PayloadHILBarometer* staticPressure = nullptr; + HILConfig::PayloadHILBarometer* dynamicPressure = nullptr; + // Boardcore::ADS131M08* ads131m08 = nullptr; + + Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("HILSensors"); +}; +} // namespace Payload \ No newline at end of file diff --git a/src/boards/Payload/Sensors/Sensors.h b/src/boards/Payload/Sensors/Sensors.h index faeee0b846d2c6b90b85ad3772128a35ce119fe8..98845bf44824f6584273dd6fdd7b71f19cbb4df3 100644 --- a/src/boards/Payload/Sensors/Sensors.h +++ b/src/boards/Payload/Sensors/Sensors.h @@ -51,23 +51,23 @@ class Sensors : public Boardcore::Module public: explicit Sensors(Boardcore::TaskScheduler* sched); - [[nodiscard]] bool start(); + [[nodiscard]] virtual bool start(); /** * @brief Stops the sensor manager * @warning Stops the passed scheduler */ - void stop(); + virtual void stop(); /** * @brief Returns if all the sensors are started successfully */ - bool isStarted(); + virtual bool isStarted(); /** * @brief Calibrates the sensors with an offset */ - void calibrate(); + virtual void calibrate(); /** * @brief Takes the result of the live magnetometer calibration and applies @@ -75,70 +75,70 @@ public: * * @return true if the write was successful */ - bool writeMagCalibration(); + virtual bool writeMagCalibration(); // Sensor getters - Boardcore::LPS22DFData getLPS22DFLastSample(); - Boardcore::LPS28DFWData getLPS28DFW_1LastSample(); - Boardcore::LPS28DFWData getLPS28DFW_2LastSample(); - Boardcore::H3LIS331DLData getH3LIS331DLLastSample(); - Boardcore::LIS2MDLData getLIS2MDLLastSample(); - Boardcore::UBXGPSData getGPSLastSample(); - Boardcore::LSM6DSRXData getLSM6DSRXLastSample(); - Boardcore::ADS131M08Data getADS131M08LastSample(); + virtual Boardcore::LPS22DFData getLPS22DFLastSample(); + virtual Boardcore::LPS28DFWData getLPS28DFW_1LastSample(); + virtual Boardcore::LPS28DFWData getLPS28DFW_2LastSample(); + virtual Boardcore::H3LIS331DLData getH3LIS331DLLastSample(); + virtual Boardcore::LIS2MDLData getLIS2MDLLastSample(); + virtual Boardcore::UBXGPSData getGPSLastSample(); + virtual Boardcore::LSM6DSRXData getLSM6DSRXLastSample(); + virtual Boardcore::ADS131M08Data getADS131M08LastSample(); // Processed getters - Boardcore::BatteryVoltageSensorData getBatteryVoltageLastSample(); - Boardcore::BatteryVoltageSensorData getCamBatteryVoltageLastSample(); - Boardcore::CurrentData getCurrentLastSample(); - RotatedIMUData getIMULastSample(); - Boardcore::MagnetometerData getCalibratedMagnetometerLastSample(); - Boardcore::HSCMRNN015PAData getStaticPressureLastSample(); - Boardcore::SSCMRNN030PAData getDynamicPressureLastSample(); - Boardcore::PitotData getPitotLastSample(); + virtual Boardcore::BatteryVoltageSensorData getBatteryVoltageLastSample(); + virtual Boardcore::BatteryVoltageSensorData getCamBatteryVoltageLastSample(); + virtual Boardcore::CurrentData getCurrentLastSample(); + virtual RotatedIMUData getIMULastSample(); + virtual Boardcore::MagnetometerData getCalibratedMagnetometerLastSample(); + virtual Boardcore::HSCMRNN015PAData getStaticPressureLastSample(); + virtual Boardcore::SSCMRNN030PAData getDynamicPressureLastSample(); + virtual Boardcore::PitotData getPitotLastSample(); void pitotSetReferenceAltitude(float altitude); void pitotSetReferenceTemperature(float temperature); std::array<Boardcore::SensorInfo, 8> getSensorInfo(); -private: +protected: // Init and callbacks methods - void lps22dfInit(); - void lps22dfCallback(); + virtual void lps22dfInit(); + virtual void lps22dfCallback(); - void lps28dfw_1Init(); - void lps28dfw_1Callback(); + virtual void lps28dfw_1Init(); + virtual void lps28dfw_1Callback(); - void lps28dfw_2Init(); - void lps28dfw_2Callback(); + virtual void lps28dfw_2Init(); + virtual void lps28dfw_2Callback(); - void h3lis331dlInit(); - void h3lis331dlCallback(); + virtual void h3lis331dlInit(); + virtual void h3lis331dlCallback(); - void lis2mdlInit(); - void lis2mdlCallback(); + virtual void lis2mdlInit(); + virtual void lis2mdlCallback(); - void ubxgpsInit(); - void ubxgpsCallback(); + virtual void ubxgpsInit(); + virtual void ubxgpsCallback(); - void lsm6dsrxInit(); - void lsm6dsrxCallback(); + virtual void lsm6dsrxInit(); + virtual void lsm6dsrxCallback(); - void ads131m08Init(); - void ads131m08Callback(); + virtual void ads131m08Init(); + virtual void ads131m08Callback(); - void staticPressureInit(); - void staticPressureCallback(); + virtual void staticPressureInit(); + virtual void staticPressureCallback(); - void dynamicPressureInit(); - void dynamicPressureCallback(); + virtual void dynamicPressureInit(); + virtual void dynamicPressureCallback(); - void pitotInit(); - void pitotCallback(); + virtual void pitotInit(); + virtual void pitotCallback(); - void imuInit(); - void imuCallback(); + virtual void imuInit(); + virtual void imuCallback(); // Sensors instances Boardcore::LPS22DF* lps22df = nullptr; diff --git a/src/boards/Payload/StateMachines/WingController/WingController.h b/src/boards/Payload/StateMachines/WingController/WingController.h index 1fb84ee6638df0990638911734ca37c7d6f9b8c1..e608a1a77943b33477096e3c79f175a7437190ad 100644 --- a/src/boards/Payload/StateMachines/WingController/WingController.h +++ b/src/boards/Payload/StateMachines/WingController/WingController.h @@ -95,6 +95,12 @@ public: bool start() override; + /** + * @brief Instance of the Early Maneuver Guidance Algorithm used by + * AutomaticWingAlgorithm + */ + EarlyManeuversGuidanceAlgorithm emGuidance; + private: /** * @brief Method to add the algorithm in the list @@ -148,12 +154,6 @@ private: */ std::atomic<size_t> selectedAlgorithm; - /** - * @brief Instance of the Early Maneuver Guidance Algorithm used by - * AutomaticWingAlgorithm - */ - EarlyManeuversGuidanceAlgorithm emGuidance; - /** * @brief starts the selected algorithm */ diff --git a/src/entrypoints/Payload/payload-entry.cpp b/src/entrypoints/Payload/payload-entry.cpp index b174e140bdca613a43e8da65d09215c41521cc5e..0a277b411e5f328611a2adcfc4211c0da7f756bb 100644 --- a/src/entrypoints/Payload/payload-entry.cpp +++ b/src/entrypoints/Payload/payload-entry.cpp @@ -26,9 +26,11 @@ #include <Payload/BoardScheduler.h> #include <Payload/Buses.h> #include <Payload/CanHandler/CanHandler.h> +#include <Payload/Configs/HILSimulationConfig.h> #include <Payload/FlightStatsRecorder/FlightStatsRecorder.h> #include <Payload/PinHandler/PinHandler.h> #include <Payload/Radio/Radio.h> +#include <Payload/Sensors/HILSensors.h> #include <Payload/Sensors/Sensors.h> #include <Payload/StateMachines/FlightModeManager/FlightModeManager.h> #include <Payload/StateMachines/NASController/NASController.h> @@ -51,6 +53,9 @@ using namespace Boardcore; using namespace Payload; using namespace Common; +using namespace HILConfig; + +constexpr bool hilSimulationActive = true; int main() { @@ -70,7 +75,9 @@ int main() // Sensors priority (MAX - 1) Sensors* sensors = - new Sensors(scheduler->getScheduler(miosix::PRIORITY_MAX - 1)); + (hilSimulationActive + ? new HILSensors(scheduler->getScheduler(miosix::PRIORITY_MAX - 1)) + : new Sensors(scheduler->getScheduler(miosix::PRIORITY_MAX - 1))); // Other critical components (Max - 2) Radio* radio = new Radio(scheduler->getScheduler(miosix::PRIORITY_MAX - 2)); @@ -100,6 +107,95 @@ int main() Buses* buses = new Buses(); PinHandler* pinHandler = new PinHandler(); + // HIL + if (hilSimulationActive) + { + PayloadHILTransceiver* hilTransceiver = + new PayloadHILTransceiver(buses->usart2); + PayloadHILPhasesManager* hilPhasesManager = new PayloadHILPhasesManager( + [&]() + { return Boardcore::TimedTrajectoryPoint(nas->getNasState()); }); + + auto updateActuatorData = [&]() + { + Boardcore::ModuleManager& modules = + Boardcore::ModuleManager::getInstance(); + + NASStateHIL nasStateHIL(modules.get<NASController>()->getNasState(), + modules.get<NASController>()->getStatus()); + + ActuatorsStateHIL actuatorsStateHIL( + actuators->getServoPosition(ServosList::AIR_BRAKES_SERVO), + actuators->getServoPosition(ServosList::EXPULSION_SERVO), + actuators->getServoPosition(ServosList::PARAFOIL_LEFT_SERVO), + actuators->getServoPosition(ServosList::PARAFOIL_RIGHT_SERVO), + actuators->getServoPosition(ServosList::MAIN_VALVE), + actuators->getServoPosition(ServosList::VENTING_VALVE), + actuators->getServoPosition(ServosList::RELEASE_VALVE), + actuators->getServoPosition(ServosList::FILLING_VALVE), + actuators->getServoPosition(ServosList::DISCONNECT_SERVO)); + + WESDataHIL wesDataHIL(windEstimation->getWindEstimationScheme()); + + auto deltaA = + actuators->getServoPosition(ServosList::PARAFOIL_LEFT_SERVO) - + actuators->getServoPosition(ServosList::PARAFOIL_RIGHT_SERVO); + + Eigen::Vector2f heading; + auto psiRef = wingController->emGuidance.calculateTargetAngle( + {nasStateHIL.n, nasStateHIL.e, nasStateHIL.d}, heading); + + GuidanceDataHIL guidanceData(psiRef, deltaA); + + FlagsHIL flags(hilTransceiver->getSensorData()->flags); + + // Returning the feedback for the simulator + return ActuatorData(nasStateHIL, actuatorsStateHIL, wesDataHIL, + guidanceData, flags, fmm); + }; + + PayloadHIL* hil = new PayloadHIL(hilTransceiver, hilPhasesManager, + updateActuatorData, SIMULATION_PERIOD); + + if (!modules.insert<PayloadHIL>(hil)) + { + initResult = false; + LOG_ERR(logger, "Error inserting the HIL module"); + } + else + { + LOG_INFO(logger, "Inserted the HIL module"); + } + + hilPhasesManager->registerToFlightPhase( + PayloadFlightPhases::SIM_FLYING, + [&]() + { + printf("Flying\n"); + EventBroker::getInstance().post( + Events::CAN_LIFTOFF, Topics::TOPIC_CAN); + }); + + hilPhasesManager->registerToFlightPhase( + PayloadFlightPhases::ARMED, + [&]() + { + printf("ARMED\n"); + EventBroker::getInstance().post( + Events::CAN_LIFTOFF, Topics::TOPIC_CAN); + miosix::ledOn(); + }); + + hilPhasesManager->registerToFlightPhase( + PayloadFlightPhases::CALIBRATION_OK, + [&]() + { + TRACE("ARM COMMAND SENT\n"); + EventBroker::getInstance().post(Events::TMTC_ARM, + Topics::TOPIC_TMTC); + }); + } + // Insert modules if (!modules.insert<BoardScheduler>(scheduler)) { @@ -191,6 +287,20 @@ int main() } // Start modules + if (hilSimulationActive) + { + if (!modules.get<PayloadHIL>()->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the HIL module"); + } + + // Waiting for start of simulation + printf("SimulatorData: %d\n", sizeof(SimulatorData)/sizeof(float)); + printf("ActuatorData: %d\n", sizeof(ActuatorData)/sizeof(float)); + ModuleManager::getInstance().get<PayloadHIL>()->waitStartSimulation(); + } + if (!Logger::getInstance().testSDCard()) { initResult = false;