diff --git a/CMakeLists.txt b/CMakeLists.txt index 56712bb3afe44c8461c32c036cd918ac2b1537b2..670a5267d89485f34359194a6a46640f52f24ff7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,13 +103,6 @@ add_executable(nokia-groundstation-entry target_include_directories(nokia-groundstation-entry PRIVATE ${OBSW_INCLUDE_DIRS}) sbs_target(nokia-groundstation-entry stm32f429zi_nokia) -add_executable(test-actuators - src/Groundstation/Automated/test-actuators.cpp - ${LYRA_GS} ${GROUNDSTATION_COMMON} -) -target_include_directories(test-actuators PRIVATE ${OBSW_INCLUDE_DIRS}) -sbs_target(test-actuators stm32f767zi_lyra_gs) - add_executable(lyra-gs-entry src/Groundstation/LyraGS/lyra-gs-entry.cpp ${LYRA_GS} ${GROUNDSTATION_COMMON} diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 2f96687df87daa93c68bb634bba780abc776c704..16ba691682e42b64f0e2fb07bac74023c28b3d86 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -45,6 +45,7 @@ set(MAIN_COMPUTER set(GROUNDSTATION_COMMON src/Groundstation/Common/Radio/RadioBase.cpp src/Groundstation/Common/Ports/EthernetBase.cpp + src/Groundstation/Common/Ports/EthernetSniffer.cpp src/Groundstation/Common/Ports/Serial.cpp src/Groundstation/Common/HubBase.cpp ) diff --git a/scripts/logdecoder/logdecoder.cpp b/scripts/logdecoder/logdecoder.cpp index 72530d5fdc0dc6aa41944d872011ceecb9822974..d6ca802b9ccb2f320ef4f80df17dc5ef3d1dc313 100644 --- a/scripts/logdecoder/logdecoder.cpp +++ b/scripts/logdecoder/logdecoder.cpp @@ -21,8 +21,11 @@ */ #include <Groundstation/Automated/Actuators/ActuatorsData.h> +#include <Groundstation/Automated/HubData.h> +#include <Groundstation/Automated/LogSniffing.h> #include <Groundstation/Automated/PinHandler/PinData.h> #include <Groundstation/Automated/SMA/SMAData.h> +#include <Groundstation/LyraGS/Radio/RadioData.h> #include <Main/PinHandler/PinData.h> #include <Main/Sensors/SensorsData.h> #include <Main/StateMachines/ABKController/ABKControllerData.h> @@ -46,6 +49,7 @@ #include <RIGv2/StateMachines/TARS3/TARS3Data.h> #include <algorithms/MEA/MEAData.h> #include <fmt/format.h> +#include <algorithms/Propagator/PropagatorData.h> #include <logger/Deserializer.h> #include <logger/LogTypes.h> #include <logger/Logger.h> @@ -134,9 +138,16 @@ void registerTypes(Deserializer& ds) // Groundstation (ARP) ds.registerType<Antennas::StepperXData>(); ds.registerType<Antennas::StepperYData>(); + ds.registerType<VN300Data>(); + ds.registerType<NASState>(); + ds.registerType<PropagatorState>(); + ds.registerType<AntennaAnglesLog>(); + ds.registerType<GPSData>(); ds.registerType<Antennas::SMAStatus>(); ds.registerType<Antennas::PinChangeData>(); - ds.registerType<AntennaAnglesLog>(); + ds.registerType<LyraGS::MainRadioLog>(); + ds.registerType<Antennas::LogSniffing>(); + ds.registerType<Antennas::HubData>(); } // cppcheck-suppress passedByValue diff --git a/src/Groundstation/Automated/Actuators/Actuators.h b/src/Groundstation/Automated/Actuators/Actuators.h index 0d457b1af7b15c5b796d715ad9ca2758f13a5ba8..18310d820724c3f478f58b16136921560581875c 100644 --- a/src/Groundstation/Automated/Actuators/Actuators.h +++ b/src/Groundstation/Automated/Actuators/Actuators.h @@ -126,31 +126,31 @@ public: } } -private: - Boardcore::StepperPWM* getStepper(StepperList stepper) + const StepperConfig* getStepperConfig(StepperList stepper) const { switch (stepper) { case StepperList::STEPPER_X: - return &stepperX; + return &Antennas::Config::stepperXConfig; case StepperList::STEPPER_Y: - return &stepperY; + return &Antennas::Config::stepperYConfig; default: - assert(false && "Non existent stepper"); + assert(false && "Non existent stepperConfig"); return nullptr; } }; - const StepperConfig* getStepperConfig(StepperList stepper) const +private: + Boardcore::StepperPWM* getStepper(StepperList stepper) { switch (stepper) { case StepperList::STEPPER_X: - return &Antennas::Config::stepperXConfig; + return &stepperX; case StepperList::STEPPER_Y: - return &Antennas::Config::stepperYConfig; + return &stepperY; default: - assert(false && "Non existent stepperConfig"); + assert(false && "Non existent stepper"); return nullptr; } }; diff --git a/src/Groundstation/Automated/Config/SMAConfig.h b/src/Groundstation/Automated/Config/SMAConfig.h index e22f0d193040846badd9b80a333e262c0ec8b063..8f2de625b950a0352a2e3166f8bc360c528683a5 100644 --- a/src/Groundstation/Automated/Config/SMAConfig.h +++ b/src/Groundstation/Automated/Config/SMAConfig.h @@ -1,5 +1,5 @@ /* Copyright (c) 2024 Skyward Experimental Rocketry - * Author: Federico Lolli + * Author: Federico Lolli, Nicolò Caruso * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,5 +34,17 @@ namespace SMAConfig /// @brief Period of the propagator algorithm [ms]. constexpr milliseconds UPDATE_PERIOD = 100ms; // 10 Hz +// No feedback gains for the Follower +static constexpr float YAW_GAIN_NF = + 1.0; ///< Yaw gain for the no feedback states +static constexpr float PITCH_GAIN_NF = + 1.0; ///< Pitch gain for the no feedback states + +// Feedback gains for the Follower +// Note that the Follower applies another limitation to avoid gains over 1 +static constexpr float YAW_GAIN_F = 0.1; ///< Yaw gain for the feedback states +static constexpr float PITCH_GAIN_F = + 1.0; ///< Pitch gain for the feedback states + } // namespace SMAConfig } // namespace Antennas diff --git a/src/Groundstation/Automated/Hub.cpp b/src/Groundstation/Automated/Hub.cpp index adc96f2c6ac6e0f43b20e1b10e2cede944e2b877..8a0a408a614d356a8bb6133344302482f7bcf12c 100644 --- a/src/Groundstation/Automated/Hub.cpp +++ b/src/Groundstation/Automated/Hub.cpp @@ -31,6 +31,7 @@ #include <algorithms/NAS/NASState.h> #include <common/Events.h> #include <common/MavlinkLyra.h> +#include <diagnostic/CpuMeter/CpuMeter.h> #include <logger/Logger.h> #include <sensors/SensorData.h> @@ -44,6 +45,13 @@ using namespace miosix; void Hub::dispatchOutgoingMsg(const mavlink_message_t& msg) { + logHubData.timestamp = TimestampTimer::getTimestamp(); + logHubData.groundRx = logHubData.groundRx + 1; + logHubData.cpuMean = CpuMeter::getCpuStats().mean; + + Logger::getInstance().log(logHubData); + + TRACE("[info] Hub: Packet arrived from outgoing messages!!!\n"); LyraGS::BoardStatus* status = getModule<LyraGS::BoardStatus>(); LyraGS::RadioMain* radioMain = getModule<LyraGS::RadioMain>(); @@ -60,6 +68,7 @@ void Hub::dispatchOutgoingMsg(const mavlink_message_t& msg) sendNack(msg, 306); } + // Message for ARP if (msg.sysid == MAV_SYSID_ARP) { switch (msg.msgid) @@ -239,6 +248,28 @@ void Hub::dispatchOutgoingMsg(const mavlink_message_t& msg) } } } + + // In case the message is spoofed from ethernet by another groundstation + if (msg.msgid == MAVLINK_MSG_ID_ROCKET_FLIGHT_TM || + msg.msgid == MAVLINK_MSG_ID_ROCKET_STATS_TM) + { + TRACE( + "[info][SNIFFING] Hub: A MAIN packet was received from ground " + "packet " + "(ethernet probably and NOT radio)\n"); + /* The message received by ethernet (outgoing) in reality is not a + * command but the telemetry spoofed, therefore is then used as incoming + */ + dispatchIncomingMsg(msg); + LogSniffing sniffing = {TimestampTimer::getTimestamp(), 1}; + Logger::getInstance().log(sniffing); + + logHubData.timestamp = TimestampTimer::getTimestamp(); + logHubData.sniffedRx = logHubData.sniffedRx + 1; + logHubData.cpuMean = CpuMeter::getCpuStats().mean; + + Logger::getInstance().log(logHubData); + } } void Hub::dispatchIncomingMsg(const mavlink_message_t& msg) @@ -250,11 +281,33 @@ void Hub::dispatchIncomingMsg(const mavlink_message_t& msg) (void)serial; #endif + logHubData.timestamp = TimestampTimer::getTimestamp(); + logHubData.rocketRx = logHubData.rocketRx + 1; + logHubData.cpuMean = CpuMeter::getCpuStats().mean; + + Logger::getInstance().log(logHubData); + // Extracting NAS rocket state if (msg.msgid == MAVLINK_MSG_ID_ROCKET_FLIGHT_TM) { mavlink_rocket_flight_tm_t rocketTM; mavlink_msg_rocket_flight_tm_decode(&msg, &rocketTM); + uint64_t timestamp = rocketTM.timestamp; + TRACE( + "[info][Radio/Sniffing] Hub: A FLIGHT_ROCKET_TM packet was " + "received " + "packet with ts %llu\n", + timestamp); + /* Messages older and within the discard interval are treated as old + * messages*/ + if (timestamp > lastFlightTMTimestamp - DISCARD_MSG_DELAY && + timestamp <= lastFlightTMTimestamp) + return; + TRACE( + "[info][Radio/Sniffing] Hub: A FLIGHT_ROCKET_TM packet is valid " + "with ts %llu\n", + timestamp); + lastFlightTMTimestamp = timestamp; NASState nasState{ mavlink_msg_rocket_flight_tm_get_timestamp(&msg), Eigen::Matrix<float, 13, 1>( @@ -273,9 +326,25 @@ void Hub::dispatchIncomingMsg(const mavlink_message_t& msg) { mavlink_rocket_stats_tm_t rocketST; mavlink_msg_rocket_stats_tm_decode(&msg, &rocketST); + TRACE( + "[info][Radio/Sniffing] Hub: A ROCKET_STAT_TM packet was received " + "packet with ts %llu\n", + rocketST.timestamp); + /* Messages older and within the discard interval are treated as old + * messages*/ + if (rocketST.timestamp > lastStatsTMTimestamp - DISCARD_MSG_DELAY && + rocketST.timestamp <= lastStatsTMTimestamp) + return; + TRACE( + "[info][Radio/Sniffing] Hub: A ROCKET_STAT_TM packet is valid, " + "with ts %llu\n", + rocketST.timestamp); + lastStatsTMTimestamp = rocketST.timestamp; + + // TODO: The origin should have its own struct since only timestamp and + // [lat, lon, alt] are needed GPSData gpsState; - gpsState = getRocketOrigin(); - + getRocketOrigin(gpsState); gpsState.gpsTimestamp = rocketST.timestamp; gpsState.latitude = rocketST.ref_lat; gpsState.longitude = rocketST.ref_lon; @@ -300,25 +369,32 @@ void Hub::sendAck(const mavlink_message_t& msg) dispatchIncomingMsg(ackMsg); } -GPSData Hub::getRocketOrigin() +bool Hub::getRocketOrigin(Boardcore::GPSData& rocketOrigin) { Lock<FastMutex> lock(coordinatesMutex); - return lastRocketCoordinates; + rocketOrigin = lastRocketCoordinates; + return originReceived; } -NASState Hub::getRocketNasState() +bool Hub::getLastRocketNasState(Boardcore::NASState& nasState) { Lock<FastMutex> lock(nasStateMutex); - flagNasSet = false; - return lastRocketNasState; + nasState = lastRocketNasState; + hasNewNasSet = false; + return rocketNasSet; } -bool Hub::hasNasSet() { return flagNasSet; } +bool Hub::hasNewNasState() +{ + Lock<FastMutex> lock(nasStateMutex); + return hasNewNasSet; +} void Hub::setRocketNasState(const NASState& newRocketNasState) { Lock<FastMutex> lock(nasStateMutex); - flagNasSet = true; + hasNewNasSet = true; + rocketNasSet = true; lastRocketNasState = newRocketNasState; } @@ -326,4 +402,5 @@ void Hub::setRocketOrigin(const GPSData& newRocketCoordinates) { Lock<FastMutex> lock(coordinatesMutex); lastRocketCoordinates = newRocketCoordinates; + originReceived = true; } diff --git a/src/Groundstation/Automated/Hub.h b/src/Groundstation/Automated/Hub.h index 9130dd058466db23751f9c511fd702c40ef3a5de..a61b174e89b8bbab7788287d2c11da112aef3c93 100644 --- a/src/Groundstation/Automated/Hub.h +++ b/src/Groundstation/Automated/Hub.h @@ -22,6 +22,8 @@ #pragma once +#include <Groundstation/Automated/HubData.h> +#include <Groundstation/Automated/LogSniffing.h> #include <Groundstation/Automated/SMA/SMA.h> #include <Groundstation/Common/HubBase.h> #include <Groundstation/LyraGS/BoardStatus.h> @@ -42,6 +44,13 @@ class BoardStatus; namespace Antennas { +/* This is used to avoid discarding messages in case the rocket timestamp is + * reset. Therefore if older than the discard msg delay, resets the messages + * last timestamp */ +static constexpr uint64_t DISCARD_MSG_DELAY = + 10 * 1000000; ///< Maximum time for which the message, if older is + ///< discarded. [micros] + /** * @brief Central hub connecting all outgoing and ingoing modules. */ @@ -72,14 +81,17 @@ public: /** * @brief Synchronized getter for the last rocket origin for NAS. */ - Boardcore::GPSData getRocketOrigin(); + bool getRocketOrigin(Boardcore::GPSData& rocketOrigin); /** * @brief Synchronized getter for the last rocket NAS state. + * + * @return true only if the rocket NAS state and is valid and the value is + * new (got from radio) */ - Boardcore::NASState getRocketNasState(); + bool getLastRocketNasState(Boardcore::NASState& nasState); - bool hasNasSet(); + bool hasNewNasState(); private: /** @@ -93,10 +105,16 @@ private: void setRocketOrigin(const Boardcore::GPSData& newRocketCoordinates); Boardcore::GPSData lastRocketCoordinates; + bool originReceived = false; Boardcore::NASState lastRocketNasState; + bool rocketNasSet = false; miosix::FastMutex coordinatesMutex; miosix::FastMutex nasStateMutex; - bool flagNasSet = false; + bool hasNewNasSet = false; + uint64_t lastFlightTMTimestamp = 0; + uint64_t lastStatsTMTimestamp = 0; + + HubData logHubData; // Data for logging }; } // namespace Antennas diff --git a/src/Groundstation/Automated/HubData.h b/src/Groundstation/Automated/HubData.h new file mode 100644 index 0000000000000000000000000000000000000000..375b112c973c9ab9d3421abbe1aca438f2ab23ca --- /dev/null +++ b/src/Groundstation/Automated/HubData.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2024 Skyward Experimental Rocketry + * Author: Nicolò Caruso + * + * 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 <stdint.h> + +#include <iostream> +#include <string> + +namespace Antennas +{ + +/** + * @brief Structure to save informations about the Hub reception + */ +struct HubData +{ + uint64_t timestamp = 0; + uint16_t groundRx = 0; + uint16_t rocketRx = 0; + uint16_t sniffedRx = 0; + float cpuMean = 0; + + static std::string header() + { + return "timestamp,groundRx,rocketRx,sniffedRx,cpuMean\n"; + } + + void print(std::ostream& os) const + { + os << timestamp << "," << groundRx << "," << rocketRx << "," + << sniffedRx << "," << cpuMean << "\n"; + } +}; +} // namespace Antennas diff --git a/src/Groundstation/Automated/LogSniffing.h b/src/Groundstation/Automated/LogSniffing.h new file mode 100644 index 0000000000000000000000000000000000000000..f8024870465438b6b7783f6d8943954d2f14022d --- /dev/null +++ b/src/Groundstation/Automated/LogSniffing.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Matteo Pancotti + * + * 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 <stdint.h> + +#include <iostream> +#include <string> + +namespace Antennas +{ + +struct LogSniffing +{ + uint64_t timestamp; + uint8_t sniffed; + + static std::string header() { return "timestamp,sniffed\n"; } + + void print(std::ostream& os) const + { + os << timestamp << "," << sniffed << "\n"; + } +}; + +} // namespace Antennas diff --git a/src/Groundstation/Automated/SMA/SMA.cpp b/src/Groundstation/Automated/SMA/SMA.cpp index 57b1707431eac53f15799160dfeb1d7feca1c915..cbe0fd980e7e2a445d69c55096c38da716407d16 100644 --- a/src/Groundstation/Automated/SMA/SMA.cpp +++ b/src/Groundstation/Automated/SMA/SMA.cpp @@ -94,14 +94,19 @@ void SMA::setRocketNASOrigin(const Boardcore::GPSData& rocketCoordinates) ActuationStatus SMA::moveStepperDeg(StepperList stepperId, float angle) { - if (!testState(&SMA::state_test) && !testState(&SMA::state_test_nf)) + if (!testState(&SMA::state_test) && !testState(&SMA::state_test_nf) && + !testState(&SMA::state_calibrate)) { LOG_ERR(logger, "Stepper can only be manually moved in the TEST state"); return ActuationStatus::NOT_TEST; } else { - return getModule<Actuators>()->moveDeg(stepperId, angle); + auto steppers = getModule<Actuators>(); + + const auto* config = steppers->getStepperConfig(stepperId); + steppers->setSpeed(stepperId, config->MAX_SPEED); + return steppers->moveDeg(stepperId, angle); } } @@ -138,41 +143,50 @@ void SMA::setFatal() { fatalInit = true; }; void SMA::update() { GPSData rocketCoordinates, antennaCoordinates; - VN300Data vn300Data; - - Hub* hub = static_cast<Hub*>(getModule<Groundstation::HubBase>()); - auto* sensors = getModule<Sensors>(); - rocketCoordinates = hub->getRocketOrigin(); - - // Update the antenna position except in case of no feedback - if (status.state != SMAState::FIX_ROCKET_NF && - status.state != SMAState::INIT_ERROR && - status.state != SMAState::ACTIVE_NF && - status.state != SMAState::ARM_READY && - status.state != SMAState::ACTIVE_NF) + VN300Data data; + auto steppers = getModule<Actuators>(); + + Hub* hub = static_cast<Hub*>(getModule<Groundstation::HubBase>()); + auto* sensors = getModule<Sensors>(); + + // TODO: Verify if same with the macrostate + // Update the antenna position if in feedback + // if (testState(&SMA::state_feedback)) + if (testState(&SMA::state_init_done) || testState(&SMA::state_armed) || + testState(&SMA::state_fix_antennas) || + testState(&SMA::state_fix_rocket) || testState(&SMA::state_active) || + testState(&SMA::state_test)) { // update antenna coordinates - vn300Data = sensors->getVN300LastSample(); - if (vn300Data.gpsFix == 3) + data = sensors->getVN300LastSample(); + if (data.gpsFix == 3) { // build the GPSData struct with the VN300 data - antennaCoordinates.gpsTimestamp = vn300Data.insTimestamp; - antennaCoordinates.latitude = vn300Data.latitude; - antennaCoordinates.longitude = vn300Data.longitude; - antennaCoordinates.height = vn300Data.altitude; - antennaCoordinates.velocityNorth = vn300Data.velocityX; - antennaCoordinates.velocityEast = vn300Data.velocityY; - antennaCoordinates.velocityDown = vn300Data.velocityZ; - antennaCoordinates.satellites = vn300Data.gpsFix; - antennaCoordinates.fix = vn300Data.gpsFix; + antennaCoordinates.gpsTimestamp = data.insTimestamp; + antennaCoordinates.latitude = data.latitude; + antennaCoordinates.longitude = data.longitude; + antennaCoordinates.height = data.altitude; + antennaCoordinates.velocityNorth = data.velocityX; + antennaCoordinates.velocityEast = data.velocityY; + antennaCoordinates.velocityDown = data.velocityZ; + antennaCoordinates.satellites = data.gpsFix; + antennaCoordinates.fix = data.gpsFix; // update follower with coordinates follower.setAntennaCoordinates(antennaCoordinates); } } + else + { + // Fake attitude data, taking the stepper current positions as pitch and + // yaw + data.pitch = steppers->getCurrentDegPosition(StepperList::STEPPER_Y); + data.yaw = steppers->getCurrentDegPosition(StepperList::STEPPER_X); + } // update follower with the rocket GPS data - follower.setRocketNASOrigin(rocketCoordinates); + if (hub->getRocketOrigin(rocketCoordinates)) + follower.setRocketNASOrigin(rocketCoordinates); switch (status.state) { @@ -195,6 +209,7 @@ void SMA::update() } break; } + // TODO: See if semantically what we want // in fix_rocket state, wait for the GPS fix of the rocket case SMAState::FIX_ROCKET: case SMAState::FIX_ROCKET_NF: @@ -211,24 +226,26 @@ void SMA::update() } break; } + // in active state, update the follower and propagator inner states case SMAState::ACTIVE: + case SMAState::ACTIVE_NF: { // retrieve the last NAS Rocket state - if (hub->hasNasSet()) - { - NASState nasState = hub->getRocketNasState(); + NASState nasState; + // In case there is a new NAS packet + if (hub->hasNewNasState() && hub->getLastRocketNasState(nasState)) // update the propagator with the NAS state // and retrieve the propagated state propagator.setRocketNasState(nasState); - } + propagator.update(); // step the propagator PropagatorState predicted = propagator.getState(); // update the follower with the propagated state follower.setLastRocketNasState(predicted.getNasState()); - follower.setLastAntennaAttitude(vn300Data); + follower.setLastAntennaAttitude(data); follower.update(); // step the follower FollowerState follow = follower.getState(); @@ -238,7 +255,6 @@ void SMA::update() Boardcore::AntennaAnglesLog(target, predicted.nPropagations)); // actuate the steppers - auto steppers = getModule<Actuators>(); steppers->setSpeed(StepperList::STEPPER_X, follow.horizontalSpeed); steppers->setSpeed(StepperList::STEPPER_Y, follow.verticalSpeed); @@ -262,77 +278,20 @@ void SMA::update() "limit. Error: ", actuation, "\n"); } - - break; } - case SMAState::ACTIVE_NF: - { - VN300Data fakeAttitudeData; - - // retrieve the last NAS Rocket state - if (hub->hasNasSet()) - { - NASState nasState = hub->getRocketNasState(); + break; - // update the propagator with the NAS state - // and retrieve the propagated state - propagator.setRocketNasState(nasState); - } - propagator.update(); // step the propagator - PropagatorState predicted = propagator.getState(); - - auto steppers = getModule<Actuators>(); - - // set the attitude as the current position of the steppers - // FIXME this method of setting the attitude is too dirty - // if the follower is updated something may break here - fakeAttitudeData.pitch = - steppers->getCurrentDegPosition(StepperList::STEPPER_Y); - fakeAttitudeData.yaw = - steppers->getCurrentDegPosition(StepperList::STEPPER_X); - - // update the follower with the propagated state - follower.setLastRocketNasState(predicted.getNasState()); - follower.setLastAntennaAttitude(fakeAttitudeData); - follower.update(); // step the follower - FollowerState follow = follower.getState(); - - // Log the target angles and propagations info - AntennaAngles target = follower.getTargetAngles(); - Boardcore::Logger::getInstance().log( - Boardcore::AntennaAnglesLog(target, predicted.nPropagations)); - - // actuate the steppers - steppers->setSpeed(StepperList::STEPPER_X, follow.horizontalSpeed); - steppers->setSpeed(StepperList::STEPPER_Y, follow.verticalSpeed); - - ActuationStatus actuation = - steppers->moveDeg(StepperList::STEPPER_X, follow.yaw); - if (actuation != ActuationStatus::OK) + case SMAState::CALIBRATE: + { + if (!sensors->isCalibrating()) { - LOG_ERR( - logger, - "Step antenna - STEPPER_X could not move or reached move " - "limit. Error: ", - actuation, "\n"); + EventBroker::getInstance().post(ARP_CAL_DONE, TOPIC_ARP); + LOG_DEBUG(logger, "Exit from calibration\n"); } - - actuation = steppers->moveDeg(StepperList::STEPPER_Y, follow.pitch); - if (actuation != ActuationStatus::OK) - { - LOG_ERR( - logger, - "Step antenna - STEPPER_Y could not move or reached move " - "limit. Error: ", - actuation, "\n"); - } - - break; } + break; default: - { break; - } } } @@ -373,13 +332,19 @@ State SMA::state_feedback(const Event& event) case EV_ENTRY: { logStatus(SMAState::FEEDBACK); - getModule<Leds>()->setOn(LedColor::YELLOW); + getModule<Leds>()->setOff(LedColor::RED); + + // Set the gains for the no feedback phase + if (!follower.setMaxGain(SMAConfig::YAW_GAIN_F, + SMAConfig::PITCH_GAIN_F)) + LOG_ERR(logger, "Follower gain set failed!\n"); + return HANDLED; } case EV_EXIT: { getModule<Actuators>()->disarm(); - getModule<Leds>()->setOff(LedColor::YELLOW); + getModule<Leds>()->setOff(LedColor::RED); return HANDLED; } case EV_EMPTY: @@ -409,13 +374,19 @@ State SMA::state_no_feedback(const Event& event) case EV_ENTRY: { logStatus(SMAState::NO_FEEDBACK); - getModule<Leds>()->setOn(LedColor::YELLOW); + getModule<Leds>()->setOn(LedColor::RED); + + // Set the gains for the no feedback phase + if (!follower.setMaxGain(SMAConfig::YAW_GAIN_NF, + SMAConfig::PITCH_GAIN_NF)) + LOG_ERR(logger, "Follower gain set failed!\n"); + return HANDLED; } case EV_EXIT: { getModule<Actuators>()->disarm(); - getModule<Leds>()->setOff(LedColor::YELLOW); + getModule<Leds>()->setOff(LedColor::RED); return HANDLED; } case EV_EMPTY: @@ -702,6 +673,9 @@ State SMA::state_calibrate(const Event& event) { case EV_ENTRY: { + auto* sensors = getModule<Sensors>(); + if (!sensors->calibrate() && sensors->isCalibrating()) + transition(&SMA::state_test); logStatus(SMAState::CALIBRATE); return HANDLED; } @@ -783,7 +757,7 @@ State SMA::state_fix_rocket(const Event& event) // init the follower before leaving the state // (compute initial arp-rocket distance and bearing) if (!follower.init()) - LOG_ERR(logger, "Follower initialization failed"); + LOG_ERR(logger, "Follower initialization failed\n"); leds->setOn(LedColor::YELLOW); return HANDLED; diff --git a/src/Groundstation/Automated/SMA/SMA.h b/src/Groundstation/Automated/SMA/SMA.h index 2bc290d9a150501f70d4b64d8aa3fb163cf72891..b73b2c66ad6c45b089b1c678d99d6da5a771cb0c 100644 --- a/src/Groundstation/Automated/SMA/SMA.h +++ b/src/Groundstation/Automated/SMA/SMA.h @@ -97,6 +97,7 @@ public: /** * @brief move the stepper `stepperId` of `angle` degrees + * */ ActuationStatus moveStepperDeg(StepperList stepperId, float angle); @@ -108,6 +109,7 @@ public: /** * @brief Setter for the multipliers of the steppers */ + // TODO: See if has sense to remove it... void setMultipliers(StepperList axis, float multiplier); /** diff --git a/src/Groundstation/Automated/Sensors/Sensors.cpp b/src/Groundstation/Automated/Sensors/Sensors.cpp index 866231a74dfb27b41b52d496cde6decf28920177..0fe17cf13e091b16f95e3e3c06a71893bd637508 100644 --- a/src/Groundstation/Automated/Sensors/Sensors.cpp +++ b/src/Groundstation/Automated/Sensors/Sensors.cpp @@ -63,9 +63,33 @@ bool Sensors::vn300Init() return true; } +bool Sensors::calibrate() +{ + // Already in calibration mode. + if (calibrating) + return false; + calibrationStart = std::chrono::nanoseconds(miosix::getTime()); + calibrating = true; + vn300->startHSIEstimator(VN300_CAL_CONVERGENCE); + return true; +} + +bool Sensors::isCalibrating() { return calibrating; } + void Sensors::vn300Callback() { - Logger::getInstance().log(vn300->getLastSample()); + if (calibrating) + { + LOG_DEBUG(logger, "Sensor Manager VN300 calibrating\n"); + if (std::chrono::nanoseconds(miosix::getTime()) - calibrationStart > + VN300_CAL_TIME) + { + vn300->stopHSIEstimator(); + calibrating = false; + } + } + else + Logger::getInstance().log(vn300->getLastSample()); } VN300Data Sensors::getVN300LastSample() { return vn300->getLastSample(); } diff --git a/src/Groundstation/Automated/Sensors/Sensors.h b/src/Groundstation/Automated/Sensors/Sensors.h index 87ce9a237b67bfc8b1923e3bace41de0a20a61b4..43d06d76b209563e578ee2d1e184a35954be4796 100644 --- a/src/Groundstation/Automated/Sensors/Sensors.h +++ b/src/Groundstation/Automated/Sensors/Sensors.h @@ -21,14 +21,23 @@ */ #pragma once +#include <drivers/timer/TimestampTimer.h> #include <utils/DependencyManager/DependencyManager.h> +#include <chrono> + #include "Groundstation/LyraGS/Buses.h" #include "sensors/SensorManager.h" #include "sensors/Vectornav/VN300/VN300.h" namespace Antennas { + +static constexpr uint8_t VN300_CAL_CONVERGENCE = + 4; ///< Calibration convergence parameter for VN300 soft and hard iron + ///< calibration. 5: converge in 60-90sec, 1: converge in 15-20sec +static constexpr std::chrono::seconds VN300_CAL_TIME = std::chrono::seconds(30); + class Sensors : public Boardcore::InjectableWithDeps<LyraGS::Buses> { public: @@ -44,14 +53,27 @@ public: */ Boardcore::VN300Data getVN300LastSample(); + /** + * @brief Trigger the calibration process for soft-hard iron in the VN300 + */ + bool calibrate(); + + /** + * @brief Returns the status of the calibration + */ + bool isCalibrating(); + private: bool vn300Init(); void vn300Callback(); + std::atomic<bool> calibrating{false}; + Boardcore::VN300* vn300 = nullptr; Boardcore::SensorManager* sm = nullptr; Boardcore::SensorManager::SensorMap_t sensorsMap; Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("sensors"); + std::chrono::nanoseconds calibrationStart; }; } // namespace Antennas diff --git a/src/Groundstation/Automated/automated-antennas-entry.cpp b/src/Groundstation/Automated/automated-antennas-entry.cpp deleted file mode 100644 index 3a022fc8cb07f136522f48d9634cc54bc626b7a7..0000000000000000000000000000000000000000 --- a/src/Groundstation/Automated/automated-antennas-entry.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* Copyright (c) 2024 Skyward Experimental Rocketry - * Authors: Riccardo Musso, Emilio Corigliano, Niccolò Betto, Federico Lolli - * - * 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 <Groundstation/Automated/Actuators/Actuators.h> -#include <Groundstation/Automated/Buses.h> -#include <Groundstation/Automated/Hub.h> -#include <Groundstation/Automated/Leds/Leds.h> -#include <Groundstation/Automated/Ports/Ethernet.h> -#include <Groundstation/Automated/Radio/Radio.h> -#include <Groundstation/Automated/SMA/SMA.h> -#include <Groundstation/Automated/Sensors/Sensors.h> -#include <Groundstation/Common/Ports/Serial.h> -#include <Groundstation/LyraGS/BoardStatus.h> -#include <common/Events.h> -#include <diagnostic/PrintLogger.h> -#include <events/EventBroker.h> -#include <miosix.h> -#include <scheduler/TaskScheduler.h> -#include <utils/ButtonHandler/ButtonHandler.h> - -#include <thread> -#include <utils/ModuleManager/ModuleManager.hpp> - -using namespace Groundstation; -using namespace Antennas; -using namespace Common; -using namespace Boardcore; -using namespace miosix; - -GpioPin button = GpioPin(GPIOG_BASE, 10); ///< Emergency stop button - -/** - * @brief Automated Antennas (SkyLink) entrypoint. - * The entrypoint performs the following operations: - * - Initializes software modules - * -> Green LED is turned on when done - * - Waits for the rocket to be powered on and acquire a GPS fix - * -> Yellow LED is turned on when done - * - Waits for the antenna to acquire a GPS fix - * -> Orange LED is turned on when done - * - Initializes the follower - * - Starts the follower task - */ -int main() -{ - ModuleManager& modules = ModuleManager::getInstance(); - PrintLogger logger = Logging::getLogger("automated_antennas"); - bool ok = true; - - button.mode(Mode::INPUT_PULL_UP); - // ButtonHandler - ButtonHandler::getInstance().registerButtonCallback( - button, - [&](ButtonEvent bEvent) - { - if (bEvent == ButtonEvent::LONG_PRESS || - bEvent == ButtonEvent::VERY_LONG_PRESS) - { - ModuleManager::getInstance() - .get<Actuators>() - ->IRQemergencyStop(); - } - }); - ButtonHandler::getInstance().start(); - - TaskScheduler* scheduler_low = new TaskScheduler(0); - TaskScheduler* scheduler_high = new TaskScheduler(); - Leds* leds = new Leds(scheduler_low); - Hub* hub = new Hub(); - Buses* buses = new Buses(); - Serial* serial = new Serial(); - RadioMain* radio_main = new RadioMain(); - BoardStatus* board_status = new BoardStatus(); - Actuators* actuators = new Actuators(); - Sensors* sensors = new Sensors(); - SMA* sma = new SMA(scheduler_high); - Ethernet* ethernet = new Ethernet(); - - // Inserting Modules - { // TODO remove this scope (improve readability) - ok &= modules.insert(sma); - ok &= modules.insert<HubBase>(hub); - ok &= modules.insert(buses); - ok &= modules.insert(serial); - ok &= modules.insert(radio_main); - ok &= modules.insert(board_status); - ok &= modules.insert(actuators); - ok &= modules.insert(sensors); - ok &= modules.insert(ethernet); - ok &= modules.insert(leds); - - // If insertion failed, stop right here - if (!ok) - { - LOG_ERR(logger, "Failed to insert all modules!\n"); - leds->endlessBlink(LedColor::RED, LED_BLINK_FAST_PERIOD_MS); - } - else - { - LOG_DEBUG(logger, "All modules inserted successfully!\n"); - } - } - - // Starting Modules - bool init_fatal = false; - bool init_error = false; - -#ifndef NO_SD_LOGGING - if (!Logger::getInstance().start()) - { - LOG_ERR(logger, "ERROR: Failed to start Logger\n"); - init_error = true; - } -#endif - if (!scheduler_low->start()) - { - LOG_ERR(logger, "ERROR: Failed to start Scheduler Low\n"); - init_error = true; - } - if (!scheduler_high->start()) - { - LOG_ERR(logger, "FATAL: Failed to start Scheduler High\n"); - init_fatal = true; - } - if (!serial->start()) - { - LOG_ERR(logger, "ERROR: Failed to start Serial\n"); - init_error = true; - } - if (!leds->start()) - { - LOG_ERR(logger, "ERROR: Failed to start Leds\n"); - init_error = true; - } - if (!radio_main->start()) - { - LOG_ERR(logger, "FATAL: Failed to start Main Radio\n"); - init_fatal = true; - } - if (!ethernet->start()) - { - LOG_ERR(logger, "ERROR: Failed to start Ethernet\n"); - init_error = true; - } - if (!board_status->start()) - { - LOG_ERR(logger, "ERROR: Failed to start Board Status\n"); - init_error = true; - } - if (!sensors->start()) - { - LOG_ERR(logger, "ERROR: Failed to start Sensors\n"); - init_error = true; - } - if (!sma->start()) - { - LOG_ERR(logger, "FATAL: Failed to start SMA\n"); - init_fatal = true; - } - actuators->start(); - - LOG_INFO(logger, "Modules setup successful"); - - if (board_status->isMainRadioPresent()) - LOG_DEBUG(logger, "Main radio is present\n"); - - // If init fatal and sma not started, blink red endlessly - if (init_fatal) - { - leds->endlessBlink(LedColor::RED, LED_BLINK_FAST_PERIOD_MS); - } // If another module is in error - else if (init_error) - { - EventBroker::getInstance().post(ARP_INIT_ERROR, TOPIC_ARP); - } // If all modules are ok - else - { - LOG_INFO(logger, "Starting ARP"); - EventBroker::getInstance().post(ARP_INIT_OK, TOPIC_ARP); - } - - while (true) - Thread::wait(); - return 0; -} diff --git a/src/Groundstation/Automated/test-actuators.cpp b/src/Groundstation/Automated/test-actuators.cpp deleted file mode 100644 index a92629b1535cecff7f8819344f433e9e6247de02..0000000000000000000000000000000000000000 --- a/src/Groundstation/Automated/test-actuators.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* Copyright (c) 2023 Skyward Experimental Rocketry - * Authors: Niccolò Betto - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * 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 <Groundstation/Automated/Actuators/Actuators.h> -#include <Groundstation/Automated/Hub.h> -#include <Groundstation/Automated/Sensors/Sensors.h> -#include <Groundstation/Common/Ports/Serial.h> -#include <Groundstation/LyraGS/Buses.h> -#include <diagnostic/PrintLogger.h> -#include <drivers/interrupt/external_interrupts.h> -#include <miosix.h> -#include <scheduler/TaskScheduler.h> -#include <utils/ButtonHandler/ButtonHandler.h> - -#define STEPPER_SPEED 0.25 -#define TEST_WAIT 5000 -#define NO_SD_LOGGING - -#define START_MODULE(name, lambda) \ - do \ - { \ - std::function<bool()> _fun = lambda; \ - if (!_fun()) \ - { \ - LOG_ERR(logger, "Failed to start module " name); \ - errorLoop(); \ - } \ - else \ - { \ - LOG_DEBUG(logger, "Successfully started module " name); \ - } \ - } while (0) - -using namespace Groundstation; -using namespace Antennas; -using namespace Boardcore; -using namespace miosix; - -void ledWaitLoop(int ms) -{ - int waited = 0; - while (waited < ms) - { - led2On(); - Thread::sleep(100); - led2Off(); - Thread::sleep(100); - waited += 200; - } -} - -void errorLoop() -{ - while (1) - { - userLed4::high(); - Thread::sleep(100); - userLed4::low(); - Thread::sleep(100); - } -} - -void test1(Actuators* actuators) -{ - PrintLogger logger = PrintLogger{Logging::getLogger("test-actuators")}; - LOG_INFO(logger, "Executing Test 1"); - - // theta1 : HORIZONTAL - // theta2 : VERTICAL - LOG_DEBUG(logger, "Setting speed to speed\n"); - actuators->setSpeed(StepperList::STEPPER_X, STEPPER_SPEED); - actuators->setSpeed(StepperList::STEPPER_Y, STEPPER_SPEED); - - LOG_DEBUG(logger, "Moving 90deg horizontally\n"); - actuators->moveDeg(StepperList::STEPPER_X, 90); - ledWaitLoop(TEST_WAIT); - - LOG_DEBUG(logger, "Moving back to the initial horizontal position\n"); - actuators->moveDeg(StepperList::STEPPER_X, -90); - ledWaitLoop(TEST_WAIT); - - LOG_DEBUG(logger, "Moving -90deg horizontally\n"); - actuators->moveDeg(StepperList::STEPPER_X, -90); - ledWaitLoop(TEST_WAIT); - - LOG_DEBUG(logger, "Moving back to the initial horizontal position\n"); - actuators->moveDeg(StepperList::STEPPER_X, 90); - ledWaitLoop(TEST_WAIT); - - LOG_INFO(logger, "Test 1 completed\n"); -} - -void test2(Actuators* actuators) -{ - PrintLogger logger = PrintLogger{Logging::getLogger("test-actuators")}; - LOG_INFO(logger, "Executing Test 2"); - - actuators->setSpeed(StepperList::STEPPER_X, STEPPER_SPEED); - actuators->setSpeed(StepperList::STEPPER_Y, STEPPER_SPEED); - actuators->moveDeg(StepperList::STEPPER_Y, 22.5); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_X, 90); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_X, -90); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_X, -90); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_X, 90); - ledWaitLoop(TEST_WAIT); - - // Return to (0,0) - actuators->moveDeg(StepperList::STEPPER_Y, -22.5); - ledWaitLoop(TEST_WAIT); - - LOG_INFO(logger, "Test 2 completed\n"); -} - -void test3(Actuators* actuators) -{ - actuators->setSpeed(StepperList::STEPPER_X, STEPPER_SPEED); - actuators->setSpeed(StepperList::STEPPER_Y, STEPPER_SPEED); - - actuators->moveDeg(StepperList::STEPPER_X, 180); - ledWaitLoop(TEST_WAIT + 4000); - actuators->moveDeg(StepperList::STEPPER_X, -180); - ledWaitLoop(TEST_WAIT + 4000); - - actuators->moveDeg(StepperList::STEPPER_Y, 90); - ledWaitLoop(TEST_WAIT); - actuators->moveDeg(StepperList::STEPPER_Y, -90); - ledWaitLoop(TEST_WAIT); -} - -void test6(Actuators* actuators) -{ - PrintLogger logger = PrintLogger{Logging::getLogger("test-actuators")}; - LOG_INFO(logger, "Executing Test 6"); - - actuators->setSpeed(StepperList::STEPPER_X, STEPPER_SPEED); - actuators->setSpeed(StepperList::STEPPER_Y, STEPPER_SPEED); - - actuators->moveDeg(StepperList::STEPPER_Y, 22.5); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_Y, -22.5); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_Y, 45); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_Y, -45); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_Y, 65.5); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_Y, -65.5); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_Y, 90); - ledWaitLoop(TEST_WAIT); - - actuators->moveDeg(StepperList::STEPPER_Y, -90); - ledWaitLoop(TEST_WAIT); - - LOG_INFO(logger, "Test 6 completed\n"); -} - -int main() -{ - ledOff(); - - Hub* hub = new Hub(); - LyraGS::Buses* buses = new LyraGS::Buses(); - Serial* serial = new Serial(); - Actuators* actuators = new Actuators(); - Sensors* sensors = new Sensors(); - - DependencyManager manager; - PrintLogger logger = PrintLogger{Logging::getLogger("test-actuators")}; - bool ok = true; - - LOG_INFO(logger, "test-actuators\n"); - - // Insert modules - { - ok &= manager.insert<HubBase>(hub); - ok &= manager.insert(buses); - ok &= manager.insert(serial); - ok &= manager.insert(actuators); - ok &= manager.insert(sensors); - - // If insertion failed, stop right here - if (!ok) - { - LOG_ERR(logger, "Failed to insert all modules!\n"); - errorLoop(); - } - else - { - LOG_DEBUG(logger, "All modules inserted successfully!\n"); - } - - if (!manager.inject()) - { - LOG_ERR(logger, "[error] Failed to inject the dependencies!\n"); - errorLoop(); - } - else - { - LOG_DEBUG(logger, "All modules injected successfully!\n"); - } - } - - // Start modules - { -#ifndef NO_SD_LOGGING - START_MODULE("Logger", [&] { return Logger::getInstance().start(); }); -#endif - START_MODULE("Serial", [&] { return serial->start(); }); - START_MODULE("Sensors", [&] { return sensors->start(); }); - - actuators->start(); - LOG_DEBUG(logger, "Actuators started successfully!\n"); - } - - // Setup success LED - led1On(); - LOG_INFO(logger, "Modules setup successful"); - - LOG_INFO(logger, "Starting tests\n"); - - actuators->arm(); - - test1(actuators); - test2(actuators); - test3(actuators); - test6(actuators); - - LOG_INFO(logger, "Tests completed\n"); - - led1Off(); - while (1) - Thread::sleep(1000); - return 0; -} diff --git a/src/Groundstation/Automated/test-automated-radio.cpp b/src/Groundstation/Automated/test-automated-radio.cpp deleted file mode 100644 index c33b71cdde30e8d197e6e4ca81e12f2c697e5838..0000000000000000000000000000000000000000 --- a/src/Groundstation/Automated/test-automated-radio.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* Copyright (c) 2023 Skyward Experimental Rocketry - * Authors: Davide Mor, 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 <Groundstation/Automated/Buses.h> -#include <Groundstation/Automated/Hub.h> -#include <Groundstation/Automated/Radio/Radio.h> -#include <Groundstation/Common/Ports/Serial.h> -#include <Groundstation/LyraGS/BoardStatus.h> -#include <miosix.h> - -using namespace Groundstation; -using namespace Antennas; -using namespace Boardcore; -using namespace miosix; - -void spinLoop() -{ - while (1) - Thread::sleep(1000); -} - -void errorLoop() -{ - while (1) - { - led1On(); - Thread::sleep(100); - led1Off(); - Thread::sleep(100); - } -} - -int main() -{ - ledOff(); - - Hub* hub = new Hub(); - Buses* buses = new Buses(); - Serial* serial = new Serial(); - RadioMain* radio_main = new RadioMain(); - BoardStatus* board_status = new BoardStatus(); - - ModuleManager& modules = ModuleManager::getInstance(); - - bool ok = true; - - ok &= modules.insert<HubBase>(hub); - ok &= modules.insert(buses); - ok &= modules.insert(serial); - ok &= modules.insert(radio_main); - ok &= modules.insert(board_status); - - // If insertion failed, stop right here - if (!ok) - { - printf("[error] Failed to insert all modules!\n"); - errorLoop(); - } - - // Ok now start them - - ok &= serial->start(); - if (!ok) - printf("[error] Failed to start serial!\n"); - - ok &= radio_main->start(); - if (!ok) - printf("[error] Failed to start main radio!\n"); - - ok &= board_status->start(); - if (!ok) - printf("[error] Failed to start radio status!\n"); - - if (board_status->isMainRadioPresent()) - led2On(); - - if (!ok) - errorLoop(); - - led1On(); - spinLoop(); - return 0; -} diff --git a/src/Groundstation/Automated/test-smcontroller.cpp b/src/Groundstation/Automated/test-smcontroller.cpp deleted file mode 100644 index a77712da36b4483cfa9ae33f823ea4fa89d41a0d..0000000000000000000000000000000000000000 --- a/src/Groundstation/Automated/test-smcontroller.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* Copyright (c) 2024 Skyward Experimental Rocketry - * Author: Federico Lolli - * - * 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 <Groundstation/Automated/SMA/SMA.h> -#include <common/Events.h> -#include <common/Topics.h> -#include <events/EventBroker.h> -#include <utils/Debug.h> - -#include <memory> -#include <unordered_set> - -#define ARP_EVENTS \ - {ARP_INIT_OK, ARP_INIT_ERROR, ARP_CAL_DONE, ARP_FIX_ANTENNAS, \ - ARP_FIX_ROCKET} - -#define TMTC_EVENTS \ - {TMTC_ARP_FORCE_INIT, \ - TMTC_ARP_RESET_ALGORITHM, \ - TMTC_ARP_FORCE_NO_FEEDBACK, \ - TMTC_ARP_ARM, \ - TMTC_ARP_DISARM, \ - TMTC_ARP_CALIBRATE, \ - TMTC_ARP_ENTER_TEST_MODE, \ - TMTC_ARP_EXIT_TEST_MODE} - -#define TEST_STATE(INITIAL_STATE, EVENT, TOPIC_SM, FINAL_STATE) \ - sm->forceState(&SMA::INITIAL_STATE); \ - EventBroker::getInstance().post(EVENT, TOPIC_SM); \ - Thread::sleep(20); \ - TRACE("Testing %-26s in " #INITIAL_STATE " -> " #FINAL_STATE " ... ", \ - getEventString(EVENT).c_str()); \ - correct = sm->testState(&SMA::FINAL_STATE); \ - printf(correct ? "OK\n" : "FAIL\n"); \ - ok &= correct; - -#define TEST_NO_TRANSITION(INITIAL_STATE, EVENT, TOPIC_SM) \ - sm->forceState(&SMA::INITIAL_STATE); \ - EventBroker::getInstance().post(EVENT, TOPIC_SM); \ - Thread::sleep(20); \ - TRACE("Testing %-26s in " #INITIAL_STATE " -X ... ", \ - getEventString(EVENT).c_str()); \ - correct = sm->testState(&SMA::INITIAL_STATE); \ - printf(correct ? "OK\n" : "FAIL\n"); \ - ok &= correct; - -#define TEST_ALL_OTHER(INITIAL_STATE, ...) \ - to_exclude = {__VA_ARGS__}; \ - for (Events ev : arp_events) \ - { \ - if (to_exclude.find(ev) == to_exclude.end()) \ - { \ - TEST_NO_TRANSITION(INITIAL_STATE, ev, TOPIC_ARP); \ - } \ - } \ - for (Events ev : tmtc_events) \ - { \ - if (to_exclude.find(ev) == to_exclude.end()) \ - { \ - TEST_NO_TRANSITION(INITIAL_STATE, ev, TOPIC_TMTC); \ - } \ - } - -// for (auto state : {__VA_ARGS__}) -// { - -using namespace Boardcore; -using namespace Common; -using namespace Antennas; - -SMA* sm = new SMA(); - -int main() -{ - bool correct, ok = true; - std::unordered_set<Events> to_exclude; - std::vector<Events> arp_events = ARP_EVENTS; - std::vector<Events> tmtc_events = TMTC_EVENTS; - - sm->start(); - TRACE("SMA started\n"); - - // TEST STATE: INIT - TEST_STATE(state_init, ARP_INIT_OK, TOPIC_ARP, state_init_done); - TEST_STATE(state_init, ARP_INIT_ERROR, TOPIC_ARP, state_init_error); - TEST_ALL_OTHER(state_init, ARP_INIT_OK, ARP_INIT_ERROR); - - // TEST STATE: INIT_DONE - TEST_STATE(state_init_done, TMTC_ARP_ARM, TOPIC_TMTC, state_armed); - TEST_STATE(state_init_done, TMTC_ARP_FORCE_NO_FEEDBACK, TOPIC_TMTC, - state_insert_info); - TEST_ALL_OTHER(state_init_done, TMTC_ARP_ARM, TMTC_ARP_FORCE_NO_FEEDBACK); - - // TEST STATE: INIT_ERROR - TEST_STATE(state_init_error, TMTC_ARP_FORCE_INIT, TOPIC_TMTC, - state_init_done); - TEST_STATE(state_init_error, TMTC_ARP_FORCE_NO_FEEDBACK, TOPIC_TMTC, - state_insert_info); - TEST_ALL_OTHER(state_init_error, TMTC_ARP_FORCE_INIT, - TMTC_ARP_FORCE_NO_FEEDBACK); - - // TEST STATE: INSERT_INFO - TEST_STATE(state_insert_info, ARP_FIX_ANTENNAS, TOPIC_ARP, state_arm_ready); - TEST_ALL_OTHER(state_insert_info, ARP_FIX_ANTENNAS); - - // TEST STATE: ARM READY - TEST_STATE(state_arm_ready, TMTC_ARP_ARM, TOPIC_TMTC, state_armed_nf); - TEST_ALL_OTHER(state_arm_ready, TMTC_ARP_ARM); - - // TEST STATE: ARMED - TEST_STATE(state_armed, TMTC_ARP_DISARM, TOPIC_TMTC, state_init_done); - TEST_STATE(state_armed, TMTC_ARP_CALIBRATE, TOPIC_TMTC, state_calibrate); - TEST_STATE(state_armed, TMTC_ARP_ENTER_TEST_MODE, TOPIC_TMTC, state_test); - TEST_ALL_OTHER(state_armed, TMTC_ARP_DISARM, TMTC_ARP_CALIBRATE, - TMTC_ARP_ENTER_TEST_MODE); - - // TEST STATE: TEST - TEST_STATE(state_test, TMTC_ARP_EXIT_TEST_MODE, TOPIC_TMTC, state_armed); - TEST_STATE(state_test, TMTC_ARP_DISARM, TOPIC_TMTC, state_init_done); - TEST_ALL_OTHER(state_test, TMTC_ARP_EXIT_TEST_MODE, TMTC_ARP_DISARM); - - // TEST STATE: CALIBRATE - TEST_STATE(state_calibrate, ARP_CAL_DONE, TOPIC_ARP, state_fix_antennas); - TEST_STATE(state_calibrate, TMTC_ARP_DISARM, TOPIC_TMTC, state_init_done); - TEST_STATE(state_calibrate, TMTC_ARP_RESET_ALGORITHM, TOPIC_TMTC, - state_armed); - TEST_ALL_OTHER(state_calibrate, ARP_CAL_DONE, TMTC_ARP_DISARM, - TMTC_ARP_RESET_ALGORITHM); - - // TEST STATE: FIX_ANTENNAS - TEST_STATE(state_fix_antennas, ARP_FIX_ANTENNAS, TOPIC_ARP, - state_fix_rocket); - TEST_STATE(state_fix_antennas, TMTC_ARP_DISARM, TOPIC_TMTC, - state_init_done); - TEST_STATE(state_fix_antennas, TMTC_ARP_RESET_ALGORITHM, TOPIC_TMTC, - state_armed); - TEST_ALL_OTHER(state_fix_antennas, ARP_FIX_ANTENNAS, TMTC_ARP_DISARM, - TMTC_ARP_RESET_ALGORITHM); - - // TEST STATE: FIX_ROCKET - TEST_STATE(state_fix_rocket, ARP_FIX_ROCKET, TOPIC_ARP, state_active); - TEST_STATE(state_fix_rocket, TMTC_ARP_DISARM, TOPIC_TMTC, state_init_done); - TEST_STATE(state_fix_rocket, TMTC_ARP_RESET_ALGORITHM, TOPIC_TMTC, - state_armed); - TEST_ALL_OTHER(state_fix_rocket, ARP_FIX_ROCKET, TMTC_ARP_DISARM, - TMTC_ARP_RESET_ALGORITHM); - - // TEST STATE: ACTIVE - TEST_STATE(state_active, TMTC_ARP_DISARM, TOPIC_TMTC, state_init_done); - TEST_STATE(state_active, TMTC_ARP_RESET_ALGORITHM, TOPIC_TMTC, state_armed); - TEST_ALL_OTHER(state_active, TMTC_ARP_DISARM, TMTC_ARP_RESET_ALGORITHM); - - // TEST STATE: ARMED_NO_FEEDBACK - TEST_STATE(state_armed_nf, TMTC_ARP_DISARM, TOPIC_TMTC, state_arm_ready); - TEST_STATE(state_armed_nf, TMTC_ARP_CALIBRATE, TOPIC_TMTC, - state_fix_rocket_nf); - TEST_STATE(state_armed_nf, TMTC_ARP_ENTER_TEST_MODE, TOPIC_TMTC, - state_test_nf); - TEST_ALL_OTHER(state_armed_nf, TMTC_ARP_DISARM, TMTC_ARP_CALIBRATE, - TMTC_ARP_ENTER_TEST_MODE); - - // TEST STATE: TEST_NO_FEEDBACK - TEST_STATE(state_test_nf, TMTC_ARP_EXIT_TEST_MODE, TOPIC_TMTC, - state_armed_nf); - TEST_STATE(state_test_nf, TMTC_ARP_DISARM, TOPIC_TMTC, state_arm_ready); - TEST_ALL_OTHER(state_test_nf, TMTC_ARP_EXIT_TEST_MODE, TMTC_ARP_DISARM); - - // TEST STATE: FIX_ROCKET_NO_FEEDBACK - TEST_STATE(state_fix_rocket_nf, ARP_FIX_ROCKET, TOPIC_ARP, state_active_nf); - TEST_STATE(state_fix_rocket_nf, TMTC_ARP_DISARM, TOPIC_TMTC, - state_arm_ready); - TEST_STATE(state_fix_rocket_nf, TMTC_ARP_RESET_ALGORITHM, TOPIC_TMTC, - state_armed_nf); - TEST_ALL_OTHER(state_fix_rocket_nf, ARP_FIX_ROCKET, TMTC_ARP_DISARM, - TMTC_ARP_RESET_ALGORITHM); - - // TEST STATE: ACTIVE_NO_FEEDBACK - TEST_STATE(state_active_nf, TMTC_ARP_DISARM, TOPIC_TMTC, state_arm_ready); - TEST_STATE(state_active_nf, TMTC_ARP_RESET_ALGORITHM, TOPIC_TMTC, - state_armed_nf); - TEST_ALL_OTHER(state_active_nf, TMTC_ARP_DISARM, TMTC_ARP_RESET_ALGORITHM); - - TRACE("Testing SMA ... "); - ok ? printf("OK\n") : printf("FAIL\n"); - return 0; -} diff --git a/src/Groundstation/Automated/test-steps.cpp b/src/Groundstation/Automated/test-steps.cpp deleted file mode 100644 index 7a69211c5b49a6e90d374849c7ae77f73840eb8e..0000000000000000000000000000000000000000 --- a/src/Groundstation/Automated/test-steps.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* 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 <Groundstation/Automated/Actuators/Actuators.h> -#include <Groundstation/Automated/Buses.h> -#include <Groundstation/Automated/Sensors/Sensors.h> -#include <diagnostic/CpuMeter/CpuMeter.h> -#include <diagnostic/PrintLogger.h> -#include <diagnostic/StackLogger.h> -#include <drivers/interrupt/external_interrupts.h> -#include <drivers/timer/TimestampTimer.h> -#include <events/EventBroker.h> -#include <miosix.h> -#include <scheduler/TaskScheduler.h> -#include <utils/ButtonHandler/ButtonHandler.h> - -#include <iostream> -#include <utils/ModuleManager/ModuleManager.hpp> - -#include "actuators/stepper/StepperPWM.h" - -using namespace miosix; -using namespace Boardcore; -using namespace Antennas; - -/** - * Test used to characterize the automated antennas. - */ - -GpioPin button = GpioPin(GPIOC_BASE, 13); - -void __attribute__((used)) EXTI13_IRQHandlerImpl() -{ - ModuleManager::getInstance().get<Actuators>()->IRQemergencyStop(); -} - -int main() -{ - bool initResult = true; - PrintLogger logger = Logging::getLogger("automated_antennas"); - ModuleManager& modules = ModuleManager::getInstance(); - TaskScheduler* scheduler = new TaskScheduler(); - - button.mode(Mode::INPUT); - enableExternalInterrupt(button.getPort(), button.getNumber(), - InterruptTrigger::RISING_EDGE); - - // INSERTING MODULES - { - if (!modules.insert<Buses>(new Buses())) - { - initResult = false; - LOG_ERR(logger, "Error inserting the Buses module"); - } - else - { - LOG_INFO(logger, "Successfully inserted Buses module\n"); - } - - if (!modules.insert<Actuators>(new Actuators())) - { - initResult = false; - LOG_ERR(logger, "Error inserting the Actuators module"); - } - else - { - LOG_INFO(logger, "Successfully inserted Actuators module\n"); - } - - if (!modules.insert<Sensors>(new Sensors())) - { - initResult = false; - LOG_ERR(logger, "Error inserting the Sensors module"); - } - else - { - LOG_INFO(logger, "Successfully inserted Sensors module\n"); - } - - // Insert algorithm modules - } - - // ADDING TASKS - { - scheduler->addTask( - []() - { - Logger::getInstance().log(CpuMeter::getCpuStats()); - CpuMeter::resetCpuStats(); - Logger::getInstance().logStats(); - StackLogger::getInstance().log(); - }, - 100); - } - - // STARTING MODULES - { -#ifndef NO_SD_LOGGING - // Starting the Logger - if (!Logger::getInstance().start()) - { - initResult = false; - LOG_ERR(logger, "Error initializing the Logger\n"); - } - else - { - LOG_INFO(logger, "Logger started successfully\n"); - } -#endif - - // Starting scheduler - if (!scheduler->start()) - { - initResult = false; - LOG_ERR(logger, "Error initializing the Scheduler\n"); - } - else - { - LOG_INFO(logger, "Scheduler started successfully\n"); - } - - // Starting the Actuators - modules.get<Actuators>()->start(); - - // Starting the Sensors - if (!modules.get<Sensors>()->start()) - { - initResult = false; - LOG_ERR(logger, "Error initializing the Sensors\n"); - } - else - { - LOG_INFO(logger, "Sensors started successfully\n"); - } - } - - if (!initResult) - { - printf("Errors present during module insertion\n"); - return -1; - } - - // Periodically statistics - float speed0 = 0.5; - float speed = speed0; - - modules.get<Actuators>()->setSpeed(StepperList::STEPPER_X, speed); - - modules.get<Actuators>()->setSpeed(StepperList::STEPPER_Y, speed); - - // bool increasing = true; - // float speedMax = 1.0; - // float stepSpeed = 0.1; - // scheduler->addTask( - // [&]() - // { - // if (increasing) - // { - // if (speed < speedMax) - // { - // speed += stepSpeed; - // modules.get<Actuators>()->setSpeed( - // StepperList::STEPPER_X, speed); - // } - // else - // { - // increasing = false; - // } - // } - // else - // { - // if (speed > 0) - // { - // speed -= stepSpeed; - // modules.get<Actuators>()->setSpeed( - // StepperList::STEPPER_X, speed); - // } - // else - // { - // increasing = true; - // } - // } - // }, - // 100); - VN300Data data; - while (!ModuleManager::getInstance().get<Actuators>()->isEmergencyStopped()) - { - { - data = modules.get<Sensors>()->getVN300LastSample(); - printf("acc[%.3f,%.3f,%.3f] ypr[%.3f,%.3f,%.3f]\n", - data.accelerationX, data.accelerationY, data.accelerationZ, - data.yaw, data.pitch, data.roll); - modules.get<Actuators>()->moveDeg(StepperList::STEPPER_X, 90); - modules.get<Actuators>()->moveDeg(StepperList::STEPPER_Y, 90); - Thread::sleep(1500); - } - - { - data = modules.get<Sensors>()->getVN300LastSample(); - printf("acc[%.3f,%.3f,%.3f] ypr[%.3f,%.3f,%.3f]\n", - data.accelerationX, data.accelerationY, data.accelerationZ, - data.yaw, data.pitch, data.roll); - modules.get<Actuators>()->moveDeg(StepperList::STEPPER_X, -90); - modules.get<Actuators>()->moveDeg(StepperList::STEPPER_Y, -90); - Thread::sleep(1500); - } - } - - // Stopping threads - { - Logger::getInstance().stop(); - printf("Logger stopped! Board can be reset/shutdown9\n"); - } - - while (1) - ; -} diff --git a/src/Groundstation/Common/Ports/EthernetBase.cpp b/src/Groundstation/Common/Ports/EthernetBase.cpp index f183a9a64b728425db6bc3a3c2982159b5e80874..76e66d89a1a3e7ce7d47cd129a48152a8e690fc2 100644 --- a/src/Groundstation/Common/Ports/EthernetBase.cpp +++ b/src/Groundstation/Common/Ports/EthernetBase.cpp @@ -1,5 +1,5 @@ -/* Copyright (c) 2023 Skyward Experimental Rocketry - * Author: Davide Mor +/* Copyright (c) 2023-2024 Skyward Experimental Rocketry + * Authors: Davide Mor, Nicolò Caruso * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -65,9 +65,9 @@ Boardcore::Wiz5500::PhyState EthernetBase::getState() return wiz5500->getPhyState(); } -bool EthernetBase::start(std::unique_ptr<Boardcore::Wiz5500> wiz5500) +bool EthernetBase::start(std::shared_ptr<Boardcore::Wiz5500> wiz5500) { - this->wiz5500 = std::move(wiz5500); + this->wiz5500 = wiz5500; // Reset the device this->wiz5500->reset(); @@ -82,7 +82,21 @@ bool EthernetBase::start(std::unique_ptr<Boardcore::Wiz5500> wiz5500) WizIp ip = IP_BASE; ip.d = 1 + ipOffset; // Add to the ip the offset set on the dipswitch this->wiz5500->setSourceIp(ip); - this->wiz5500->setSourceMac(genNewRandomMac()); + WizMac mac = MAC_BASE; + // Add to the mac address the offset set on the dipswitch + mac.c += 1 + ipOffset; + // In case of sniffing change ulteriorly the MAC to avoid switch to + // filter based on not whole MAC... + if (sniffOtherGs) + { + mac.a += 1; + mac.b += 1; + mac.c += 1; + mac.d += 1; + mac.e += 1; + mac.f += 1; + } + this->wiz5500->setSourceMac(mac); } else { @@ -108,6 +122,15 @@ bool EthernetBase::start(std::unique_ptr<Boardcore::Wiz5500> wiz5500) if (!mav_driver->start()) return false; + // In case of sniffing mode initialize and start the EthernetSniffer + if (sniffOtherGs) + { + getModule<EthernetSniffer>()->init(1, SEND_PORT, RECV_PORT); + if (!getModule<EthernetSniffer>()->start(this->wiz5500)) + return false; + } + + TRACE("[info] Ethernet module started correctly\n"); return true; } @@ -126,5 +149,6 @@ ssize_t EthernetBase::receive(uint8_t* pkt, size_t max_len) bool EthernetBase::send(uint8_t* pkt, size_t len) { - return wiz5500->send(0, pkt, len, 100); + return wiz5500->send(0, pkt, len, 1000); + // return true; } diff --git a/src/Groundstation/Common/Ports/EthernetBase.h b/src/Groundstation/Common/Ports/EthernetBase.h index 5f997f6caed74ecbfaf0dc24cc7353dcd116cd52..57e6622e50e7be804694ae030ecf4afc25df5104 100644 --- a/src/Groundstation/Common/Ports/EthernetBase.h +++ b/src/Groundstation/Common/Ports/EthernetBase.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2023 Skyward Experimental Rocketry - * Author: Davide Mor +/* Copyright (c) 2023-2024 Skyward Experimental Rocketry + * Authors: Davide Mor, Nicolò Caruso * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,7 @@ #include <ActiveObject.h> #include <Groundstation/Common/HubBase.h> +#include <Groundstation/Common/Ports/EthernetSniffer.h> #include <common/MavlinkLyra.h> #include <drivers/WIZ5500/WIZ5500.h> #include <radio/MavlinkDriver/MavlinkDriver.h> @@ -34,19 +35,23 @@ namespace Groundstation { +// Timeout for the port receive +static constexpr uint16_t RECEIVE_PORT_TIMEOUT_MS = 500; + Boardcore::WizIp genNewRandomIp(); Boardcore::WizMac genNewRandomMac(); using EthernetMavDriver = Boardcore::MavlinkDriver<1024, 10, MAVLINK_MAX_DIALECT_PAYLOAD_SIZE>; -class EthernetBase : public Boardcore::Transceiver, - public Boardcore::InjectableWithDeps<HubBase> +class EthernetBase + : public Boardcore::Transceiver, + public Boardcore::InjectableWithDeps<HubBase, EthernetSniffer> { public: EthernetBase() {}; - EthernetBase(bool randomIp, uint8_t ipOffset) - : randomIp{randomIp}, ipOffset{ipOffset} {}; + EthernetBase(bool randomIp, uint8_t ipOffset, bool sniffing) + : randomIp{randomIp}, ipOffset{ipOffset}, sniffOtherGs{sniffing} {}; void handleINTn(); @@ -55,7 +60,8 @@ public: Boardcore::Wiz5500::PhyState getState(); protected: - bool start(std::unique_ptr<Boardcore::Wiz5500> wiz5500); + bool start(std::shared_ptr<Boardcore::Wiz5500> wiz5500); + std::shared_ptr<Boardcore::Wiz5500> wiz5500; private: /** @@ -68,10 +74,11 @@ private: bool send(uint8_t* pkt, size_t len) override; bool started = false; - std::unique_ptr<Boardcore::Wiz5500> wiz5500; std::unique_ptr<EthernetMavDriver> mav_driver; - bool randomIp = true; - uint8_t ipOffset = 0; + bool randomIp = true; + uint8_t ipOffset = 0; + bool sniffOtherGs = false; + bool firstPort = true; }; } // namespace Groundstation diff --git a/src/Groundstation/Common/Ports/EthernetSniffer.cpp b/src/Groundstation/Common/Ports/EthernetSniffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e08e1c538c3f842536cf6097bde48d7572b11d7e --- /dev/null +++ b/src/Groundstation/Common/Ports/EthernetSniffer.cpp @@ -0,0 +1,95 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Nicolò Caruso + * + * 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 <Groundstation/Common/Config/EthernetConfig.h> +#include <Groundstation/Common/HubBase.h> + +#include <random> + +#include "EthernetBase.h" + +using namespace Groundstation; +using namespace Boardcore; +using namespace miosix; + +void EthernetSniffer::handleINTn() +{ + if (wiz5500) + wiz5500->handleINTn(); +} + +bool EthernetSniffer::send(uint8_t* pkt, size_t len) +{ + // Send is not needed in sniffing, therefore not implemented + return false; +}; + +Boardcore::Wiz5500::PhyState EthernetSniffer::getState() +{ + return wiz5500->getPhyState(); +} + +void EthernetSniffer::init(uint16_t portNumber, uint16_t srcPort, + uint16_t dstPort) +{ + portNr = portNumber; + this->srcPort = srcPort; + this->dstPort = dstPort; +} + +bool EthernetSniffer::start(std::shared_ptr<Boardcore::Wiz5500> wiz5500) +{ + this->wiz5500 = wiz5500; + + TRACE("[info] Opening sniffing UDP socket\n"); + // We open the UDP socket for sniffing + if (!this->wiz5500->openUdp(portNr, srcPort, {255, 255, 255, 255}, dstPort, + 500)) + { + return false; + } + + auto mav_handler = [this](EthernetMavDriver* channel, + const mavlink_message_t& msg) { handleMsg(msg); }; + + mav_driver = std::make_unique<EthernetMavDriver>(this, mav_handler, 0, 10); + + if (!mav_driver->start()) + return false; + + TRACE("[info] EthernetSniffer start ok\n"); + + return true; +} + +void EthernetSniffer::handleMsg(const mavlink_message_t& msg) +{ + // Dispatch the message through the hub. + getModule<HubBase>()->dispatchOutgoingMsg(msg); +} + +ssize_t EthernetSniffer::receive(uint8_t* pkt, size_t max_len) +{ + WizIp dst_ip; + uint16_t dst_port; + return wiz5500->recvfrom(portNr, pkt, max_len, dst_ip, dst_port); +} diff --git a/src/Groundstation/Common/Ports/EthernetSniffer.h b/src/Groundstation/Common/Ports/EthernetSniffer.h new file mode 100644 index 0000000000000000000000000000000000000000..7f4ea7a74340f2bd367d761b11c254f6117ca1e0 --- /dev/null +++ b/src/Groundstation/Common/Ports/EthernetSniffer.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Nicolò Caruso + * + * 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 <Groundstation/Common/HubBase.h> +#include <common/MavlinkLyra.h> +#include <drivers/WIZ5500/WIZ5500.h> +#include <radio/MavlinkDriver/MavlinkDriver.h> +#include <utils/DependencyManager/DependencyManager.h> + +#include <memory> + +namespace Groundstation +{ + +using EthernetMavDriver = + Boardcore::MavlinkDriver<1024, 10, MAVLINK_MAX_DIALECT_PAYLOAD_SIZE>; + +class EthernetSniffer : public Boardcore::Transceiver, + public Boardcore::InjectableWithDeps<HubBase> +{ +public: + void handleINTn(); + + Boardcore::Wiz5500::PhyState getState(); + + bool start(std::shared_ptr<Boardcore::Wiz5500> wiz5500); + + void init(uint16_t portNumber, uint16_t srcPort, uint16_t dstPort); + +private: + std::shared_ptr<Boardcore::Wiz5500> wiz5500; + + /** + * @brief Called internally when a message is received. + */ + void handleMsg(const mavlink_message_t& msg); + + ssize_t receive(uint8_t* pkt, size_t max_len) override; + + bool send(uint8_t* pkt, size_t len) override; + + bool started = false; + std::unique_ptr<EthernetMavDriver> mav_driver; + uint16_t portNr; + uint16_t srcPort; + uint16_t dstPort; +}; + +} // namespace Groundstation diff --git a/src/Groundstation/LyraGS/BoardStatus.cpp b/src/Groundstation/LyraGS/BoardStatus.cpp index 0b7f6bb91c95ca19ae1e31f94340a0b79ffbfee5..b65ac904bdbabe308b7975ad6bf181dac7180195 100644 --- a/src/Groundstation/LyraGS/BoardStatus.cpp +++ b/src/Groundstation/LyraGS/BoardStatus.cpp @@ -108,6 +108,11 @@ void BoardStatus::arpRoutine() tm.main_rx_rssi = stats.rx_rssi; last_main_stats = stats; + + Logger::getInstance().log(MainRadioLog{ + tm.timestamp, tm.main_packet_tx_error_count, tm.main_tx_bitrate, + tm.main_packet_rx_success_count, tm.main_packet_rx_drop_count, + tm.main_rx_bitrate, tm.main_rx_rssi}); } if (ethernet_present) diff --git a/src/Groundstation/LyraGS/BoardStatus.h b/src/Groundstation/LyraGS/BoardStatus.h index 3eb286799a41a297744808a0244e435ecfa7f4c6..4d789f71a6896bb22002d07071417d4f08f5a805 100644 --- a/src/Groundstation/LyraGS/BoardStatus.h +++ b/src/Groundstation/LyraGS/BoardStatus.h @@ -32,6 +32,7 @@ #include <Groundstation/Common/Radio/RadioBase.h> #include <Groundstation/LyraGS/Ports/Ethernet.h> #include <Groundstation/LyraGS/Radio/Radio.h> +#include <Groundstation/LyraGS/Radio/RadioData.h> #include <common/MavlinkLyra.h> #include <drivers/timer/TimestampTimer.h> #include <utils/DependencyManager/DependencyManager.h> diff --git a/src/Groundstation/LyraGS/Ports/Ethernet.cpp b/src/Groundstation/LyraGS/Ports/Ethernet.cpp index 6b3e3270fc12fa4186473c77f74e6cf6f1c58d41..6013c835a97d4865c89e5e161228ab1a9ab59198 100644 --- a/src/Groundstation/LyraGS/Ports/Ethernet.cpp +++ b/src/Groundstation/LyraGS/Ports/Ethernet.cpp @@ -45,7 +45,7 @@ namespace LyraGS bool EthernetGS::start() { - std::unique_ptr<Wiz5500> wiz5500 = std::make_unique<Wiz5500>( + std::shared_ptr<Wiz5500> wiz5500 = std::make_shared<Wiz5500>( getModule<Buses>()->ethernet_bus, miosix::ethernet::cs::getPin(), miosix::ethernet::intr::getPin(), SPI::ClockDivider::DIV_64); @@ -55,7 +55,7 @@ bool EthernetGS::start() if (!present) return false; - if (!EthernetBase::start(std::move(wiz5500))) + if (!EthernetBase::start(wiz5500)) return false; ethernetGSGlobal = this; diff --git a/src/Groundstation/LyraGS/Ports/Ethernet.h b/src/Groundstation/LyraGS/Ports/Ethernet.h index a267a7abe1cef7bc863076659560f706ab3e3167..4899208287603a70beb44f845202a6c594a01523 100644 --- a/src/Groundstation/LyraGS/Ports/Ethernet.h +++ b/src/Groundstation/LyraGS/Ports/Ethernet.h @@ -36,7 +36,10 @@ class EthernetGS : public Boardcore::InjectableWithDeps< { public: EthernetGS() : Super{} {} - EthernetGS(bool randomIp, uint8_t ipOffset) : Super{randomIp, ipOffset} {} + EthernetGS(bool randomIp, uint8_t ipOffset, bool sniffing) + : Super{randomIp, ipOffset, sniffing} + { + } [[nodiscard]] bool start(); void sendMsg(const mavlink_message_t& msg); void handleINTn(); diff --git a/src/Groundstation/LyraGS/Radio/RadioData.h b/src/Groundstation/LyraGS/Radio/RadioData.h new file mode 100644 index 0000000000000000000000000000000000000000..5edb3ce4b88e6c0e72b235222a9d0aec2e1bfd47 --- /dev/null +++ b/src/Groundstation/LyraGS/Radio/RadioData.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2024 Skyward Experimental Rocketry + * Author: Nicolò Caruso + * + * 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 <stdint.h> + +#include <iostream> +#include <string> + +/** + * @brief Logging struct for the main radio informations + * + */ + +namespace LyraGS +{ +struct MainRadioLog +{ + uint64_t timestamp = 0; + uint16_t main_packet_tx_error_count = 0; + uint32_t main_tx_bitrate = 0; + uint16_t main_packet_rx_success_count = 0; + uint16_t main_packet_rx_drop_count = 0; + uint32_t main_rx_bitrate = 0; + float main_rx_rssi = 0; + + static std::string header() + { + return "timestamp,main_packet_tx_error_count,main_tx_bitrate,main_" + "packet_rx_success_count,main_packet_rx_drop_count,main_rx_" + "bitrate,main_rx_rssi\n"; + } + + void print(std::ostream& os) const + { + os << timestamp << "," << main_packet_tx_error_count << "," + << main_tx_bitrate << "," << main_tx_bitrate << "," + << main_packet_rx_success_count << "," << main_packet_rx_drop_count + << "," << main_rx_bitrate << "," << main_rx_rssi << "\n"; + } +}; +} // namespace LyraGS diff --git a/src/Groundstation/LyraGS/lyra-gs-entry.cpp b/src/Groundstation/LyraGS/lyra-gs-entry.cpp index 869328463a5abc32c44df6001423c7d8c5337773..959829b5abefca09b73ea510ec24fce021c7cd6b 100644 --- a/src/Groundstation/LyraGS/lyra-gs-entry.cpp +++ b/src/Groundstation/LyraGS/lyra-gs-entry.cpp @@ -61,6 +61,17 @@ struct DipStatusLyraGS uint8_t ipConfig; }; +/** + * Dipswitch configuration + * arp mb pb mtx ptx ip3 ip2 ip1 + * | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + * --------------------------------- + * | I | I | I | I | I | I | I | I | + * | O | O | O | O | O | O | O | O | + * --------------------------------- + * | H | G | F | E | D | C | B | A | + */ + DipStatusLyraGS getDipStatus(uint8_t read) { DipStatusLyraGS dipRead; @@ -93,6 +104,8 @@ void errorLoop() } } +static bool constexpr ethernetSniffing = true; + /** * @brief Lyra GS entrypoint. * This entrypoint performs the following operations: @@ -129,6 +142,14 @@ int main() DipSwitch dip(sh, clk, qh, std::chrono::microseconds(microSecClk)); DipStatusLyraGS dipRead = getDipStatus(dip.read()); + std::cout << "Dipswitch state:" + << "\n\t Is ARP: " << dipRead.isARP + << "\n\t Main radio backup: " << dipRead.mainHasBackup + << "\n\t Payload radio backup: " << dipRead.payloadHasBackup + << "\n\t Main TX: " << dipRead.mainTXenable + << "\n\t Main TX: " << dipRead.payloadTXenable + << "\n\t Ip offset: " << (int)dipRead.ipConfig << "\n"; + DependencyManager manager; PrintLogger logger = Logging::getLogger("lyra_gs"); @@ -140,8 +161,9 @@ int main() LyraGS::RadioMain* radio_main = new LyraGS::RadioMain(dipRead.mainHasBackup, dipRead.mainTXenable); LyraGS::BoardStatus* board_status = new LyraGS::BoardStatus(dipRead.isARP); - LyraGS::EthernetGS* ethernet = - new LyraGS::EthernetGS(false, dipRead.ipConfig); + LyraGS::EthernetGS* ethernet = new LyraGS::EthernetGS( + false, dipRead.ipConfig, dipRead.isARP & ethernetSniffing); + EthernetSniffer* ethernetSniffer = new EthernetSniffer(); LyraGS::RadioPayload* radio_payload = new LyraGS::RadioPayload( dipRead.payloadHasBackup, dipRead.payloadTXenable); @@ -160,6 +182,7 @@ int main() ok &= manager.insert(serial); ok &= manager.insert<LyraGS::RadioMain>(radio_main); ok &= manager.insert<LyraGS::EthernetGS>(ethernet); + ok &= manager.insert<EthernetSniffer>(ethernetSniffer); ok &= manager.insert<LyraGS::RadioPayload>(radio_payload); ok &= manager.insert(board_status); @@ -343,7 +366,7 @@ int main() // Check presence of radio and ethernet - if (board_status->isMainRadioPresent() && !dipRead.isARP) + if (board_status->isMainRadioPresent()) { LOG_INFO(logger, "Main radio detected!\n"); led1On(); //< GREEN led on (CU) @@ -351,7 +374,7 @@ int main() else std::cout << "Main NOT detected" << std::endl; - if (board_status->isPayloadRadioPresent() && !dipRead.isARP) + if (board_status->isPayloadRadioPresent()) { LOG_INFO(logger, "Payload radio detected!\n"); led2On(); //< YELLOW led on (CU) @@ -359,7 +382,7 @@ int main() else std::cout << "Payload NOT detected" << std::endl; - if (board_status->isEthernetPresent() && !dipRead.isARP) + if (board_status->isEthernetPresent()) { LOG_INFO(logger, "Ethernet detected!\n"); led4On(); //< ORANGE led on (CU)