1use rustflexstack::btp::router::{BTPRouterHandle, Router as BTPRouter};
16use rustflexstack::btp::service_access_point::BTPDataRequest;
17use rustflexstack::facilities::ca_basic_service::cam_coder::{
18 cam_header, generation_delta_time_now, AccelerationComponent, AccelerationConfidence,
19 AccelerationValue, Altitude, AltitudeConfidence, AltitudeValue, BasicContainer,
20 BasicVehicleContainerHighFrequency, Cam, CamCoder, CamParameters, CamPayload, Curvature,
21 CurvatureCalculationMode, CurvatureConfidence, CurvatureValue, DriveDirection, Heading,
22 HeadingConfidence, HeadingValue, HighFrequencyContainer, Latitude, Longitude,
23 PositionConfidenceEllipse, ReferencePositionWithConfidence, SemiAxisLength, Speed,
24 SpeedConfidence, SpeedValue, TrafficParticipantType, VehicleLength,
25 VehicleLengthConfidenceIndication, VehicleLengthValue, VehicleWidth, Wgs84AngleValue, YawRate,
26 YawRateConfidence, YawRateValue,
27};
28use rustflexstack::geonet::gn_address::{GNAddress, M, MID, ST};
29use rustflexstack::geonet::mib::Mib;
30use rustflexstack::geonet::position_vector::LongPositionVector;
31use rustflexstack::geonet::router::{Router as GNRouter, RouterHandle};
32use rustflexstack::geonet::service_access_point::{
33 Area, CommonNH, CommunicationProfile, GNDataIndication, GNDataRequest, HeaderSubType,
34 HeaderType, PacketTransportType, TopoBroadcastHST, TrafficClass,
35};
36use rustflexstack::link_layer::raw_link_layer::RawLinkLayer;
37use rustflexstack::security::sn_sap::SecurityProfile;
38
39use std::env;
40use std::sync::mpsc;
41use std::thread;
42use std::time::{Duration, Instant};
43
44fn main() {
45 let iface = env::args().nth(1).unwrap_or_else(|| "lo".to_string());
46 let duration_s = env::args()
47 .nth(2)
48 .and_then(|s| s.parse::<u64>().ok())
49 .unwrap_or(10);
50
51 println!("=== Benchmark: Maximum CAM TX throughput ===");
52 println!("Interface : {iface}");
53 println!("Duration : {duration_s} s\n");
54
55 let mac = random_mac();
57 let mut mib = Mib::new();
58 mib.itsGnLocalGnAddr = GNAddress::new(M::GnMulticast, ST::PassengerCar, MID::new(mac));
59 mib.itsGnBeaconServiceRetransmitTimer = 0;
60
61 let (gn_handle, gn_to_ll_rx, gn_to_btp_rx) = GNRouter::spawn(mib, None, None, None);
63 let (btp_handle, btp_to_gn_rx) = BTPRouter::spawn(mib);
64
65 let (ll_to_gn_tx, ll_to_gn_rx) = mpsc::channel::<Vec<u8>>();
66 RawLinkLayer::new(ll_to_gn_tx, gn_to_ll_rx, &iface, mac).start();
67 wire_routers(
68 &gn_handle,
69 &btp_handle,
70 ll_to_gn_rx,
71 gn_to_btp_rx,
72 btp_to_gn_rx,
73 );
74
75 let mut epv = LongPositionVector::decode([0u8; 24]);
77 epv.update_from_gps(41.552, 2.134, 0.0, 0.0, true);
78 gn_handle.update_position_vector(epv);
79 thread::sleep(Duration::from_millis(50));
80
81 let station_id = u32::from_be_bytes([mac[2], mac[3], mac[4], mac[5]]);
83 let coder = CamCoder::new();
84 let template = make_cam(station_id);
85
86 println!("Sending CAMs as fast as possible…\n");
88 println!(
89 "{:>7} {:>10} {:>12} {:>12}",
90 "time(s)", "total_sent", "rate(pkt/s)", "avg_enc(µs)"
91 );
92
93 let mut total_sent: u64 = 0;
94 let mut total_enc_us: u128 = 0;
95 let bench_start = Instant::now();
96 let bench_end = bench_start + Duration::from_secs(duration_s);
97 let mut win_start = Instant::now();
98 let mut win_sent: u64 = 0;
99
100 while Instant::now() < bench_end {
101 let t0 = Instant::now();
102 let data = match coder.encode(&template) {
103 Ok(d) => d,
104 Err(e) => {
105 eprintln!("[TX] Encode error: {e}");
106 continue;
107 }
108 };
109 let enc_us = t0.elapsed().as_micros();
110
111 btp_handle.send_btp_data_request(cam_btp_request(data));
112
113 total_sent += 1;
114 total_enc_us += enc_us;
115 win_sent += 1;
116
117 let win_elapsed = win_start.elapsed();
118 if win_elapsed >= Duration::from_secs(1) {
119 let pps = win_sent as f64 / win_elapsed.as_secs_f64();
120 let avg_enc = total_enc_us / total_sent.max(1) as u128;
121 println!(
122 "{:>7.1} {:>10} {:>12.1} {:>12}",
123 bench_start.elapsed().as_secs_f64(),
124 total_sent,
125 pps,
126 avg_enc
127 );
128 win_start = Instant::now();
129 win_sent = 0;
130 }
131 }
132
133 let elapsed = bench_start.elapsed().as_secs_f64();
135 let avg_rate = total_sent as f64 / elapsed;
136 let avg_encode = total_enc_us / total_sent.max(1) as u128;
137
138 println!();
139 println!("=== CAM TX Results ===");
140 println!(" Total sent : {total_sent}");
141 println!(" Elapsed : {elapsed:.3} s");
142 println!(" Average rate : {avg_rate:.1} pkt/s");
143 println!(" Avg encode time : {avg_encode} µs");
144}
145
146fn make_cam(station_id: u32) -> Cam {
149 let hf = BasicVehicleContainerHighFrequency::new(
150 Heading::new(HeadingValue(900), HeadingConfidence(127)),
151 Speed::new(SpeedValue(0), SpeedConfidence(127)),
152 DriveDirection::unavailable,
153 VehicleLength::new(
154 VehicleLengthValue(1023),
155 VehicleLengthConfidenceIndication::unavailable,
156 ),
157 VehicleWidth(62),
158 AccelerationComponent::new(AccelerationValue(161), AccelerationConfidence(102)),
159 Curvature::new(CurvatureValue(1023), CurvatureConfidence::unavailable),
160 CurvatureCalculationMode::unavailable,
161 YawRate::new(YawRateValue(32767), YawRateConfidence::unavailable),
162 None,
163 None,
164 None,
165 None,
166 None,
167 None,
168 None,
169 );
170 Cam::new(
171 cam_header(station_id),
172 CamPayload::new(
173 generation_delta_time_now(),
174 CamParameters::new(
175 BasicContainer::new(
176 TrafficParticipantType(5),
177 ReferencePositionWithConfidence::new(
178 Latitude(415_520_000),
179 Longitude(21_340_000),
180 PositionConfidenceEllipse::new(
181 SemiAxisLength(4095),
182 SemiAxisLength(4095),
183 Wgs84AngleValue(3601),
184 ),
185 Altitude::new(AltitudeValue(12000), AltitudeConfidence::unavailable),
186 ),
187 ),
188 HighFrequencyContainer::basicVehicleContainerHighFrequency(hf),
189 None,
190 None,
191 None,
192 ),
193 ),
194 )
195}
196
197fn cam_btp_request(data: Vec<u8>) -> BTPDataRequest {
198 BTPDataRequest {
199 btp_type: CommonNH::BtpB,
200 source_port: 0,
201 destination_port: 2001,
202 destination_port_info: 0,
203 gn_packet_transport_type: PacketTransportType {
204 header_type: HeaderType::Tsb,
205 header_sub_type: HeaderSubType::TopoBroadcast(TopoBroadcastHST::SingleHop),
206 },
207 gn_destination_address: GNAddress {
208 m: M::GnMulticast,
209 st: ST::Unknown,
210 mid: MID::new([0xFF; 6]),
211 },
212 communication_profile: CommunicationProfile::Unspecified,
213 gn_area: Area {
214 latitude: 0,
215 longitude: 0,
216 a: 0,
217 b: 0,
218 angle: 0,
219 },
220 traffic_class: TrafficClass {
221 scf: false,
222 channel_offload: false,
223 tc_id: 0,
224 },
225 security_profile: SecurityProfile::NoSecurity,
226 its_aid: 36,
227 security_permissions: vec![],
228 gn_max_hop_limit: 1,
229 gn_max_packet_lifetime: None,
230 gn_repetition_interval: None,
231 gn_max_repetition_time: None,
232 destination: None,
233 length: data.len() as u16,
234 data,
235 }
236}
237
238fn random_mac() -> [u8; 6] {
239 use std::time::{SystemTime, UNIX_EPOCH};
240 let s = SystemTime::now()
241 .duration_since(UNIX_EPOCH)
242 .unwrap()
243 .subsec_nanos();
244 [
245 0x02,
246 (s >> 24) as u8,
247 (s >> 16) as u8,
248 (s >> 8) as u8,
249 s as u8,
250 0xBB,
251 ]
252}
253
254fn wire_routers(
255 gn: &RouterHandle,
256 btp: &BTPRouterHandle,
257 ll_rx: mpsc::Receiver<Vec<u8>>,
258 gn_btp_rx: mpsc::Receiver<GNDataIndication>,
259 btp_gn_rx: mpsc::Receiver<GNDataRequest>,
260) {
261 let g1 = gn.clone();
262 thread::spawn(move || {
263 while let Ok(p) = ll_rx.recv() {
264 g1.send_incoming_packet(p);
265 }
266 });
267 let b1 = btp.clone();
268 thread::spawn(move || {
269 while let Ok(i) = gn_btp_rx.recv() {
270 b1.send_gn_data_indication(i);
271 }
272 });
273 let g2 = gn.clone();
274 thread::spawn(move || {
275 while let Ok(r) = btp_gn_rx.recv() {
276 g2.send_gn_data_request(r);
277 }
278 });
279}