From cb35f4f46a3390c17a487a303410a7a4d883986b Mon Sep 17 00:00:00 2001 From: Emilio Corigliano <emilio.corigliano@skywarder.eu> Date: Mon, 26 Feb 2024 00:42:00 +0100 Subject: [PATCH] [HIL] Moved HIL framework from OBSW to boardcore WARNING! Not compiling. Useful only for reference on the changes done on the framework wrt before --- CMakeLists.txt | 4 + src/shared/hil/Events.h | 293 ++++++++++++++++ src/shared/hil/HIL.h | 68 ++++ src/shared/hil/HILConfig.h | 51 +++ src/shared/hil/HILFlightPhasesManager.cpp | 296 ++++++++++++++++ src/shared/hil/HILFlightPhasesManager.h | 133 +++++++ src/shared/hil/HILTransceiver.cpp | 157 +++++++++ src/shared/hil/HILTransceiver.h | 83 +++++ src/shared/hil/README.md | 122 +++++++ .../sensors/HILSensors/HILAccelerometer.h | 63 ++++ src/shared/sensors/HILSensors/HILBarometer.h | 60 ++++ src/shared/sensors/HILSensors/HILGps.h | 76 ++++ src/shared/sensors/HILSensors/HILGyroscope.h | 62 ++++ .../sensors/HILSensors/HILMagnetometer.h | 63 ++++ src/shared/sensors/HILSensors/HILPitot.h | 63 ++++ src/shared/sensors/HILSensors/HILSensor.h | 160 +++++++++ .../sensors/HILSensors/HILSensorsData.h | 136 ++++++++ .../sensors/HILSensors/HILTemperature.h | 59 ++++ .../HILSensors/HILTimestampManagement.h | 42 +++ .../sensors/HILSensors/IncludeHILSensors.h | 33 ++ src/tests/hil/HILSimulationConfig.h | 328 ++++++++++++++++++ src/tests/hil/test-hil.cpp | 139 ++++++++ 22 files changed, 2491 insertions(+) create mode 100644 src/shared/hil/Events.h create mode 100644 src/shared/hil/HIL.h create mode 100644 src/shared/hil/HILConfig.h create mode 100644 src/shared/hil/HILFlightPhasesManager.cpp create mode 100644 src/shared/hil/HILFlightPhasesManager.h create mode 100644 src/shared/hil/HILTransceiver.cpp create mode 100644 src/shared/hil/HILTransceiver.h create mode 100644 src/shared/hil/README.md create mode 100644 src/shared/sensors/HILSensors/HILAccelerometer.h create mode 100644 src/shared/sensors/HILSensors/HILBarometer.h create mode 100644 src/shared/sensors/HILSensors/HILGps.h create mode 100644 src/shared/sensors/HILSensors/HILGyroscope.h create mode 100644 src/shared/sensors/HILSensors/HILMagnetometer.h create mode 100644 src/shared/sensors/HILSensors/HILPitot.h create mode 100644 src/shared/sensors/HILSensors/HILSensor.h create mode 100644 src/shared/sensors/HILSensors/HILSensorsData.h create mode 100644 src/shared/sensors/HILSensors/HILTemperature.h create mode 100644 src/shared/sensors/HILSensors/HILTimestampManagement.h create mode 100644 src/shared/sensors/HILSensors/IncludeHILSensors.h create mode 100644 src/tests/hil/HILSimulationConfig.h create mode 100644 src/tests/hil/test-hil.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 820ee31d8..53311e9d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,10 @@ sbs_target(test-taskscheduler stm32f407vg_stm32f4discovery) add_executable(test-trace-logger src/tests/test-trace-logger.cpp) sbs_target(test-trace-logger stm32f429zi_stm32f4discovery) +add_executable(test-hil src/tests/hil/test-hil.cpp) +target_compile_definitions(test-hil PRIVATE HILTest) +sbs_target(test-hil stm32f767zi_compute_unit) + #-----------------------------------------------------------------------------# # Tests - Catch # #-----------------------------------------------------------------------------# diff --git a/src/shared/hil/Events.h b/src/shared/hil/Events.h new file mode 100644 index 000000000..9571ef54a --- /dev/null +++ b/src/shared/hil/Events.h @@ -0,0 +1,293 @@ +/* Copyright (c) 2018-2022 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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 <events/Event.h> + +#include <iostream> +#include <map> +#include <string> +#include <vector> + +namespace Common +{ + +enum Events : uint8_t +{ + ABK_DISABLE = Boardcore::EV_FIRST_CUSTOM, + ABK_OPEN, + ABK_RESET, + ABK_SHADOW_MODE_TIMEOUT, + ABK_WIGGLE, + ADA_CALIBRATE, + ADA_PRESS_STAB_TIMEOUT, + ADA_READY, + ADA_FORCE_START, + ADA_FORCE_STOP, + ADA_SHADOW_MODE_TIMEOUT, + ADA_APOGEE_DETECTED, + MEA_SHUTDOWN_DETECTED, + DPL_CUT_DROGUE, + DPL_CUT_TIMEOUT, + DPL_NC_OPEN, + DPL_NC_RESET, + DPL_SERVO_ACTUATION_DETECTED, + DPL_WIGGLE, + DPL_WES_CAL_DONE, + CAN_FORCE_INIT, + CAN_ARM, + CAN_ENTER_TEST_MODE, + CAN_EXIT_TEST_MODE, + CAN_CALIBRATE, + CAN_DISARM, + CAN_LIFTOFF, + CAN_APOGEE_DETECTED, + CAN_IGNITION, + FLIGHT_APOGEE_DETECTED, + FLIGHT_ARMED, + FLIGHT_DROGUE_DESCENT, + FLIGHT_DISARMED, + FLIGHT_DPL_ALT_DETECTED, + FLIGHT_ERROR_DETECTED, + FLIGHT_LANDING_DETECTED, + FLIGHT_LANDING_TIMEOUT, + FLIGHT_LAUNCH_PIN_DETACHED, + FLIGHT_LIFTOFF, + FLIGHT_MOTOR_SHUTDOWN, + FLIGHT_MISSION_TIMEOUT, + FLIGHT_NC_DETACHED, + FLIGHT_WING_DESCENT, + FMM_ASCENDING, + FMM_ALGOS_CAL_DONE, + FMM_STOP_LOGGING, + FMM_INIT_OK, + FMM_INIT_ERROR, + FMM_CALIBRATE, + FMM_ALGOS_CALIBRATE, + FMM_SENSORS_CAL_DONE, + FMM_READY, + FSR_STATS_TIMEOUT, + NAS_CALIBRATE, + NAS_READY, + NAS_FORCE_START, + NAS_FORCE_STOP, + TMTC_ARM, + TMTC_DISARM, + TMTC_CALIBRATE, + TMTC_FORCE_INIT, + TMTC_FORCE_LAUNCH, + TMTC_FORCE_LANDING, + TMTC_FORCE_APOGEE, + TMTC_FORCE_EXPULSION, + TMTC_FORCE_DEPLOYMENT, + TMTC_START_LOGGING, + TMTC_STOP_LOGGING, + TMTC_RESET_BOARD, + TMTC_ENTER_TEST_MODE, + TMTC_EXIT_TEST_MODE, + TMTC_START_RECORDING, + TMTC_STOP_RECORDING, + MOTOR_START_TARS, + MOTOR_STOP_TARS, + MOTOR_OPEN_VENTING_VALVE, + MOTOR_CLOSE_VENTING_VALVE, + MOTOR_OPEN_FILLING_VALVE, + MOTOR_CLOSE_FILLING_VALVE, + MOTOR_OPEN_RELEASE_VALVE, + MOTOR_CLOSE_RELEASE_VALVE, + MOTOR_DISCONNECT, + MOTOR_IGNITION, + MOTOR_OPEN_FEED_VALVE, + MOTOR_CLOSE_FEED_VALVE, + MOTOR_MANUAL_ACTION, + MOTOR_OPEN_OXIDANT, + MOTOR_SHADOW_MODE_TIMEOUT, + TARS_WASHING_DONE, + TARS_CHECK_PRESSURE_STABILIZE, + TARS_PRESSURE_STABILIZED, + TARS_FILLING_DONE, + TARS_REFINING_DONE, + ALTITUDE_TRIGGER_ALTITUDE_REACHED, + LAST_EVENT +}; + +inline std::string getEventString(uint8_t event) +{ + static const std::map<uint8_t, std::string> event_string_map{ + {ABK_DISABLE, "ABK_DISABLE"}, + {ABK_OPEN, "ABK_OPEN"}, + {ABK_RESET, "ABK_RESET"}, + {ABK_SHADOW_MODE_TIMEOUT, "ABK_SHADOW_MODE_TIMEOUT"}, + {ABK_WIGGLE, "ABK_WIGGLE"}, + {ADA_CALIBRATE, "ADA_CALIBRATE"}, + {ADA_PRESS_STAB_TIMEOUT, "ADA_PRESS_STAB_TIMEOUT"}, + {ADA_READY, "ADA_READY"}, + {ADA_FORCE_START, "ADA_FORCE_START"}, + {ADA_FORCE_STOP, "ADA_FORCE_STOP"}, + {ADA_SHADOW_MODE_TIMEOUT, "ADA_SHADOW_MODE_TIMEOUT"}, + {ADA_APOGEE_DETECTED, "ADA_APOGEE_DETECTED"}, + {MEA_SHUTDOWN_DETECTED, "MEA_SHUTDOWN_DETECTED"}, + {DPL_CUT_DROGUE, "DPL_CUT_DROGUE"}, + {DPL_CUT_TIMEOUT, "DPL_CUT_TIMEOUT"}, + {DPL_NC_OPEN, "DPL_NC_OPEN"}, + {DPL_NC_RESET, "DPL_NC_RESET"}, + {DPL_SERVO_ACTUATION_DETECTED, "DPL_SERVO_ACTUATION_DETECTED"}, + {DPL_WIGGLE, "DPL_WIGGLE"}, + {DPL_WES_CAL_DONE, "DPL_WES_CAL_DONE"}, + {CAN_FORCE_INIT, "CAN_FORCE_INIT"}, + {CAN_ARM, "CAN_ARM"}, + {CAN_ENTER_TEST_MODE, "CAN_ENTER_TEST_MODE"}, + {CAN_EXIT_TEST_MODE, "CAN_EXIT_TEST_MODE"}, + {CAN_CALIBRATE, "CAN_CALIBRATE"}, + {CAN_DISARM, "CAN_DISARM"}, + {CAN_LIFTOFF, "CAN_LIFTOFF"}, + {CAN_APOGEE_DETECTED, "CAN_APOGEE_DETECTED"}, + {CAN_IGNITION, "CAN_IGNITION"}, + {FLIGHT_APOGEE_DETECTED, "FLIGHT_APOGEE_DETECTED"}, + {FLIGHT_ARMED, "FLIGHT_ARMED"}, + {FLIGHT_DROGUE_DESCENT, "FLIGHT_DROGUE_DESCENT"}, + {FLIGHT_DISARMED, "FLIGHT_DISARMED"}, + {FLIGHT_DPL_ALT_DETECTED, "FLIGHT_DPL_ALT_DETECTED"}, + {FLIGHT_ERROR_DETECTED, "FLIGHT_ERROR_DETECTED"}, + {FLIGHT_LANDING_DETECTED, "FLIGHT_LANDING_DETECTED"}, + {FLIGHT_LAUNCH_PIN_DETACHED, "FLIGHT_LAUNCH_PIN_DETACHED"}, + {FLIGHT_LIFTOFF, "FLIGHT_LIFTOFF"}, + {FLIGHT_MOTOR_SHUTDOWN, "FLIGHT_MOTOR_SHUTDOWN"}, + {FLIGHT_LANDING_TIMEOUT, "FLIGHT_LANDING_TIMEOUT"}, + {FLIGHT_NC_DETACHED, "FLIGHT_NC_DETACHED"}, + {FLIGHT_MISSION_TIMEOUT, "FLIGHT_MISSION_TIMEOUT"}, + {FLIGHT_WING_DESCENT, "FLIGHT_WING_DESCENT"}, + {FMM_ASCENDING, "FMM_ASCENDING"}, + {FMM_ALGOS_CAL_DONE, "FMM_ALGOS_CAL_DONE"}, + {FMM_STOP_LOGGING, "FMM_STOP_LOGGING"}, + {FMM_INIT_OK, "FMM_INIT_OK"}, + {FMM_INIT_ERROR, "FMM_INIT_ERROR"}, + {FMM_CALIBRATE, "FMM_CALIBRATE"}, + {FMM_ALGOS_CALIBRATE, "FMM_ALGOS_CALIBRATE"}, + {FMM_SENSORS_CAL_DONE, "FMM_SENSORS_CAL_DONE"}, + {FMM_READY, "FMM_READY"}, + {FSR_STATS_TIMEOUT, "FSR_STATS_TIMEOUT"}, + {NAS_CALIBRATE, "NAS_CALIBRATE"}, + {NAS_FORCE_START, "NAS_FORCE_START"}, + {NAS_FORCE_STOP, "NAS_FORCE_STOP"}, + {NAS_READY, "NAS_READY"}, + {TMTC_ARM, "TMTC_ARM"}, + {TMTC_DISARM, "TMTC_DISARM"}, + {TMTC_CALIBRATE, "TMTC_CALIBRATE"}, + {TMTC_FORCE_INIT, "TMTC_FORCE_INIT"}, + {TMTC_FORCE_LAUNCH, "TMTC_FORCE_LAUNCH"}, + {TMTC_FORCE_LANDING, "TMTC_FORCE_LANDING"}, + {TMTC_FORCE_APOGEE, "TMTC_FORCE_APOGEE"}, + {TMTC_FORCE_EXPULSION, "TMTC_FORCE_EXPULSION"}, + {TMTC_FORCE_DEPLOYMENT, "TMTC_FORCE_DEPLOYMENT"}, + {TMTC_START_LOGGING, "TMTC_START_LOGGING"}, + {TMTC_STOP_LOGGING, "TMTC_STOP_LOGGING"}, + {TMTC_RESET_BOARD, "TMTC_RESET_BOARD"}, + {TMTC_ENTER_TEST_MODE, "TMTC_ENTER_TEST_MODE"}, + {TMTC_EXIT_TEST_MODE, "TMTC_EXIT_TEST_MODE"}, + {TMTC_START_RECORDING, "TMTC_START_RECORDING"}, + {TMTC_STOP_RECORDING, "TMTC_STOP_RECORDING"}, + {MOTOR_START_TARS, "MOTOR_START_TARS"}, + {MOTOR_STOP_TARS, "MOTOR_STOP_TARS"}, + {MOTOR_OPEN_VENTING_VALVE, "MOTOR_OPEN_VENTING_VALVE"}, + {MOTOR_CLOSE_VENTING_VALVE, "MOTOR_CLOSE_VENTING_VALVE"}, + {MOTOR_OPEN_FILLING_VALVE, "MOTOR_OPEN_FILLING_VALVE"}, + {MOTOR_CLOSE_FILLING_VALVE, "MOTOR_CLOSE_FILLING_VALVE"}, + {MOTOR_OPEN_RELEASE_VALVE, "MOTOR_OPEN_RELEASE_VALVE"}, + {MOTOR_CLOSE_RELEASE_VALVE, "MOTOR_CLOSE_RELEASE_VALVE"}, + {MOTOR_DISCONNECT, "MOTOR_DISCONNECT"}, + {MOTOR_IGNITION, "MOTOR_IGNITION"}, + {MOTOR_OPEN_FEED_VALVE, "MOTOR_OPEN_FEED_VALVE"}, + {MOTOR_CLOSE_FEED_VALVE, "MOTOR_CLOSE_FEED_VALVE"}, + {MOTOR_MANUAL_ACTION, "MOTOR_MANUAL_ACTION"}, + {MOTOR_OPEN_OXIDANT, "MOTOR_OPEN_OXIDANT"}, + {MOTOR_SHADOW_MODE_TIMEOUT, "MOTOR_SHADOW_MODE_TIMEOUT"}, + {TARS_WASHING_DONE, "TARS_WASHING_DONE"}, + {TARS_CHECK_PRESSURE_STABILIZE, "TARS_CHECK_PRESSURE_STABILIZE"}, + {TARS_PRESSURE_STABILIZED, "TARS_PRESSURE_STABILIZED"}, + {TARS_FILLING_DONE, "TARS_FILLING_DONE"}, + {TARS_REFINING_DONE, "TARS_REFINING_DONE"}, + {ALTITUDE_TRIGGER_ALTITUDE_REACHED, + "ALTITUDE_TRIGGER_ALTITUDE_REACHED"}, + {LAST_EVENT, "LAST_EVENT"}, + }; + + auto it = event_string_map.find(event); + return it == event_string_map.end() ? "EV_UNKNOWN" : it->second; +} + +struct LiftoffEvent +{ + uint64_t timestamp; + + static std::string header() { return "timestamp\n"; } + + void print(std::ostream& os) const { os << timestamp << "\n"; } +}; + +struct ApogeeEvent +{ + uint64_t timestamp; + + static std::string header() { return "timestamp\n"; } + + void print(std::ostream& os) const { os << timestamp << "\n"; } +}; + +struct NoseconeEvent +{ + uint64_t timestamp; + + static std::string header() { return "timestamp\n"; } + + void print(std::ostream& os) const { os << timestamp << "\n"; } +}; + +struct ExpulsionEvent +{ + uint64_t timestamp; + + static std::string header() { return "timestamp\n"; } + + void print(std::ostream& os) const { os << timestamp << "\n"; } +}; + +struct MainEvent +{ + uint64_t timestamp; + + static std::string header() { return "timestamp\n"; } + + void print(std::ostream& os) const { os << timestamp << "\n"; } +}; + +struct LandingEvent +{ + uint64_t timestamp; + + static std::string header() { return "timestamp\n"; } + + void print(std::ostream& os) const { os << timestamp << "\n"; } +}; + +} // namespace Common diff --git a/src/shared/hil/HIL.h b/src/shared/hil/HIL.h new file mode 100644 index 000000000..d5e74a07a --- /dev/null +++ b/src/shared/hil/HIL.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2021-2023 Skyward Experimental Rocketry + * Authors: Luca Conterio, 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 <Singleton.h> + +#include <utils/ModuleManager/ModuleManager.hpp> + +#include "HILConfig.h" +#include "HILFlightPhasesManager.h" +#include "HILTransceiver.h" + +/** + * @brief Single interface to the hardware-in-the-loop framework. + */ +class HIL : public Boardcore::Module +{ +public: + HIL(Boardcore::USART &hilSerial, + HILFlightPhasesManager *flightPhasesManager) + : flightPhasesManager(flightPhasesManager) + { + simulator = new HILTransceiver(hilSerial); + } + + HILTransceiver *simulator; + HILFlightPhasesManager *flightPhasesManager; + + /** + * @brief Start the needed hardware-in-the-loop components. + */ + [[nodiscard]] bool start() + { + return simulator->start() && flightPhasesManager->start(); + } + + void stop() { simulator->stop(); } + + void send(HILConfig::ActuatorData actuatorData) + { + simulator->setActuatorData(actuatorData); + } + + /** + * @brief Returns if all the schedulers are up and running + */ + bool isStarted(); +}; diff --git a/src/shared/hil/HILConfig.h b/src/shared/hil/HILConfig.h new file mode 100644 index 000000000..bf731068f --- /dev/null +++ b/src/shared/hil/HILConfig.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2020-2023 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 + +/** + * @brief Configuration file that includes only the right structures described + * in the config file of the test. + * + * Usage: + * #elif <Flag> + * #include "<test-directory>/HILSimulationConfig.h" + * + * REMEMBER: + * when defining the entry in "CMakeLists" you should add + * target_compile_definitions(<test-directory> PRIVATE <Flag>) + * + * WARNING: + * You should always CLEAN your board before flashing a new entrypoint. Some + * flags could still be in memory + */ + +/* Hardware in the loop entrypoint */ +#if defined(HILTest) +#include "../tests/hil/HILSimulationConfig.h" +/* +#elif defined(HIL_<tuoFlag>) +#include "<test-directory>/HILSimulationConfig.h" +*/ +#else +#error You have add the flag of your configuration file for the HIL testing! +#endif diff --git a/src/shared/hil/HILFlightPhasesManager.cpp b/src/shared/hil/HILFlightPhasesManager.cpp new file mode 100644 index 000000000..fa833297c --- /dev/null +++ b/src/shared/hil/HILFlightPhasesManager.cpp @@ -0,0 +1,296 @@ +/* Copyright (c) 2021-2023 Skyward Experimental Rocketry + * Authors: Luca Conterio, 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 "HILFlightPhasesManager.h" + +#include <events/Event.h> + +#include "events/EventBroker.h" + +using namespace Common; +using namespace HILConfig; + +HILFlightPhasesManager::HILFlightPhasesManager() + : Boardcore::EventHandler(), + flagsFlightPhases({{FlightPhases::SIM_FLYING, false}, + {FlightPhases::SIM_ASCENT, false}, + {FlightPhases::SIM_BURNING, false}, + {FlightPhases::SIM_AEROBRAKES, false}, + {FlightPhases::SIM_PARA1, false}, + {FlightPhases::SIM_PARA2, false}, + {FlightPhases::SIMULATION_STARTED, false}, + {FlightPhases::CALIBRATION, false}, + {FlightPhases::CALIBRATION_OK, false}, + {FlightPhases::ARMED, false}, + {FlightPhases::LIFTOFF_PIN_DETACHED, false}, + {FlightPhases::LIFTOFF, false}, + {FlightPhases::AEROBRAKES, false}, + {FlightPhases::APOGEE, false}, + {FlightPhases::PARA1, false}, + {FlightPhases::PARA2, false}, + {FlightPhases::SIMULATION_STOPPED, false}}) +{ + prev_flagsFlightPhases = flagsFlightPhases; + + auto& eventBroker = Boardcore::EventBroker::getInstance(); + eventBroker.subscribe(this, TOPIC_ABK); + eventBroker.subscribe(this, TOPIC_ADA); + eventBroker.subscribe(this, TOPIC_MEA); + eventBroker.subscribe(this, TOPIC_DPL); + eventBroker.subscribe(this, TOPIC_CAN); + eventBroker.subscribe(this, TOPIC_FLIGHT); + eventBroker.subscribe(this, TOPIC_FMM); + eventBroker.subscribe(this, TOPIC_FSR); + eventBroker.subscribe(this, TOPIC_NAS); + eventBroker.subscribe(this, TOPIC_TMTC); + eventBroker.subscribe(this, TOPIC_MOTOR); + eventBroker.subscribe(this, TOPIC_TARS); + eventBroker.subscribe(this, TOPIC_ALT); +} + +void HILFlightPhasesManager::setCurrentPositionSource( + std::function<Boardcore::TimedTrajectoryPoint()> getCurrentPosition) +{ + this->getCurrentPosition = getCurrentPosition; +} + +bool HILFlightPhasesManager::isFlagActive(FlightPhases flag) +{ + return flagsFlightPhases[flag]; +} + +void HILFlightPhasesManager::registerToFlightPhase(FlightPhases flag, + TCallback func) +{ + callbacks[flag].push_back(func); +} + +void HILFlightPhasesManager::setFlagFlightPhase(FlightPhases flag, + bool isEnable) +{ + flagsFlightPhases[flag] = isEnable; +} + +void HILFlightPhasesManager::processFlags(FlightPhasesFlags hil_flags) +{ + updateSimulatorFlags(hil_flags); + + std::vector<FlightPhases> changed_flags; + + // set true when the first packet from the simulator arrives + if (isSetTrue(FlightPhases::SIMULATION_STARTED)) + { + t_start = Boardcore::TimestampTimer::getTimestamp(); + + TRACE("[HIL] ------- SIMULATION STARTED ! ------- \n"); + changed_flags.push_back(FlightPhases::SIMULATION_STARTED); + } + + if (flagsFlightPhases[FlightPhases::SIM_FLYING]) + { + if (isSetTrue(FlightPhases::SIM_FLYING)) + { + registerOutcomes(FlightPhases::SIM_FLYING); + TRACE("[HIL] ------- SIMULATOR LIFTOFF ! ------- \n"); + changed_flags.push_back(FlightPhases::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 HILFlightPhasesManager::registerOutcomes(FlightPhases phase) +{ + Boardcore::TimedTrajectoryPoint temp = getCurrentPosition(); + outcomes[phase] = Outcomes(temp.z, temp.vz); +} + +void HILFlightPhasesManager::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[FlightPhases::SIM_BURNING].print(t_liftoff); + + printf("Airbrakes exit shadowmode: \n"); + outcomes[FlightPhases::AEROBRAKES].print(t_liftoff); + + printf("Apogee: \n"); + outcomes[FlightPhases::APOGEE].print(t_liftoff); + + printf("Parachute 1: \n"); + outcomes[FlightPhases::PARA1].print(t_liftoff); + + printf("Parachute 2: \n"); + outcomes[FlightPhases::PARA2].print(t_liftoff); + + printf("Simulation Stopped: \n"); + outcomes[FlightPhases::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); +} + +/** + * @brief Updates the flags of the object with the flags sent from matlab + * and checks for the apogee + */ +void HILFlightPhasesManager::updateSimulatorFlags(FlightPhasesFlags hil_flags) +{ + flagsFlightPhases[FlightPhases::SIM_ASCENT] = hil_flags.flag_ascent; + flagsFlightPhases[FlightPhases::SIM_FLYING] = hil_flags.flag_flight; + flagsFlightPhases[FlightPhases::SIM_BURNING] = hil_flags.flag_burning; + flagsFlightPhases[FlightPhases::SIM_AEROBRAKES] = hil_flags.flag_airbrakes; + flagsFlightPhases[FlightPhases::SIM_PARA1] = hil_flags.flag_para1; + flagsFlightPhases[FlightPhases::SIM_PARA2] = hil_flags.flag_para2; + + flagsFlightPhases[FlightPhases::SIMULATION_STOPPED] = + isSetFalse(FlightPhases::SIM_FLYING) || + prev_flagsFlightPhases[FlightPhases::SIMULATION_STOPPED]; +} + +void HILFlightPhasesManager::handleEvent(const Boardcore::Event& e) +{ + std::vector<FlightPhases> changed_flags; + + switch (e) + { + case FMM_INIT_ERROR: + printf("[HIL] ------- INIT FAILED ! ------- \n"); + case FMM_INIT_OK: + setFlagFlightPhase(FlightPhases::CALIBRATION, true); + TRACE("[HIL] ------- CALIBRATION ! ------- \n"); + changed_flags.push_back(FlightPhases::CALIBRATION); + break; + case FLIGHT_DISARMED: + setFlagFlightPhase(FlightPhases::CALIBRATION_OK, true); + TRACE("[HIL] CALIBRATION OK!\n"); + changed_flags.push_back(FlightPhases::CALIBRATION_OK); + break; + case FLIGHT_ARMED: + setFlagFlightPhase(FlightPhases::ARMED, true); + printf("[HIL] ------- READY TO LAUNCH ! ------- \n"); + changed_flags.push_back(FlightPhases::ARMED); + break; + case FLIGHT_LAUNCH_PIN_DETACHED: + setFlagFlightPhase(FlightPhases::LIFTOFF_PIN_DETACHED, true); + TRACE("[HIL] ------- LIFTOFF PIN DETACHED ! ------- \n"); + changed_flags.push_back(FlightPhases::LIFTOFF_PIN_DETACHED); + break; + case FLIGHT_LIFTOFF: + case TMTC_FORCE_LAUNCH: + t_liftoff = Boardcore::TimestampTimer::getTimestamp(); + printf("[HIL] ------- LIFTOFF -------: %f, %f \n", + getCurrentPosition().z, getCurrentPosition().vz); + changed_flags.push_back(FlightPhases::LIFTOFF); + break; + case ABK_SHADOW_MODE_TIMEOUT: + setFlagFlightPhase(FlightPhases::AEROBRAKES, true); + registerOutcomes(FlightPhases::AEROBRAKES); + TRACE("[HIL] ABK shadow mode timeout\n"); + changed_flags.push_back(FlightPhases::AEROBRAKES); + break; + case ADA_SHADOW_MODE_TIMEOUT: + TRACE("[HIL] ADA shadow mode timeout\n"); + break; + case ABK_DISABLE: + setFlagFlightPhase(FlightPhases::AEROBRAKES, false); + TRACE("[HIL] ABK disabled\n"); + break; + case FLIGHT_APOGEE_DETECTED: + case CAN_APOGEE_DETECTED: + setFlagFlightPhase(FlightPhases::AEROBRAKES, false); + registerOutcomes(FlightPhases::APOGEE); + printf("[HIL] ------- APOGEE DETECTED ! ------- %f, %f \n", + getCurrentPosition().z, getCurrentPosition().vz); + changed_flags.push_back(FlightPhases::APOGEE); + break; + case FLIGHT_DROGUE_DESCENT: + case TMTC_FORCE_EXPULSION: + setFlagFlightPhase(FlightPhases::PARA1, true); + registerOutcomes(FlightPhases::PARA1); + printf("[HIL] ------- PARA1 ! -------%f, %f \n", + getCurrentPosition().z, getCurrentPosition().vz); + changed_flags.push_back(FlightPhases::PARA1); + break; + case FLIGHT_WING_DESCENT: + case FLIGHT_DPL_ALT_DETECTED: + case TMTC_FORCE_DEPLOYMENT: + setFlagFlightPhase(FlightPhases::PARA1, false); + setFlagFlightPhase(FlightPhases::PARA2, true); + registerOutcomes(FlightPhases::PARA2); + printf("[HIL] ------- PARA2 ! ------- %f, %f \n", + getCurrentPosition().z, getCurrentPosition().vz); + changed_flags.push_back(FlightPhases::PARA2); + break; + case FLIGHT_LANDING_DETECTED: + case TMTC_FORCE_LANDING: + t_stop = Boardcore::TimestampTimer::getTimestamp(); + setFlagFlightPhase(FlightPhases::PARA2, false); + setFlagFlightPhase(FlightPhases::SIMULATION_STOPPED, true); + changed_flags.push_back(FlightPhases::SIMULATION_STOPPED); + registerOutcomes(FlightPhases::SIMULATION_STOPPED); + TRACE("[HIL] ------- SIMULATION STOPPED ! -------: %f \n\n\n", + (double)t_stop / 1000000.0f); + printOutcomes(); + break; + default: + TRACE("%s invalid event\n", 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; +} + +bool HILFlightPhasesManager::isSetTrue(FlightPhases phase) +{ + return flagsFlightPhases[phase] && !prev_flagsFlightPhases[phase]; +} + +bool HILFlightPhasesManager::isSetFalse(FlightPhases phase) +{ + return !flagsFlightPhases[phase] && prev_flagsFlightPhases[phase]; +} diff --git a/src/shared/hil/HILFlightPhasesManager.h b/src/shared/hil/HILFlightPhasesManager.h new file mode 100644 index 000000000..68390d983 --- /dev/null +++ b/src/shared/hil/HILFlightPhasesManager.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2021-2023 Skyward Experimental Rocketry + * Authors: Luca Conterio, 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 <algorithms/AirBrakes/TrajectoryPoint.h> +#include <drivers/timer/TimestampTimer.h> +#include <events/Event.h> +#include <events/EventHandler.h> +#include <hil/HILTransceiver.h> +#include <miosix.h> + +#include <iostream> +#include <map> + +#include "Events.h" + +typedef std::function<void()> TCallback; +class HILTransceiver; + +enum class FlightPhases +{ + // simulator flags + SIM_FLYING, + SIM_ASCENT, + SIM_BURNING, + SIM_AEROBRAKES, + SIM_PARA1, + SIM_PARA2, + + // flight flags + SIMULATION_STARTED, + CALIBRATION, + CALIBRATION_OK, + ARMED, + LIFTOFF_PIN_DETACHED, + LIFTOFF, + AEROBRAKES, + APOGEE, + PARA1, + PARA2, + SIMULATION_STOPPED +}; + +struct Outcomes +{ + uint64_t t = 0; + float z = 0; + float vz = 0; + + Outcomes() : t(0), z(0), vz(0) {} + Outcomes(float z, float vz) + : t(Boardcore::TimestampTimer::getTimestamp()), z(z), vz(vz) + { + } + + void print(uint64_t t_start) const + { + printf("@time : %f [sec]\n", (double)(t - t_start) / 1000000); + printf("@altitude : %f [m]\n", z); + printf("@velocity : %f [m/s]\n\n", vz); + } +}; + +/** + * @brief Singleton object that manages all the phases of the simulation. + * After his instantiation we need to set the source of the current position in + * order to be able to save the outcomes for each event. + */ +class HILFlightPhasesManager : public Boardcore::EventHandler, + public Boardcore::Module +{ + using FlightPhasesFlags = HILConfig::SimulatorData::Flags; + +public: + HILFlightPhasesManager(); + + void setCurrentPositionSource( + std::function<Boardcore::TimedTrajectoryPoint()> getCurrentPosition); + + bool isFlagActive(FlightPhases flag); + + void registerToFlightPhase(FlightPhases flag, TCallback func); + + void setFlagFlightPhase(FlightPhases flag, bool isEnable); + + virtual void processFlags(FlightPhasesFlags hil_flags); + +protected: + virtual void handleEvent(const Boardcore::Event& e) override; + + virtual void registerOutcomes(FlightPhases phase); + + virtual void printOutcomes(); + + /** + * @brief Updates the flags of the object with the flags sent from matlab + * and checks for the apogee + */ + virtual void updateSimulatorFlags(FlightPhasesFlags hil_flags); + + bool isSetTrue(FlightPhases phase); + + bool isSetFalse(FlightPhases phase); + + uint64_t t_start = 0; + uint64_t t_liftoff = 0; + uint64_t t_stop = 0; + std::map<FlightPhases, bool> flagsFlightPhases; + std::map<FlightPhases, bool> prev_flagsFlightPhases; + std::map<FlightPhases, vector<TCallback>> callbacks; + std::map<FlightPhases, Outcomes> outcomes; + std::function<Boardcore::TimedTrajectoryPoint()> getCurrentPosition; +}; diff --git a/src/shared/hil/HILTransceiver.cpp b/src/shared/hil/HILTransceiver.cpp new file mode 100644 index 000000000..8eb5cc26d --- /dev/null +++ b/src/shared/hil/HILTransceiver.cpp @@ -0,0 +1,157 @@ +/* Copyright (c) 2020-2023 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. + */ + +#include "HIL.h" +#include "drivers/usart/USART.h" + +using namespace HILConfig; + +/** + * @brief Construct a serial connection attached to a control algorithm + */ +HILTransceiver::HILTransceiver(Boardcore::USART &hilSerial) + : hilSerial(hilSerial), actuatorData{} +{ +} + +/** + * @brief sets the actuator data and then wakes up the MatlabTransceiver + * thread in order to send the data back to the simulator (called by the + * control algorithm) + * @param actuatorData sets the data that will be sent to the simulator + */ +void HILTransceiver::setActuatorData(ActuatorData actuatorData) +{ + this->actuatorData = actuatorData; + updated = true; + condVar.signal(); +} + +/** + * @brief returns the reference of the SimulatorData + * + * @return reference to the data simulated by matlab + */ +SimulatorData *HILTransceiver::getSensorData() { return &sensorData; } + +/** + * @brief adds to the resetSampleCounter list an object that has to be + * notified when a new packet of data is arrived from the simulator + * + * @param t SimTimestampManagement object + */ +void HILTransceiver::addResetSampleCounter(HILTimestampManagement *t) +{ + sensorsTimestamp.push_back(t); +} + +/** + * @brief The thread deals with the communication between the simulator and the + * board. + * + * TODO: Check: + * The first read is done in the init() function + * + * After the first time the data is received, the loop of this thread: + * - Reads the simulated data and copies them in the SensorData structure; + * - Notifies every sensor that new data arrived; + * - Waits for the control algorithms to update the actuator data; + * - Sends back the value to the simulator. + */ +void HILTransceiver::run() +{ + TRACE("[HILT] Transceiver started\n"); + bool lostUpdate = false; + hilSerial.clearQueue(); + + while (true) + { + // Pausing the kernel in order to copy the data in the shared structure + { + SimulatorData tempData; + miosix::led3On(); + if (!hilSerial.readBlocking(&tempData, sizeof(SimulatorData))) + { + TRACE("Failed Serial read\n"); + } + hilSerial.clearQueue(); + miosix::led3Off(); + + miosix::PauseKernelLock kLock; + sensorData = tempData; + + if (updated) + { + lostUpdate = true; + updated = false; // We want the last computation + } + } + + // If this is the first packet to be received, then update the flight + // phase manager + if (!receivedFirstPacket) + { + receivedFirstPacket = true; + Boardcore::ModuleManager::getInstance() + .get<HIL>() + ->flightPhasesManager->setFlagFlightPhase( + FlightPhases::SIMULATION_STARTED, true); + } + + // Notify all sensors that a new set of data is arrived + //[REVIEW] Could be moved in HILFlightPhasesManager + for (auto st : sensorsTimestamp) + st->resetSampleCounter(); + + // Trigger events relative to the flight phases + Boardcore::ModuleManager::getInstance() + .get<HIL>() + ->flightPhasesManager->processFlags(sensorData.flags); + + if (lostUpdate) + { + // This means also that the number of samples used for the mean sent + // to the HIL simulator is made up of more than the number of + // samples we though + TRACE("[HILT] lost updates!\n"); + lostUpdate = false; + } + + waitActuatorData(); + miosix::led2On(); + hilSerial.write(&actuatorData, sizeof(ActuatorData)); + miosix::led2Off(); + } +} + +/** + * @brief Waits for the control algorithms to update actuatorData. + */ +void HILTransceiver::waitActuatorData() +{ + miosix::Lock<miosix::FastMutex> l(mutex); + while (!updated) + { + condVar.wait(l); + } + updated = false; +} diff --git a/src/shared/hil/HILTransceiver.h b/src/shared/hil/HILTransceiver.h new file mode 100644 index 000000000..4306f9c8f --- /dev/null +++ b/src/shared/hil/HILTransceiver.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2020-2023 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 <ActiveObject.h> +#include <drivers/timer/TimestampTimer.h> +#include <drivers/usart/USART.h> +#include <hil/HILConfig.h> +#include <sensors/HILSensors/HILTimestampManagement.h> +#include <utils/Debug.h> + +/** + * @brief HILTransceiver is a Singleton and provides an easy interface for + * the control algorithms to send and receive data during a simulation + */ +class HILTransceiver : public Boardcore::ActiveObject +{ +public: + /** + * @brief Construct a serial connection attached to a control algorithm + */ + explicit HILTransceiver(Boardcore::USART &hilSerial); + + /** + * @brief sets the actuator data and then wakes up the MatlabTransceiver + * thread in order to send the data back to the simulator (called by the + * control algorithm) + * @param actuatorData sets the data that will be sent to the simulator + */ + void setActuatorData(HILConfig::ActuatorData actuatorData); + + /** + * @brief returns the reference of the SimulatorData + * + * @return reference to the data simulated by matlab + */ + HILConfig::SimulatorData *getSensorData(); + + /** + * @brief adds to the resetSampleCounter list an object that has to be + * notified when a new packet of data is arrived from the simulator + * + * @param t SimTimestampManagement object + */ + void addResetSampleCounter(HILTimestampManagement *t); + +private: + /** + * @brief Waits for the control algorithm(s) to update actuatorData. + */ + void waitActuatorData(); + + void run() override; + + Boardcore::USART &hilSerial; + bool receivedFirstPacket = false; + bool updated = false; + HILConfig::SimulatorData sensorData; + HILConfig::ActuatorData actuatorData; + std::vector<HILTimestampManagement *> sensorsTimestamp; + miosix::FastMutex mutex; + miosix::ConditionVariable condVar; +}; diff --git a/src/shared/hil/README.md b/src/shared/hil/README.md new file mode 100644 index 000000000..daf02c427 --- /dev/null +++ b/src/shared/hil/README.md @@ -0,0 +1,122 @@ +# Hardware in the loop framework + +This README refers to the OBSW (On-Board Software) and matlab framework used for the **Hardware In the Loop** (HIL) testing. + +## HIL framework - OBSW side + +### Dependencies: +- skyward-boardcore branch: sensors-dev +- miosix branch: gcc-9.2.0 + +### Files: +- `test-SerialInterface.cpp`: Used to make quick tests of the serial communication with the simulator. It's a pretty simple example of usage of the *SerialInterface* module, used to test if the the serial connection is working properly. +- `test-hil.cpp`: Used to test the control algorithms. + - enable timestamp timer for the HILsensors + - define a *HILTransceiver* object + - define all the sensors and the actuators used by the simulation + - define the control algorithm + - add the algorithms to the *addNotifyToBegin* queue of the *HILTransceiver* object + - initialize all the sensors and actuators + - start the HILTransceiver thread + - sample the sensors and update the algorithm at the right rate +- `HILSimulationConfig.h`: Configuration file for the entrypoint; for every type of simulation you should create a directory with your entrypoint and one of this configuration file with these main informations: + - `SIM_BAUDRATE`: Baudrate of the connection for the simulation + - `SIM_PERIOD`: Time of each simulated step [in milliseconds] + - `SENSOR_FREQ`: Frequency of every sensor you will use in the simulation [in Hz = samples/second] + - `SensorData`: Data structure sent by the simulator + - `ActuatorData`: Data structure that the simulator expects back +- `HILConfig.h`: In this file you have to set the right config file to include when a component need the structures or other configuration parameters. You have to choose a *Flag* that represents the config file. +e.g. +``` +#if defined(HIL_SERIALINTERFACE) +#include "test-SerialInterface/HILSimulationConfig.h" +``` +- `SerialInterface.h`: Opens a serial port on the OBSW. After his initialization it's ready to send and receive the data. The default baudrate value is 256000 by default. The default port number value is 2 (for the stm32f407vg_stm32f4discovery is tx_board=PA2 rx_board=PA3) +- `HILTransceiver.h`: Is an ActiveObject and provides an easier interface with the simulator automatically creating a SerialTestSensor object (you can pass the baudrate or the USART port to the constructor) and offers these methods: + - `getSensorData` returns a pointer to the last data simulated. The struct returned (*SensorData*) reflects in number of elements, type and positions the struct sent from the simulator (the struct sent from matlab and SensorData should always correspond!). + - `setActuatorData` sends the data to the simulator + - `addNotifyToBegin` takes an *Algorithm* object as input and starts it when the first packet from the simulator is received + - `addResetSampleCounter` takes a *HILSensor* as input (or every object that implements *HILTimestampManagement*) and notifies to it that new data has arrived from the simulator +- `MockAirbrakeAlgorithm.h`: Example of control algorithm. Receives in the constructor a *HILSensor* (or a kalman that implements *HILSensor* as in this case) and a *ServoInterface*. The algorithm have to *getLastSample* from the sensor passed, elaborates it and then sends the output to the actuator calling his *set* method +- `HILSensor`: Interface implemented by all the sensors and kalmans. To the constructor (templated with the type of the struct used by the sensor to store the last sample) we have to pass the reference to the *HILTransceiver* object, the *simulation period* and the *number of simulated samples* by matlab of this sensor (corresponding to the number of rows of the corresponding field of the sensorData structure in the matlab simulatos). They have to be initialized before use. Everytime the `sample` method is invoked, the sensor takes the last unread sample from the data simulated and creates a timestamp for that sample. If we sample more data then the available one we [continue receiving the last sample/throw an exception/print an error message on the stdin]. We have to register the sensor to the `addResetSampleCounter` queue of *HILTransceiver* in order to be notified when fresh new data is arrived from the simulator, so that the sensor can start reading the data from the beginning of the array. The method `getLastSample` returns the *HILSensorData* structure with the last sample. +- `HILServo`: Is a *ServoInterface* implementation. Interfaces the control algorithm to the HILTransceiver object. Invoking the method `set`, the value passed is converted (if needed) and then sent to the simulator. + +### Usage of the hardware-in-the-loop framework: + +To use this module as-is: +- The first thing to do is to develop the control algorithm in the `step` method of *MockAirbrakeAlgorithm*. Then you should check that the *HILServo* sends back to the simulator the data in the conversion you want. +- Now you should create the entrypoint in the `sbs.conf` file. In the *Defines:* field you should add the flag of your entrypoint corresponding to the one used in `HILConfig.h` in order to choose the right config file (e.g. `Defines: -DHIL`) +- After this you should be able to build the `test-hil.cpp` and flash the executable on the board. +- Then you should connect via serial the board to the computer (with a serial adapter). The connections between adapter and board are: + - adapter_GND to board_GND + - adapter_RX to board_TX (check table or cheatsheet for available serialPorts and relative pins) + - adapter_TX to board_RX (check table or cheatsheet for available serialPorts and relative pins) + +<center> + +| **board** | **serialPort** | **board_TX** | **board_RX** | +| ---------------------------- | -------------- | ------------ | ------------ | +| stm32f429zi_stm32f4discovery | (USART)3 | PB10 | PB11 | +| stm32f407vg_stm32f4discovery | (USART)2 | PA2 | PA3 | + +</center> + +Now the board is ready for the simulation. + +### Example of usage of the hardware-in-the-loop framework on the OBSW: + +A basic example is available in the *src/tests/test-hil* directory. + +## HIL framework - Matlab side + +### Files: +- `serialib.h` and `serialib.cpp`: used to build the C++ code for matlab +- `serialbridge.cpp`: the file that implements the serialbridge feature in C++ (not necessary if you don't have to change the framework) +- `build.m`: used for building the serialbridge.cpp file +- `serialbridge.mexw64`: the compiled mex file that provides the serialbridge + function for matlab (this is the only file needed for the simulation) +- `structToSingles.m`: function that converts a generic struct (with also nested structs) to an array of singles, ready to be sent on the serial communication + +### Usage of the hardware-in-the-loop framework: + +First of all, in order to execute the following commands and receive the data back the board should already be flashed and connected properly as above. Then the following functions can be used to program the hardware-in-the-loop simulator: +- `serialbridge("Open", string_serialPort, uint_baudrate)` opens the serial communication with the board; usually the serial communication is opened at the beginning of `start_simulation.m`: + - **"Open"**: specifies we want to open the serial port + - *string_serialPort*: is the serial port we want to open (eg: "COM6"); In order to find the right port you can use on Windows the command `mode` and see the name of the serial port with baudrate *19200* or *256000*. + - *uint_baudrate*: is the baudrate of the port (eg: 256000) +- `serialbridge("Write", singleArray_Data)` sends the data to the board; *you should send all the data in one single write command*: + - **"Write"**: specifies that we want to write on the serial port to the board + - *singleArray_Data*: is the array of singles we want to write on serial (eg: [1 2 3 4.5 5.4]). If we want to send a struct (or even nested structs) we can use the function `structToSingles` to turn the struct into an array +- `singleArray_Data = serialbridge("Read", uint_nData)` to receive data from the board; matlab will wait on the read till all the data is received: + - **"Read"**: specifies that we want to read from the serial port + - *uint_nData*: How many floats to read from serial (eg: 1) + - *singleArray_Data*: array of floats read from the serial (eg: alpha_degree) +- `serialbridge("Close")` closes the serial communication: + - **"Close"**: specifies we want to close the serial port + +When the simulator is ready and the board is flashed and connected you can execute the simulation as a normal project in matlab. + +### Example of usage of the hardware-in-the-loop framework in matlab: +``` +serialbridge("Open", "COM6", 256000); % Opens the serial port +serialbridge("Write", [1 2 3 4]); % Sends the array "[1 2 3 4]" to the serial device +data = serialbridge("Read", 2); % Receives 2 floats and stores them in the variable "data" +serialbridge("Close"); % Closes the serial port +``` + +### WARNING: +- It's possible to open just ONE serial port with serialbridge +- The files serialib.h and serialib.cpp were modified in order to fix compiling errors on windows +- the function `structToSingles` ignores the struct fields called *time* because the obsw forges it's own timestamps for every sample + +## FAQ +Some common problems found using the framework: +- For every problem you encounter, first time reboot your board. If it doesn't work clear and flash the board again. also you can try to disconnect and reconnect the serial adapter. +- If matlab gives back an error about something wrong with array indices check the data received from the OBSW, if they exceed the limit of that value (for example giving to the airbrake aperture a value of 2000 degrees) matlab returns this error +- If you have problems on linux (on the matlab side) check if you are opening the right port and at the right baudrate; check also if the baudrate you are using is supported by your operative system +- If you are reading malformed data check the baudrate on OBSW and matlab side. they have to be the same +- If you are not able to read data and matlab stays in the *busy* state (to restart matlab usage you must reboot matlab, *ctrl+c* or *ctrl+z* doesn't work cause you are waiting in the mex file and matlab can't communicate to it... thanks matlab): + - check if you connected the right pins + - check if the USART you are using is free from any other communication (use the cheatsheet, I'm sorry) + - try to use another USART (if you are lucky and it works please update the table with all the boards, USART and pins verified to work properly) + - check if it's a problem on the obsw (use a simpler obsw test in order to check communication) diff --git a/src/shared/sensors/HILSensors/HILAccelerometer.h b/src/shared/sensors/HILSensors/HILAccelerometer.h new file mode 100644 index 000000000..854234965 --- /dev/null +++ b/src/shared/sensors/HILSensors/HILAccelerometer.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2020-2023 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 <logger/Logger.h> + +#include "HILSensor.h" + +/** + * @brief fake accelerometer sensor used for the simulation. + * + * This class is used to simulate as near as possible the situation of the + * OBSW during the flight, using fake sensors classes instead of the real + * ones, taking their data from the data received from a simulator. + */ +class HILAccelerometer : public HILSensor<HILAccelerometerData> +{ +public: + HILAccelerometer(int n_data_sensor, void *sensorData) + : HILSensor(n_data_sensor, sensorData) + { + } + +protected: + HILAccelerometerData updateData() override + { + HILAccelerometerData tempData; + + miosix::PauseKernelLock pkLock; + HILConfig::SimulatorData::Accelerometer *accelerometer = + reinterpret_cast<HILConfig::SimulatorData::Accelerometer *>( + sensorData); + + tempData.accelerationX = accelerometer->measures[sampleCounter][0]; + tempData.accelerationY = accelerometer->measures[sampleCounter][1]; + tempData.accelerationZ = accelerometer->measures[sampleCounter][2]; + tempData.accelerationTimestamp = updateTimestamp(); + + Boardcore::Logger::getInstance().log(tempData); + + return tempData; + } +}; diff --git a/src/shared/sensors/HILSensors/HILBarometer.h b/src/shared/sensors/HILSensors/HILBarometer.h new file mode 100644 index 000000000..9e283f925 --- /dev/null +++ b/src/shared/sensors/HILSensors/HILBarometer.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2020-2023 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 <logger/Logger.h> + +#include "HILSensor.h" + +/** + * @brief fake barometer sensor used for the simulation. + * + * This class is used to simulate as near as possible the situation of the + * OBSW during the flight, using fake sensors classes instead of the real + * ones, taking their data from the data received from a simulator. + */ +class HILBarometer : public HILSensor<HILBarometerData> +{ +public: + HILBarometer(int n_data_sensor, void *sensorData) + : HILSensor(n_data_sensor, sensorData) + { + } + +protected: + HILBarometerData updateData() override + { + HILBarometerData tempData; + + miosix::PauseKernelLock pkLock; + HILConfig::SimulatorData::Barometer *barometer = + reinterpret_cast<HILConfig::SimulatorData::Barometer *>(sensorData); + + tempData.pressure = barometer->measures[sampleCounter]; + tempData.pressureTimestamp = updateTimestamp(); + + Boardcore::Logger::getInstance().log(tempData); + + return tempData; + } +}; diff --git a/src/shared/sensors/HILSensors/HILGps.h b/src/shared/sensors/HILSensors/HILGps.h new file mode 100644 index 000000000..aecd70ba9 --- /dev/null +++ b/src/shared/sensors/HILSensors/HILGps.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2020-2023 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 <logger/Logger.h> + +#include <cmath> + +#include "HILSensor.h" + +/** + * @brief fake gps sensor used for the HILulation. + * + * This class is used to HILulate as near as possible the situation of the + * OBSW during the flight, using fake sensors classes instead of the real + * ones, taking their data from the data received from a HILulator. + */ +class HILGps : public HILSensor<HILGpsData> +{ +public: + HILGps(int n_data_sensor, void *sensorData) + : HILSensor(n_data_sensor, sensorData) + { + } + +protected: + HILGpsData updateData() override + { + HILGpsData tempData; + + miosix::PauseKernelLock pkLock; + HILConfig::SimulatorData::Gps *gps = + reinterpret_cast<HILConfig::SimulatorData::Gps *>(sensorData); + + tempData.latitude = + gps->positionMeasures[sampleCounter][0]; // divide by earth radius + tempData.longitude = gps->positionMeasures[sampleCounter][1]; + tempData.height = gps->positionMeasures[sampleCounter][2]; + + tempData.velocityNorth = gps->velocityMeasures[sampleCounter][0]; + tempData.velocityEast = gps->velocityMeasures[sampleCounter][1]; + tempData.velocityDown = gps->velocityMeasures[sampleCounter][2]; + tempData.speed = sqrtf(tempData.velocityNorth * tempData.velocityNorth + + tempData.velocityDown * tempData.velocityDown); + tempData.positionDOP = 0; + + tempData.fix = static_cast<uint8_t>(gps->fix); + tempData.satellites = static_cast<uint8_t>(gps->num_satellites); + + tempData.gpsTimestamp = updateTimestamp(); + + Boardcore::Logger::getInstance().log(tempData); + + return tempData; + } +}; diff --git a/src/shared/sensors/HILSensors/HILGyroscope.h b/src/shared/sensors/HILSensors/HILGyroscope.h new file mode 100644 index 000000000..78b10e907 --- /dev/null +++ b/src/shared/sensors/HILSensors/HILGyroscope.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2020-2023 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 <logger/Logger.h> + +#include "HILSensor.h" + +/** + * @brief fake gyroscope sensor used for the simulation. + * + * This class is used to simulate as near as possible the situation of the + * OBSW during the flight, using fake sensors classes instead of the real + * ones, taking their data from the data received from a simulator. + */ +class HILGyroscope : public HILSensor<HILGyroscopeData> +{ +public: + HILGyroscope(int n_data_sensor, void *sensorData) + : HILSensor(n_data_sensor, sensorData) + { + } + +protected: + HILGyroscopeData updateData() override + { + HILGyroscopeData tempData; + + miosix::PauseKernelLock pkLock; + HILConfig::SimulatorData::Gyro *gyroscope = + reinterpret_cast<HILConfig::SimulatorData::Gyro *>(sensorData); + + tempData.angularSpeedX = gyroscope->measures[sampleCounter][0]; + tempData.angularSpeedY = gyroscope->measures[sampleCounter][1]; + tempData.angularSpeedZ = gyroscope->measures[sampleCounter][2]; + tempData.angularSpeedTimestamp = updateTimestamp(); + + Boardcore::Logger::getInstance().log(tempData); + + return tempData; + } +}; diff --git a/src/shared/sensors/HILSensors/HILMagnetometer.h b/src/shared/sensors/HILSensors/HILMagnetometer.h new file mode 100644 index 000000000..8c79ce53a --- /dev/null +++ b/src/shared/sensors/HILSensors/HILMagnetometer.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2020-2023 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 <logger/Logger.h> + +#include "HILSensor.h" + +/** + * @brief fake magnetometer sensor used for the simulation. + * + * This class is used to simulate as near as possible the situation of the + * OBSW during the flight, using fake sensors classes instead of the real + * ones, taking their data from the data received from a simulator. + */ +class HILMagnetometer : public HILSensor<HILMagnetometerData> +{ +public: + HILMagnetometer(int n_data_sensor, void *sensorData) + : HILSensor(n_data_sensor, sensorData) + { + } + +protected: + HILMagnetometerData updateData() override + { + HILMagnetometerData tempData; + + miosix::PauseKernelLock pkLock; + HILConfig::SimulatorData::Magnetometer *magnetometer = + reinterpret_cast<HILConfig::SimulatorData::Magnetometer *>( + sensorData); + + tempData.magneticFieldX = magnetometer->measures[sampleCounter][0]; + tempData.magneticFieldY = magnetometer->measures[sampleCounter][1]; + tempData.magneticFieldZ = magnetometer->measures[sampleCounter][2]; + tempData.magneticFieldTimestamp = updateTimestamp(); + + Boardcore::Logger::getInstance().log(tempData); + + return tempData; + } +}; diff --git a/src/shared/sensors/HILSensors/HILPitot.h b/src/shared/sensors/HILSensors/HILPitot.h new file mode 100644 index 000000000..13c0b654c --- /dev/null +++ b/src/shared/sensors/HILSensors/HILPitot.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2022-2023 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 <logger/Logger.h> +#include <sensors/analog/Pitot/Pitot.h> +#include <utils/AeroUtils/AeroUtils.h> + +#include "HILSensor.h" + +/** + * @brief fake pitot (differential pressure) sensor used for the simulation. + * + * This class is used to simulate as near as possible the situation of the + * OBSW during the flight, using fake sensors classes instead of the real + * ones, taking their data from the data received from a simulator. + */ +class HILPitot : public HILSensor<HILPitotData> +{ +public: + HILPitot(int n_data_sensor, void *sensorData) + : HILSensor(n_data_sensor, sensorData) + { + } + +protected: + HILPitotData updateData() override + { + miosix::PauseKernelLock pkLock; + + auto *pitotData = + reinterpret_cast<HILConfig::SimulatorData::Pitot *>(sensorData); + + HILPitotData tempData; + tempData.deltaP = pitotData->deltaP[sampleCounter]; + tempData.airspeed = pitotData->airspeed[sampleCounter]; + tempData.timestamp = updateTimestamp(); + + Boardcore::Logger::getInstance().log(tempData); + + return tempData; + } +}; diff --git a/src/shared/sensors/HILSensors/HILSensor.h b/src/shared/sensors/HILSensors/HILSensor.h new file mode 100644 index 000000000..d9f185784 --- /dev/null +++ b/src/shared/sensors/HILSensors/HILSensor.h @@ -0,0 +1,160 @@ +/* Copyright (c) 2020-2023 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 <typeinfo> + +#include "HIL.h" +#include "HILConfig.h" +#include "HILSensorsData.h" +#include "HILTimestampManagement.h" +#include "drivers/timer/TimestampTimer.h" +#include "sensors/Sensor.h" +#include "sensors/SensorData.h" + +/** + * @brief Fake sensor base used for the simulation. Every sensor for the + * simulation should extend this class. + * + * This class is used to simulate as near as possible the situation of the + * OBSW during the flight, using fake sensors classes instead of the real + * ones, taking their data from the data received from a simulator. + */ +template <typename HILSensorData> +class HILSensor : public virtual HILTimestampManagement, + public virtual Boardcore::Sensor<HILSensorData> +{ +public: + /** + * @brief constructor of the fake sensor used for the simulation. + * + * @param matlab reference of the MatlabTransceiver object that deals with + * the simulator + * @param n_data_sensor number of samples in every period of simulation + */ + HILSensor(int n_data_sensor, void *sensorData) + { + this->sensorData = sensorData; + this->n_data_sensor = n_data_sensor; + + /* Registers the sensor on the MatlabTransceiver to be notified when a + * new packet of simulated data arrives */ + Boardcore::ModuleManager::getInstance() + .get<HIL>() + ->simulator->addResetSampleCounter(this); + } + + /** + * @brief sets the sample counter to 0. + * + * Updates the reference timestamp, resets the sampleCounter and clears the + * lastError variable. Called by the HILTransceiver when receives a new + * simulated period. + */ + void resetSampleCounter() override + { + this->lastError = Boardcore::SensorErrors::NO_ERRORS; + sampleCounter = 0; + } + + /** + * @brief Initializes the fake sensor. + */ + bool init() override + { + if (initialized) + { + this->lastError = Boardcore::SensorErrors::ALREADY_INIT; + TRACE("ALREADY INITIALIZED!"); + } + else + { + initialized = true; + } + + return initialized; + } + + bool selfTest() override { return true; } + +protected: + /** + * @brief Updates the internal structure of the fake sensor from the + * structure received from the simulator. + * + * Takes the next unread sample available, continues sending the last sample + * with the old timestamp if we already read all the samples. + */ + HILSensorData sampleImpl() override + { + if (initialized) + { + /* updates the last_sensor only if there is still data to be read */ + if (sampleCounter >= n_data_sensor) + { + this->lastError = Boardcore::SensorErrors::NO_NEW_DATA; + /*TRACE("[%s] NO NEW DATA! Simulation error\n", + typeid(this).name());*/ + } + else if (this->lastError != Boardcore::SensorErrors::NO_NEW_DATA) + { + return updateData(); + } + } + else + { + this->lastError = Boardcore::SensorErrors::NOT_INIT; + TRACE( + "[HILSensor] sampleImpl() : not initialized, unable to " + "sample data \n"); + } + + return this->lastSample; + } + + /** + * @brief updates the timestamp and increments the sampleCounter. + * WARNING: You should call this method after all the values has been + * updated, it modifies the sampleCounter! + * @return the timestamp of the sample + */ + uint64_t updateTimestamp() + { + sampleCounter++; + return Boardcore::TimestampTimer::getTimestamp(); + } + + /** + * @brief Function that updates the sensor structure with new data. + * + * Sensor struct updated from MatlabTransceiver::sensorData. + * WARNING: This method should call **AT THE END** the updateTimestamp + * method. + */ + virtual HILSensorData updateData() = 0; + + bool initialized = false; + int sampleCounter = 0; /**< counter of the next sample to take */ + int n_data_sensor; /**< number of samples in every period */ + void *sensorData; /**< reference to the Buffer structure */ +}; diff --git a/src/shared/sensors/HILSensors/HILSensorsData.h b/src/shared/sensors/HILSensors/HILSensorsData.h new file mode 100644 index 000000000..0b45f73c2 --- /dev/null +++ b/src/shared/sensors/HILSensors/HILSensorsData.h @@ -0,0 +1,136 @@ +/* Copyright (c) 2021-2023 Skyward Experimental Rocketry + * Author: Luca Conterio + * + * 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 <sensors/SensorData.h> +#include <sensors/analog/Pitot/PitotData.h> + +struct HILAccelerometerData : public Boardcore::AccelerometerData +{ + static std::string header() + { + return "timestamp,accelerationX,accelerationY,accelerationZ\n"; + } + + void print(std::ostream& os) const + { + os << accelerationTimestamp << "," << accelerationX << "," + << accelerationY << "," << accelerationZ << "\n"; + } +}; + +struct HILGyroscopeData : public Boardcore::GyroscopeData +{ + static std::string header() + { + return "timestamp,angularSpeedX,angularSpeedY,angularSpeedZ\n"; + } + + void print(std::ostream& os) const + { + os << angularSpeedTimestamp << "," << angularSpeedX << "," + << angularSpeedY << "," << angularSpeedZ << "\n"; + } +}; + +struct HILMagnetometerData : public Boardcore::MagnetometerData +{ + static std::string header() + { + return "timestamp,magneticFieldX,magneticFieldY,magneticFieldZ\n"; + } + + void print(std::ostream& os) const + { + os << magneticFieldTimestamp << "," << magneticFieldX << "," + << magneticFieldY << "," << magneticFieldZ << "\n"; + } +}; + +struct HILImuData : public HILAccelerometerData, + public HILGyroscopeData, + public HILMagnetometerData +{ + static std::string header() + { + return "accelerationTimestamp,accelerationX,accelerationY," + "accelerationZ,angularSpeedTimestamp,angularSpeedX," + "angularSpeedY,angularSpeedZ,magneticFieldTimestamp," + "magneticFieldX,magneticFieldY,magneticFieldZ\n"; + } + + void print(std::ostream& os) const + { + os << accelerationTimestamp << "," << accelerationX << "," + << accelerationY << "," << accelerationZ << "," + << angularSpeedTimestamp << "," << angularSpeedX << "," + << angularSpeedY << "," << angularSpeedZ << "," + << magneticFieldTimestamp << "," << magneticFieldX << "," + << magneticFieldY << "," << magneticFieldZ << "\n"; + } +}; + +struct HILGpsData : public Boardcore::GPSData +{ + static std::string header() + { + return "timestamp,latitude,longitude,height,velocityNorth,velocityEast," + "velocityDown,speed,track,positionDOP,satellites,fix\n"; + } + + void print(std::ostream& os) const + { + os << gpsTimestamp << "," << longitude << "," << latitude << "," + << height << "," << velocityNorth << "," << velocityEast << "," + << velocityDown << "," << speed << "," << track << "," << positionDOP + << "," << (int)satellites << "," << (int)fix << "\n"; + } +}; + +struct HILBarometerData : public Boardcore::PressureData +{ + static std::string header() { return "timestamp,pressure\n"; } + + void print(std::ostream& os) const + { + os << pressureTimestamp << "," << pressure << "\n"; + } +}; + +struct HILPitotData : public Boardcore::PitotData +{ + static std::string header() { return "timestamp,deltaP,airspeed\n"; } + + void print(std::ostream& os) const + { + os << timestamp << "," << deltaP << "," << airspeed << "\n"; + } +}; + +struct HILTempData : public Boardcore::TemperatureData +{ + static std::string header() { return "timestamp,temperature\n"; } + + void print(std::ostream& os) const + { + os << temperatureTimestamp << "," << temperature << "\n"; + } +}; diff --git a/src/shared/sensors/HILSensors/HILTemperature.h b/src/shared/sensors/HILSensors/HILTemperature.h new file mode 100644 index 000000000..765382856 --- /dev/null +++ b/src/shared/sensors/HILSensors/HILTemperature.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2020-2023 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 <logger/Logger.h> + +#include "HILSensor.h" + +/** + * @brief fake barometer sensor used for the simulation. + * + * This class is used to simulate as near as possible the situation of the + * OBSW during the flight, using fake sensors classes instead of the real + * ones, taking their data from the data received from a simulator. + */ +class HILTemperature : public HILSensor<HILTempData> +{ +public: + HILTemperature(int n_data_sensor, void *sensorData) + : HILSensor(n_data_sensor, sensorData) + { + } + +protected: + HILTempData updateData() override + { + HILTempData tempData; + + tempData.temperature = + reinterpret_cast<HILConfig::SimulatorData::Temperature *>( + sensorData) + ->measure; + tempData.temperatureTimestamp = updateTimestamp(); + + Boardcore::Logger::getInstance().log(tempData); + + return tempData; + } +}; diff --git a/src/shared/sensors/HILSensors/HILTimestampManagement.h b/src/shared/sensors/HILSensors/HILTimestampManagement.h new file mode 100644 index 000000000..a893d6871 --- /dev/null +++ b/src/shared/sensors/HILSensors/HILTimestampManagement.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2020-2023 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 + +/** + * @brief Interface in order to reset the parameters of the SimSensor even if + * they are templated on different SimSensorData + */ +class HILTimestampManagement +{ +public: + HILTimestampManagement() {} + + /** + * @brief sets the sample counter to 0. + * + * Updates the reference timestamp, resets the sampleCounter and clears the + * last_error variable. Called by the MatlabTransceiver when receives a new + * simulated period + */ + virtual void resetSampleCounter() = 0; +}; \ No newline at end of file diff --git a/src/shared/sensors/HILSensors/IncludeHILSensors.h b/src/shared/sensors/HILSensors/IncludeHILSensors.h new file mode 100644 index 000000000..21b5210ff --- /dev/null +++ b/src/shared/sensors/HILSensors/IncludeHILSensors.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2021-2023 Skyward Experimental Rocketry + * Author: Luca Conterio + * + * 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 "HILAccelerometer.h" +#include "HILBarometer.h" +#include "HILGps.h" +#include "HILGyroscope.h" +#include "HILMagnetometer.h" +#include "HILPitot.h" +#include "HILSensor.h" +#include "HILTemperature.h" +#include "HILTimestampManagement.h" \ No newline at end of file diff --git a/src/tests/hil/HILSimulationConfig.h b/src/tests/hil/HILSimulationConfig.h new file mode 100644 index 000000000..55dee4d62 --- /dev/null +++ b/src/tests/hil/HILSimulationConfig.h @@ -0,0 +1,328 @@ +/* Copyright (c) 2020-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 <drivers/timer/TimestampTimer.h> +#include <drivers/usart/USART.h> +#include <utils/Debug.h> +#include <utils/Stats/Stats.h> + +#include <list> +#include <utils/ModuleManager/ModuleManager.hpp> + +#include "algorithms/ADA/ADAData.h" +#include "algorithms/NAS/NAS.h" +#include "algorithms/NAS/NASState.h" +#include "sensors/SensorInfo.h" + +namespace HILConfig +{ + +struct SensorConfig : public Boardcore::SensorInfo +{ + SensorConfig(const std::string s, const uint32_t period) + : Boardcore::SensorInfo{s, period, []() {}, true} + { + } +}; + +/** baudrate of connection */ +const int SIM_BAUDRATE = 115200; + +/** Period of simulation in milliseconds */ +const int SIMULATION_PERIOD = 100; + +/** sample frequency of sensor (samples/second) */ +const int ACCEL_FREQ = 100; +const int GYRO_FREQ = 100; +const int MAGN_FREQ = 100; +const int IMU_FREQ = 100; +const int BARO_FREQ = 20; +const int PITOT_FREQ = 20; +const int TEMP_FREQ = 10; +const int GPS_FREQ = 10; + +// /** update frequency of airbrakes control algorithm */ +// const int CONTROL_FREQ = 10; + +// /** min and max values in radiants of the actuator */ +// const float MinAlphaDegree = 0.0; +// const float MaxAlphaDegree = 0.84; + +/** sensors configuration */ +const SensorConfig accelConfig("accel", ACCEL_FREQ); +const SensorConfig gyroConfig("gyro", GYRO_FREQ); +const SensorConfig magnConfig("magn", MAGN_FREQ); +const SensorConfig imuConfig("imu", IMU_FREQ); +const SensorConfig baroConfig("baro", BARO_FREQ); +const SensorConfig pitotConfig("pitot", PITOT_FREQ); +const SensorConfig gpsConfig("gps", GPS_FREQ); +const SensorConfig tempConfig("temp", TEMP_FREQ); + +/** Number of samples per sensor at each simulator iteration */ +const int N_DATA_ACCEL = (ACCEL_FREQ * SIMULATION_PERIOD) / 1000; // 10 +const int N_DATA_GYRO = (GYRO_FREQ * SIMULATION_PERIOD) / 1000; // 10 +const int N_DATA_MAGN = (MAGN_FREQ * SIMULATION_PERIOD) / 1000; // 10 +const int N_DATA_IMU = (IMU_FREQ * SIMULATION_PERIOD) / 1000; // 10 +const int N_DATA_BARO = (BARO_FREQ * SIMULATION_PERIOD) / 1000; // 2 +const int N_DATA_PITOT = (PITOT_FREQ * SIMULATION_PERIOD) / 1000; // 2 +const int N_DATA_GPS = (GPS_FREQ * SIMULATION_PERIOD) / 1000; // 1 +const int N_DATA_TEMP = (TEMP_FREQ * SIMULATION_PERIOD) / 1000; // 1 + +/** + * @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 Accelerometer + { + float measures[N_DATA_ACCEL][3]; + } accelerometer; + + struct Gyro + { + float measures[N_DATA_GYRO][3]; + } gyro; + + struct Magnetometer + { + float measures[N_DATA_MAGN][3]; + } magnetometer; + + struct Gps + { + float positionMeasures[N_DATA_GPS][3]; + float velocityMeasures[N_DATA_GPS][3]; + float fix; + float num_satellites; + } gps; + + struct Barometer + { + float measures[N_DATA_BARO]; + } barometer1, barometer2, barometer3; + + struct Pitot + { + float deltaP[N_DATA_PITOT]; + float staticPressure[N_DATA_PITOT]; + } pitot; + + struct Temperature + { + float measure; + } temperature; + + struct Flags + { + float flag_flight; + float flag_ascent; + float flag_burning; + float flag_airbrakes; + float flag_para1; + float flag_para2; + } flags; + + void printAccelerometer() + { + TRACE("accel\n"); + for (int i = 0; i < N_DATA_ACCEL; i++) + TRACE("%+.3f\t%+.3f\t%+.3f\n", accelerometer.measures[i][0], + accelerometer.measures[i][1], accelerometer.measures[i][2]); + } + + void printGyro() + { + TRACE("gyro\n"); + for (int i = 0; i < N_DATA_GYRO; i++) + TRACE("%+.3f\t%+.3f\t%+.3f\n", gyro.measures[i][0], + gyro.measures[i][1], gyro.measures[i][2]); + } + + void printMagnetometer() + { + TRACE("magneto\n"); + for (int i = 0; i < N_DATA_MAGN; i++) + TRACE("%+.3f\t%+.3f\t%+.3f\n", magnetometer.measures[i][0], + magnetometer.measures[i][1], magnetometer.measures[i][2]); + } + + void printGPS() + { + TRACE("gps\n"); + TRACE("pos\n"); + for (int i = 0; i < N_DATA_GPS; i++) + TRACE("%+.3f\t%+.3f\t%+.3f\n", gps.positionMeasures[i][0], + gps.positionMeasures[i][1], gps.positionMeasures[i][2]); + + TRACE("vel\n"); + for (int i = 0; i < N_DATA_GPS; i++) + TRACE("%+.3f\t%+.3f\t%+.3f\n", gps.velocityMeasures[i][0], + gps.velocityMeasures[i][1], gps.velocityMeasures[i][2]); + TRACE("fix:%+.3f\tnsat:%+.3f\n", gps.fix, gps.num_satellites); + } + + void printBarometer1() + { + TRACE("press1\n"); + for (int i = 0; i < N_DATA_BARO; i++) + TRACE("%+.3f\n", barometer1.measures[i]); + } + + void printBarometer2() + { + TRACE("press2\n"); + for (int i = 0; i < N_DATA_BARO; i++) + TRACE("%+.3f\n", barometer2.measures[i]); + } + + void printBarometer3() + { + TRACE("press3\n"); + for (int i = 0; i < N_DATA_BARO; i++) + TRACE("%+.3f\n", barometer3.measures[i]); + } + + void printPitot() + { + TRACE("pitot\n"); + for (int i = 0; i < N_DATA_PITOT; i++) + TRACE("%+.3f, \n", pitot.staticPressure[i], pitot.deltaP[i]); + } + + void printTemperature() + { + TRACE("temp\n"); + for (int i = 0; i < N_DATA_TEMP; i++) + TRACE("%+.3f\n", temperature.measure); + } + + void printFlags() + { + TRACE("flags\n"); + TRACE( + "flight:\t%+.3f\n" + "ascent:\t%+.3f\n" + "burning:\t%+.3f\n" + "airbrakes:\t%+.3f\n" + "para1:\t%+.3f\n" + "para2:\t%+.3f\n", + flags.flag_flight, flags.flag_ascent, flags.flag_burning, + flags.flag_airbrakes, flags.flag_para1, flags.flag_para2); + } + + void print() + { + printAccelerometer(); + printGyro(); + printMagnetometer(); + printGPS(); + printBarometer1(); + printBarometer2(); + printBarometer3(); + printPitot(); + printTemperature(); + printFlags(); + } +}; + +/** + * @brief ADA data sent to the simulator + */ +struct ADAdataHIL +{ + uint64_t ada_timestamp; + float aglAltitude; // Altitude at mean sea level [m]. + float verticalSpeed; // Vertical speed [m/s]. + + ADAdataHIL& operator+=(const ADAdataHIL& x) + { + this->ada_timestamp += x.ada_timestamp; + this->aglAltitude += x.aglAltitude; + this->verticalSpeed += x.verticalSpeed; + + return *this; // return the result by reference + } + + ADAdataHIL operator/(int x) + { + return ADAdataHIL{this->ada_timestamp / x, this->aglAltitude / x, + this->verticalSpeed / x}; + } + + ADAdataHIL operator*(int x) + { + return ADAdataHIL{this->ada_timestamp * x, this->aglAltitude * x, + this->verticalSpeed * x}; + } + + static std::string header() + { + return "timestamp,aglAltitude,verticalSpeed\n"; + } + + void print(std::ostream& os) const + { + os << ada_timestamp << "," << aglAltitude << "," << verticalSpeed + << "\n"; + } +}; + +/** + * @brief Data structure expected by the simulator + */ +struct ActuatorData +{ + Boardcore::NASState nasState; ///< NAS + ADAdataHIL adaState; ///< ADA + float airbrakes_opening; ///< Airbrakes opening (percentage) + float estimated_mass; ///< Estimated mass of the rocket + float liftoff; ///< Flag for liftoff + float burning_shutdown; ///< Flag for engine shutdown + + void print() const + { + TRACE( + "size:%u, %u, %u\n" + "abk:%f\n" + "tsnas:%f\n" + "ned:%f,%f,%f\n" + "vned:%f,%f,%f\n" + "q:%f,%f,%f,%f\n" + "bias:%f,%f,%f\n" + "tsada:%f\n" + "ada:%f,%f\n\n", + sizeof(airbrakes_opening), sizeof(Boardcore::NASState), + sizeof(ADAdataHIL), airbrakes_opening, nasState.timestamp, + nasState.n, nasState.e, nasState.d, nasState.vn, nasState.ve, + nasState.vd, nasState.qx, nasState.qy, nasState.qz, nasState.qw, + nasState.bx, nasState.by, nasState.bz, adaState.ada_timestamp, + adaState.aglAltitude, adaState.verticalSpeed); + } +}; + +} // namespace HILConfig diff --git a/src/tests/hil/test-hil.cpp b/src/tests/hil/test-hil.cpp new file mode 100644 index 000000000..bd712dfdc --- /dev/null +++ b/src/tests/hil/test-hil.cpp @@ -0,0 +1,139 @@ +/* Copyright (c) 2023 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. + */ + +#include <diagnostic/CpuMeter/CpuMeter.h> +#include <diagnostic/PrintLogger.h> +#include <diagnostic/StackLogger.h> +#include <drivers/timer/TimestampTimer.h> +#include <events/EventBroker.h> +#include <events/EventData.h> +#include <events/utils/EventSniffer.h> +#include <hil/Events.h> +#include <hil/HIL.h> +#include <hil/HILConfig.h> +#include <hil/HILFlightPhasesManager.h> +#include <scheduler/TaskScheduler.h> + +#include <utils/ModuleManager/ModuleManager.hpp> + +#include "HILSimulationConfig.h" + +using namespace Boardcore; +using namespace Common; +using namespace HILConfig; + +int main() +{ + // Overall status, if at some point it becomes false, there is a problem + // somewhere + bool initResult = true; + PrintLogger logger = Logging::getLogger("main"); + + // Create modules + USART usart2(USART2, 115200); + HIL* hil = new HIL(usart2, new HILFlightPhasesManager()); + Boardcore::TaskScheduler scheduler; + + // Insert modules + if (!ModuleManager::getInstance().insert<HIL>(hil)) + { + initResult = false; + LOG_ERR(logger, "Error inserting the HIL module"); + } + + // Start modules + if (!EventBroker::getInstance().start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the EventBroker module"); + } + + if (!scheduler.start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the board scheduler module"); + } + + if (!ModuleManager::getInstance().get<HIL>()->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the HIL module"); + } + + scheduler.addTask( + [&]() + { + HILConfig::SimulatorData* sensorData = + ModuleManager::getInstance() + .get<HIL>() + ->simulator->getSensorData(); + HILConfig::ADAdataHIL adaDataHil{ + Boardcore::TimestampTimer::getTimestamp(), + sensorData->gps.positionMeasures[0][2], + sensorData->gps.velocityMeasures[0][2]}; + HILConfig::ActuatorData actuatorData{ + NASState{}, + adaDataHil, + 0, + 30, + sensorData->flags.flag_ascent, + sensorData->flags.flag_burning}; + + // Sending to the simulator + ModuleManager::getInstance().get<HIL>()->send(actuatorData); + }, + HILConfig::SIMULATION_PERIOD); + + // scheduler.addTask( + // [&]() { + // ModuleManager::getInstance().get<HIL>()->simulator->getSensorData()->print(); + // }, 1000); + + // Check the init result and launch an event + if (initResult) + { + // Post OK + LOG_INFO(logger, "Init OK"); + + // Set the LED status + miosix::ledOn(); + } + else + { + LOG_ERR(logger, "Init ERROR"); + } + + // Periodic statistics + while (true) + { + Thread::sleep(1000); + printf("max: %.3f, min: %.3f, free stack: %ld, free heap: %ld\n", + CpuMeter::getCpuStats().maxValue, + CpuMeter::getCpuStats().minValue, + CpuMeter::getCpuStats().freeStack, + CpuMeter::getCpuStats().freeHeap); + CpuMeter::resetCpuStats(); + StackLogger::getInstance().log(); + } + + return 0; +} \ No newline at end of file -- GitLab