//! V2X Transmission Utilities use alloc::vec::Vec; use c_its_parser::gn as geonetworking; pub mod gn; /// Interface for ITS Payloads inside a Geonetworking header pub trait GnItsPayload { // common GN parameters const LIFETIME_MILLIS: u32 = 60_000; // itsGnDefaultPacketLifetime is 60 seconds according to ETSI EN 302 636-4-1 V1.4.1 // --------------------------------------------- // methods to build a GN packet with ITS payload // --------------------------------------------- /// Builds the [`geonetworking::en302636_4_1::ExtendedHeader`] and additional information for the ITS message /// /// Output: /// - Extended header /// - Area type (when using GAC or GBC) /// - hop limit /// /// Note: Make sure to increase the sequence number on multi-hop packets. /// /// # Errors /// Returns a human-readable error description if values are out of range fn make_eh( &self, address: [u8; 6], own_position: &crate::PosVel, time: chrono::DateTime, seq_no: &mut u16, ) -> Result< ( geonetworking::en302636_4_1::ExtendedHeader, Option, u8, ), alloc::string::String, >; /// Creates the ITS payload (without BTP header) /// /// # Errors /// Returns a human-readable error description if values are out of range fn make_payload( &self, ) -> Result<(Vec, c_its_parser::standards::extensions::ItsMessageId), alloc::string::String>; // --------------------------------------------- // common methods (with implementation) // --------------------------------------------- /// Encodes the GN packet /// /// # Errors /// Returns a human-readable error description if values are out of range fn encode_packet( &self, address: [u8; 6], own_position: &crate::PosVel, time: chrono::DateTime, is_mobile: bool, seq_no: &mut u16, ) -> Result, alloc::string::String> { use geonetworking::Encode as _; // Build payload (including BTP header) let (mut its_payload, msg_type) = self.make_payload()?; let mut payload = make_btp_b(msg_type).encode()?; payload.append(&mut its_payload); #[allow(clippy::cast_possible_truncation)] let payload_length = (payload.len()) as u16; // get extended header (depending on message type and sometimes content as well) let (eh, area_type, maximum_hop_limit) = self.make_eh(address, own_position, time, seq_no)?; // Build GN headers let traffic_class = geonetworking::en302636_4_1::TrafficClass::try_new( false, false, make_traffic_class(msg_type), ) .map_err(|err| alloc::format!("Failed to create TrafficClass: {err:?}"))?; let common = geonetworking::en302636_4_1::CommonHeader::from_values( geonetworking::en302636_4_1::NextAfterCommon::BTPB, geonetworking::en302636_4_1::HeaderType::new_from(&eh, area_type)?, traffic_class, is_mobile, payload_length, maximum_hop_limit, ); let remaining_hop_limit = maximum_hop_limit; let basic = geonetworking::en302636_4_1::BasicHeader::try_new( 1, geonetworking::en302636_4_1::NextAfterBasic::CommonHeader, geonetworking::en302636_4_1::Lifetime::from_milliseconds(Self::LIFETIME_MILLIS), remaining_hop_limit, ) .map_err(|err| alloc::format!("Failed to create BasicHeader: {err}"))?; // Assemble and encode packet let packet = geonetworking::Packet::Unsecured { basic, common, extended: Some(eh), payload: &payload, }; packet .encode_to_vec() .map_err(|err| alloc::format!("Failed to encode GN packet: {err:?}")) } } /// Creates a BTP-B header based on the ITS message ID (message type) /// /// Port numbers and destination port info according to ETSI TS 103 248 fn make_btp_b( msg_type: c_its_parser::standards::extensions::ItsMessageId, ) -> c_its_parser::transport::TransportHeader { let (destination_port, destination_port_info) = match msg_type { c_its_parser::standards::extensions::ItsMessageId::Denm => (2002, 0), c_its_parser::standards::extensions::ItsMessageId::Cam => (2001, 0), c_its_parser::standards::extensions::ItsMessageId::Mapem => (2003, 0), c_its_parser::standards::extensions::ItsMessageId::Spatem => (2004, 0), c_its_parser::standards::extensions::ItsMessageId::Saem => (2005, 0), c_its_parser::standards::extensions::ItsMessageId::Ivim => (2006, 0), c_its_parser::standards::extensions::ItsMessageId::Srem => (2007, 0), c_its_parser::standards::extensions::ItsMessageId::Ssem => (2008, 0), c_its_parser::standards::extensions::ItsMessageId::Cpm => (2009, 0), c_its_parser::standards::extensions::ItsMessageId::Poi => (2010, 0), c_its_parser::standards::extensions::ItsMessageId::EvRsr => (2012, 0), c_its_parser::standards::extensions::ItsMessageId::Rtcmem => (2013, 0), _ => (0, 0), // no information found for the subsequent types }; c_its_parser::transport::TransportHeader::BtpB(c_its_parser::transport::BasicTransportBHeader { destination_port, destination_port_info, }) } /// Creates a traffic class ID based on the ITS message ID (message type) fn make_traffic_class(msg_type: c_its_parser::standards::extensions::ItsMessageId) -> u8 { #[allow(clippy::match_same_arms)] match msg_type { c_its_parser::standards::extensions::ItsMessageId::Denm => 1, c_its_parser::standards::extensions::ItsMessageId::Cam => 2, c_its_parser::standards::extensions::ItsMessageId::Mapem => 3, // from C-Roads RS_RSP_063(1) c_its_parser::standards::extensions::ItsMessageId::Spatem => 3, // from C-Roads RS_RSP_063(1) c_its_parser::standards::extensions::ItsMessageId::Ivim => 3, // from C-Roads RS_RSP_063(1) c_its_parser::standards::extensions::ItsMessageId::Srem => 2, // from C-Roads RS_RSP_063(1) c_its_parser::standards::extensions::ItsMessageId::Ssem => 2, // from C-Roads RS_RSP_063(1) c_its_parser::standards::extensions::ItsMessageId::Rtcmem => 3, // CSP_MaxPrio=252 -> 3 _ => 3, // fall-back to lowest priority } }