diff --git a/app/backplane/radio_module/include/c_downlink_scheduler_tenant.h b/app/backplane/radio_module/include/c_downlink_scheduler_tenant.h index 26ffbf3d4..02db0ebce 100644 --- a/app/backplane/radio_module/include/c_downlink_scheduler_tenant.h +++ b/app/backplane/radio_module/include/c_downlink_scheduler_tenant.h @@ -51,6 +51,10 @@ class CDownlinkSchedulerTenant : public CRunnableTenant, public CPadFlightLanded void GroundRun() override; private: + // Send a telemetry frame, piggybacking the flight phase onto GNSS frames + // (port 12005) so the ground can observe the radio's state. + void SendFrame(LaunchLoraFrame& frame); + CMessagePort& loraDownlinkMessagePort; CHashMap*> telemetryMessagePortMap; CHashMap> telemetryDownlinkTimers; diff --git a/app/backplane/radio_module/src/c_downlink_scheduler_tenant.cpp b/app/backplane/radio_module/src/c_downlink_scheduler_tenant.cpp index 0e95b52d1..99cdd148e 100644 --- a/app/backplane/radio_module/src/c_downlink_scheduler_tenant.cpp +++ b/app/backplane/radio_module/src/c_downlink_scheduler_tenant.cpp @@ -23,6 +23,18 @@ CDownlinkSchedulerTenant::CDownlinkSchedulerTenant( }(telemetryDownlinkTimers.GetPtr(NNetworkDefs::RADIO_MODULE_GNSS_DATA_PORT))); } +void CDownlinkSchedulerTenant::SendFrame(LaunchLoraFrame& frame) { + // Piggyback the flight phase onto GNSS frames. Reflects the radio's belief + // of the phase, not independent confirmation of physical events. + if (frame.Port == NNetworkDefs::RADIO_MODULE_GNSS_DATA_PORT && frame.Size < sizeof(frame.Payload)) { + frame.Payload[frame.Size++] = static_cast(state); + } + + if (loraDownlinkMessagePort.Send(frame, K_NO_WAIT) < 0) { + LOG_ERR("Failed to send telemetry frame on port %d", frame.Port); + } +} + void CDownlinkSchedulerTenant::HandleFrame(const ReceivedLaunchLoraFrame& rxFrame) { const LaunchLoraFrame& frame = rxFrame.Frame; if (this->state == State::PAD || this->state == State::LANDED) { @@ -43,10 +55,7 @@ void CDownlinkSchedulerTenant::HandleFrame(const ReceivedLaunchLoraFrame& rxFram LOG_ERR("Failed to receive telemetry frame on port %d", port); } - ret = loraDownlinkMessagePort.Send(telemFrame, K_NO_WAIT); - if (ret < 0) { - LOG_ERR("Failed to send telemetry frame on port %d", port); - } + SendFrame(telemFrame); } } } @@ -83,10 +92,7 @@ void CDownlinkSchedulerTenant::FlightRun() { continue; } - ret = loraDownlinkMessagePort.Send(telemFrame, K_NO_WAIT); - if (ret < 0) { - LOG_ERR("Failed to send telemetry frame on port %d", port); - } + SendFrame(telemFrame); } } } @@ -107,10 +113,7 @@ void CDownlinkSchedulerTenant::LandedRun() { return; } - ret = loraDownlinkMessagePort.Send(telemFrame, K_NO_WAIT); - if (ret < 0) { - LOG_ERR("Failed to send GNSS telemetry frame"); - } + SendFrame(telemFrame); } } diff --git a/app/backplane/sensor_module/include/c_detection_handler.h b/app/backplane/sensor_module/include/c_detection_handler.h index ecd50157a..16ffdb250 100644 --- a/app/backplane/sensor_module/include/c_detection_handler.h +++ b/app/backplane/sensor_module/include/c_detection_handler.h @@ -45,6 +45,14 @@ class CDetectionHandler { static constexpr uint64_t BOOST_NOT_YET_HAPPENED = ~0; uint64_t boost_detected_time = BOOST_NOT_YET_HAPPENED; + static constexpr uint32_t alertResendCycles = 20; + uint32_t boostAlertResendsRemaining = 0; + uint32_t landedAlertResendsRemaining = alertResendCycles; + + // Rebroadcast the landed alert once GroundHit occurs. Covers both the + // barometer and timer paths, and survives UDP loss like the boost alert. + void ServiceLandedAlert(); + /** * Process sensor information * @param uptime uptime in milliseconds of the system diff --git a/app/backplane/sensor_module/src/c_detection_handler.cpp b/app/backplane/sensor_module/src/c_detection_handler.cpp index 3eec1f6b1..6f2860f3e 100644 --- a/app/backplane/sensor_module/src/c_detection_handler.cpp +++ b/app/backplane/sensor_module/src/c_detection_handler.cpp @@ -42,6 +42,15 @@ void CDetectionHandler::HandleData(const uint64_t timestamp, const NTypes::Senso if (!controller.HasEventOccurred(Events::Boost)) { HandleBoost(timestamp, data, sensor_states); + if (controller.HasEventOccurred(Events::Boost)) { + // Boost confirmed so record for noseover and ground detection to + // measure time since boost and schedule rebroadcast of alert + boost_detected_time = timestamp; + boostAlertResendsRemaining = alertResendCycles; + } + } else if (boostAlertResendsRemaining > 0) { + alertMessagePort.Send(boostNotification); + boostAlertResendsRemaining--; } // Don't worry about the rest until we've got boost if (!controller.HasEventOccurred(Events::Boost)) { @@ -75,11 +84,16 @@ void CDetectionHandler::HandleGround(const uint32_t t_plus_ms, const NTypes::Sen if (primaryBaromGroundDetector.Passed() && sensor_states.primaryBarometerOk) { controller.SubmitEvent(Sources::BaromMS5611, Events::GroundHit); - alertMessagePort.Send(landedNotification); } if (secondaryBaromGroundDetector.Passed() && sensor_states.secondaryBarometerOk) { controller.SubmitEvent(Sources::BaromBMP, Events::GroundHit); + } +} + +void CDetectionHandler::ServiceLandedAlert() { + if (controller.HasEventOccurred(Events::GroundHit) && landedAlertResendsRemaining > 0) { alertMessagePort.Send(landedNotification); + landedAlertResendsRemaining--; } } diff --git a/app/backplane/sensor_module/src/c_sensing_tenant.cpp b/app/backplane/sensor_module/src/c_sensing_tenant.cpp index 02cbe7342..edcebdbf8 100644 --- a/app/backplane/sensor_module/src/c_sensing_tenant.cpp +++ b/app/backplane/sensor_module/src/c_sensing_tenant.cpp @@ -55,6 +55,7 @@ void CSensingTenant::Startup() { void CSensingTenant::PostStartup() {} void CSensingTenant::Run() { + detectionHandler.ServiceLandedAlert(); if (!detectionHandler.ContinueCollecting()) { return; }