From 97412982bd9a939c0b5f60b3d9d52cf433323b21 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 3 Aug 2026 11:17:51 +0200 Subject: [PATCH] WIP rx: Add basic CAM and DENM evaluation --- src/rx/cam.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/rx/denm.rs | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/rx/cam.rs create mode 100644 src/rx/denm.rs diff --git a/src/rx/cam.rs b/src/rx/cam.rs new file mode 100644 index 0000000..1ee6c53 --- /dev/null +++ b/src/rx/cam.rs @@ -0,0 +1,65 @@ +//! C-ITS CAM Evaluation + +use c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions::{self, HighFrequencyContainer}; + +/// Prints a CAM as one line +pub fn pretty_print(etsi: &cam_pdu_descriptions::CAM) -> Option { + match &etsi.cam.cam_parameters.high_frequency_container { + HighFrequencyContainer::basicVehicleContainerHighFrequency(obu) => { + let station_id = etsi.header.station_id.0; + let lat_deg = etsi + .cam + .cam_parameters + .basic_container + .reference_position + .latitude + .as_deg(); + let lon_deg = etsi + .cam + .cam_parameters + .basic_container + .reference_position + .longitude + .as_deg(); + + let result = if obu.speed.speed_value.is_unavailable() { + alloc::format!( + "Got CAM from {station_id} at {lat_deg:.3}°N, {lon_deg:.3}°E, speed unavailable", + ) + } else { + alloc::format!( + "Got CAM from {station_id} at {lat_deg:.3}°N, {lon_deg:.3}°E, speed {:.2} km/h", + obu.speed.speed_value.as_kmh() + ) + }; + + Some(result) + } + HighFrequencyContainer::rsuContainerHighFrequency(rsu) => Some(alloc::format!( + "Got RSU from {} at {:.3}°N, {:.3}°E, {} protected zones", + etsi.header.station_id.0, + etsi.cam + .cam_parameters + .basic_container + .reference_position + .latitude + .as_deg(), + etsi.cam + .cam_parameters + .basic_container + .reference_position + .longitude + .as_deg(), + rsu.protected_communication_zones_rsu + .as_ref() + .map(|d| d.0.len()) + .unwrap_or_default() + )), + + _ => { + // enum is non-exhaustive, so we need a default case + // but this should never be reached in practical applications + None + } + } +} diff --git a/src/rx/denm.rs b/src/rx/denm.rs new file mode 100644 index 0000000..592646b --- /dev/null +++ b/src/rx/denm.rs @@ -0,0 +1,46 @@ +//! C-ITS DENM Evaluation + +use c_its_parser::standards::denm_2_2_1::denm_pdu_description; + +/// Prints a DENM as one line (unless it's a termination) +pub fn pretty_print(etsi: &denm_pdu_description::DENM) -> Option { + let mgmt = &etsi.denm.management; + + if mgmt.termination.is_some() { + return None; + } + + let result = if let (Some(sit), Some(loc)) = (&etsi.denm.situation, &etsi.denm.location) { + // let timestamp: chrono::DateTime = mgmt.reference_time.clone().into(); + let end_time = chrono::DateTime::::from(mgmt.reference_time.clone()) + + chrono::Duration::seconds(mgmt.validity_duration.0.into()); + let station_id = &mgmt.action_id.originating_station_id.0; + let seq_num = mgmt.action_id.sequence_number.0; + + let lat_deg = mgmt.event_position.latitude.as_deg(); + let lon_deg = mgmt.event_position.longitude.as_deg(); + + let heading = loc + .event_position_heading + .as_ref() + .map(|v| v.value.as_deg()); + + // let direction = mgmt.traffic_direction; + + let (cc, scc) = sit.event_type.cc_and_scc.to_u8_tuple(); + + if let Some(hdg) = heading { + alloc::format!( + "DENN: {station_id}/{seq_num} CC {cc}/{scc} at {lat_deg:.3}°N, {lon_deg:.3}°E, hdg {hdg:.1}° until {end_time:?}" + ) + } else { + alloc::format!( + "DENN: {station_id}/{seq_num} CC {cc}/{scc} at {lat_deg:.3}°N, {lon_deg:.3}°E until {end_time:?}" + ) + } + } else { + alloc::format!("DENM: One of situation or location container not available") + }; + + Some(result) +}