Skip to main content

BasicHeader

Struct BasicHeader 

Source
pub struct BasicHeader {
    pub version: u8,
    pub nh: BasicNH,
    pub reserved: u8,
    pub lt: LT,
    pub rhl: u8,
}

Fields§

§version: u8§nh: BasicNH§reserved: u8§lt: LT§rhl: u8

Implementations§

Source§

impl BasicHeader

Source

pub fn decode(bytes: [u8; 4]) -> Self

Examples found in repository?
examples/secured_vam_sender_receiver.rs (line 216)
118fn main() {
119    // ── Parse arguments ──────────────────────────────────────────────────
120    let args: Vec<String> = env::args().collect();
121    let mut at_index: usize = 1;
122    let mut iface = "lo".to_string();
123
124    let mut i = 1;
125    while i < args.len() {
126        match args[i].as_str() {
127            "--at" => {
128                i += 1;
129                at_index = args[i].parse::<usize>().expect("--at must be 1 or 2");
130                assert!(at_index == 1 || at_index == 2, "--at must be 1 or 2");
131            }
132            "--iface" | "-i" => {
133                i += 1;
134                iface = args[i].clone();
135            }
136            other => {
137                // Positional argument: interface name
138                iface = other.to_string();
139            }
140        }
141        i += 1;
142    }
143
144    println!("=== Secured VAM Sender/Receiver (VRU Awareness Service) ===");
145    println!("AT index: {}", at_index);
146    println!("Interface: {}", iface);
147
148    // ── Build security stack ─────────────────────────────────────────────
149    let sign_service = build_security_stack(at_index);
150    println!(
151        "Security stack loaded. Own AT HashedId8: {:02x?}",
152        sign_service
153            .cert_library
154            .own_certificates
155            .keys()
156            .next()
157            .unwrap()
158    );
159
160    // Wrap in Arc<Mutex> so it can be shared between threads
161    let sign_service = Arc::new(Mutex::new(sign_service));
162
163    // ── Generate a random locally-administered MAC ───────────────────────
164    let mac = {
165        use std::time::{SystemTime, UNIX_EPOCH};
166        let seed = SystemTime::now()
167            .duration_since(UNIX_EPOCH)
168            .unwrap()
169            .subsec_nanos()
170            ^ (at_index as u32 * 0x1234_5678); // different seed per AT
171        [
172            0x02u8,
173            (seed >> 24) as u8,
174            (seed >> 16) as u8,
175            (seed >> 8) as u8,
176            seed as u8,
177            at_index as u8,
178        ]
179    };
180    println!(
181        "MAC: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
182        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
183    );
184
185    // ── MIB ──────────────────────────────────────────────────────────────
186    let mut mib = Mib::new();
187    mib.itsGnLocalGnAddr = GNAddress::new(M::GnMulticast, ST::Cyclist, MID::new(mac));
188    mib.itsGnBeaconServiceRetransmitTimer = 0;
189
190    // ── Location Service ─────────────────────────────────────────────────
191    let mut loc_svc = LocationService::new();
192    let gn_gps_rx = loc_svc.subscribe();
193    let vru_gps_rx = loc_svc.subscribe();
194
195    // ── Spawn GN router and BTP router ───────────────────────────────────
196    let (gn_handle, gn_to_ll_rx, gn_to_btp_rx) = GNRouter::spawn(mib, None, None, None);
197    let (btp_handle, btp_to_gn_rx) = BTPRouter::spawn(mib);
198
199    // ── Wire RawLinkLayer ────────────────────────────────────────────────
200    let (ll_to_gn_tx, ll_to_gn_rx) = mpsc::channel::<Vec<u8>>();
201
202    // ── Security middleware: TX path ─────────────────────────────────────
203    //
204    // Intercept packets from GN router → link layer.
205    // Sign the payload (CommonHeader + extended header + data) and wrap
206    // in a secured GN packet with BasicNH::SecuredPacket.
207    let (secured_ll_tx, secured_ll_rx) = mpsc::channel::<Vec<u8>>();
208    let sign_svc_tx = Arc::clone(&sign_service);
209    thread::spawn(move || {
210        while let Ok(packet) = gn_to_ll_rx.recv() {
211            if packet.len() < 4 {
212                let _ = secured_ll_tx.send(packet);
213                continue;
214            }
215            let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
216            let bh = BasicHeader::decode(bh_bytes);
217
218            match bh.nh {
219                BasicNH::CommonHeader if packet.len() > 4 => {
220                    let inner_payload = &packet[4..];
221
222                    let request = SNSignRequest {
223                        tbs_message: inner_payload.to_vec(),
224                        its_aid: ITS_AID_VAM,
225                        permissions: vec![],
226                        generation_location: None,
227                    };
228
229                    let sec_message = {
230                        let mut svc = sign_svc_tx.lock().unwrap();
231                        svc.sign_request(&request).sec_message
232                    };
233
234                    // Build new packet: BasicHeader(nh=SecuredPacket) + sec_message
235                    let mut new_bh = bh;
236                    new_bh.nh = BasicNH::SecuredPacket;
237                    let secured_packet: Vec<u8> = new_bh
238                        .encode()
239                        .iter()
240                        .copied()
241                        .chain(sec_message.iter().copied())
242                        .collect();
243                    let _ = secured_ll_tx.send(secured_packet);
244                }
245                _ => {
246                    // Pass through (e.g. beacons)
247                    let _ = secured_ll_tx.send(packet);
248                }
249            }
250        }
251    });
252
253    // The link layer reads from secured_ll_rx (post-signing)
254    let raw_ll = RawLinkLayer::new(ll_to_gn_tx, secured_ll_rx, &iface, mac);
255    raw_ll.start();
256
257    // ── Security middleware: RX path ─────────────────────────────────────
258    //
259    // Intercept packets from link layer → GN router.
260    // If BasicNH::SecuredPacket, verify and extract, then forward
261    // with BasicNH::CommonHeader.
262    let gn_h_rx = gn_handle.clone();
263    let sign_svc_rx = Arc::clone(&sign_service);
264    thread::spawn(move || {
265        while let Ok(packet) = ll_to_gn_rx.recv() {
266            if packet.len() < 4 {
267                gn_h_rx.send_incoming_packet(packet);
268                continue;
269            }
270            let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
271            let bh = BasicHeader::decode(bh_bytes);
272
273            match bh.nh {
274                BasicNH::SecuredPacket if packet.len() > 4 => {
275                    let sec_message = &packet[4..];
276                    let request = SNVerifyRequest {
277                        message: sec_message.to_vec(),
278                    };
279
280                    let (confirm, _events) = {
281                        let mut svc = sign_svc_rx.lock().unwrap();
282                        let svc = &mut *svc;
283                        let result = verify_message(&request, &svc.backend, &mut svc.cert_library);
284                        // Process VerifyEvents for P2PCD
285                        for event in &result.1 {
286                            match event {
287                                VerifyEvent::UnknownAt(h8) => {
288                                    svc.notify_unknown_at(h8);
289                                }
290                                VerifyEvent::InlineP2pcdRequest(h3s) => {
291                                    svc.notify_inline_p2pcd_request(h3s);
292                                }
293                                VerifyEvent::ReceivedCaCertificate(cert) => {
294                                    svc.notify_received_ca_certificate(cert.as_ref().clone());
295                                }
296                            }
297                        }
298                        result
299                    };
300
301                    if confirm.report == ReportVerify::Success {
302                        println!(
303                            "[SEC RX] Verified OK — ITS-AID={}, cert={:02x?}",
304                            confirm.its_aid,
305                            &confirm.certificate_id[..],
306                        );
307                        // Rebuild the packet: BasicHeader(nh=CommonHeader) + plain_message
308                        let mut new_bh = bh;
309                        new_bh.nh = BasicNH::CommonHeader;
310                        let plain_packet: Vec<u8> = new_bh
311                            .encode()
312                            .iter()
313                            .copied()
314                            .chain(confirm.plain_message.iter().copied())
315                            .collect();
316                        gn_h_rx.send_incoming_packet(plain_packet);
317                    } else {
318                        eprintln!("[SEC RX] Verification failed: {:?}", confirm.report);
319                    }
320                }
321                _ => {
322                    // Non-secured packet — forward directly
323                    gn_h_rx.send_incoming_packet(packet);
324                }
325            }
326        }
327    });
328
329    // ── GN → BTP ─────────────────────────────────────────────────────────
330    let btp_h1 = btp_handle.clone();
331    thread::spawn(move || {
332        while let Ok(ind) = gn_to_btp_rx.recv() {
333            btp_h1.send_gn_data_indication(ind);
334        }
335    });
336
337    // ── BTP → GN ─────────────────────────────────────────────────────────
338    let gn_h2 = gn_handle.clone();
339    thread::spawn(move || {
340        while let Ok(req) = btp_to_gn_rx.recv() {
341            gn_h2.send_gn_data_request(req);
342        }
343    });
344
345    // ── LocationService → GN position vector ─────────────────────────────
346    let gn_h3 = gn_handle.clone();
347    thread::spawn(move || {
348        while let Ok(fix) = gn_gps_rx.recv() {
349            let mut epv = LongPositionVector::decode([0u8; 24]);
350            epv.update_from_gps(
351                fix.latitude,
352                fix.longitude,
353                fix.speed_mps,
354                fix.heading_deg,
355                fix.pai,
356            );
357            gn_h3.update_position_vector(epv);
358        }
359    });
360
361    // ── VRU Awareness Service ────────────────────────────────────────────
362    let station_id = u32::from_be_bytes([mac[2], mac[3], mac[4], mac[5]]);
363    let device_data = DeviceData {
364        station_id,
365        station_type: 2, // cyclist
366    };
367
368    let (vru_svc, vam_rx) = VruAwarenessService::new(btp_handle.clone(), device_data);
369    vru_svc.start(vru_gps_rx);
370
371    // ── Decoded VAM printer ──────────────────────────────────────────────
372    thread::spawn(move || {
373        while let Ok(vam) = vam_rx.recv() {
374            let lat = vam
375                .vam
376                .vam_parameters
377                .basic_container
378                .reference_position
379                .latitude
380                .0 as f64
381                / 1e7;
382            let lon = vam
383                .vam
384                .vam_parameters
385                .basic_container
386                .reference_position
387                .longitude
388                .0 as f64
389                / 1e7;
390            println!(
391                "[VAM RX] station={:>10}  lat={:.5}  lon={:.5}",
392                vam.header.0.station_id.0, lat, lon,
393            );
394        }
395    });
396
397    // ── GPS publisher (simulates a VRU GNSS sensor at 10 Hz) ─────────────
398    thread::sleep(Duration::from_millis(100));
399    println!("Publishing GPS fixes @ 10 Hz — Ctrl+C to stop\n");
400
401    loop {
402        thread::sleep(Duration::from_millis(100));
403        // 41.552°N  2.134°E — Parc Tecnològic del Vallès
404        loc_svc.publish(GpsFix {
405            latitude: 41.552,
406            longitude: 2.134,
407            altitude_m: 120.0,
408            speed_mps: 1.5,
409            heading_deg: 90.0,
410            pai: true,
411        });
412    }
413}
More examples
Hide additional examples
examples/secured_cam_sender_receiver.rs (line 224)
125fn main() {
126    // ── Parse arguments ──────────────────────────────────────────────────
127    let args: Vec<String> = env::args().collect();
128    let mut at_index: usize = 1;
129    let mut iface = "lo".to_string();
130
131    let mut i = 1;
132    while i < args.len() {
133        match args[i].as_str() {
134            "--at" => {
135                i += 1;
136                at_index = args[i].parse::<usize>().expect("--at must be 1 or 2");
137                assert!(at_index == 1 || at_index == 2, "--at must be 1 or 2");
138            }
139            "--iface" | "-i" => {
140                i += 1;
141                iface = args[i].clone();
142            }
143            other => {
144                // Positional argument: interface name
145                iface = other.to_string();
146            }
147        }
148        i += 1;
149    }
150
151    println!("=== Secured CAM Sender/Receiver ===");
152    println!("AT index: {}", at_index);
153    println!("Interface: {}", iface);
154
155    // ── Build security stack ─────────────────────────────────────────────
156    let sign_service = build_security_stack(at_index);
157    println!(
158        "Security stack loaded. Own AT HashedId8: {:02x?}",
159        sign_service
160            .cert_library
161            .own_certificates
162            .keys()
163            .next()
164            .unwrap()
165    );
166
167    // Wrap in Arc<Mutex> so it can be shared between threads
168    let sign_service = Arc::new(Mutex::new(sign_service));
169
170    // ── Generate a random locally-administered MAC ───────────────────────
171    let mac = {
172        use std::time::{SystemTime, UNIX_EPOCH};
173        let seed = SystemTime::now()
174            .duration_since(UNIX_EPOCH)
175            .unwrap()
176            .subsec_nanos()
177            ^ (at_index as u32 * 0x1234_5678); // different seed per AT
178        [
179            0x02u8,
180            (seed >> 24) as u8,
181            (seed >> 16) as u8,
182            (seed >> 8) as u8,
183            seed as u8,
184            at_index as u8,
185        ]
186    };
187    println!(
188        "MAC: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
189        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
190    );
191
192    // ── MIB ──────────────────────────────────────────────────────────────
193    let mut mib: Mib = Mib::new();
194    mib.itsGnLocalGnAddr = GNAddress::new(M::GnMulticast, ST::PassengerCar, MID::new(mac));
195    mib.itsGnBeaconServiceRetransmitTimer = 0;
196
197    // ── Location Service ─────────────────────────────────────────────────
198    let mut loc_svc = LocationService::new();
199    let gn_gps_rx = loc_svc.subscribe();
200    let ca_gps_rx = loc_svc.subscribe();
201
202    // ── Spawn GN router and BTP router ───────────────────────────────────
203    let (gn_handle, gn_to_ll_rx, gn_to_btp_rx) = GNRouter::spawn(mib, None, None, None);
204    let (btp_handle, btp_to_gn_rx) = BTPRouter::spawn(mib);
205
206    // ── Wire RawLinkLayer ────────────────────────────────────────────────
207    let (ll_to_gn_tx, ll_to_gn_rx) = mpsc::channel::<Vec<u8>>();
208
209    // ── Security middleware: TX path ─────────────────────────────────────
210    //
211    // Intercept packets from GN router → link layer.
212    // Sign the payload (CommonHeader + extended header + data) and wrap
213    // in a secured GN packet with BasicNH::SecuredPacket.
214    let (secured_ll_tx, secured_ll_rx) = mpsc::channel::<Vec<u8>>();
215    let sign_svc_tx = Arc::clone(&sign_service);
216    thread::spawn(move || {
217        while let Ok(packet) = gn_to_ll_rx.recv() {
218            if packet.len() < 4 {
219                let _ = secured_ll_tx.send(packet);
220                continue;
221            }
222            // Parse BasicHeader to check if it's a CommonHeader packet
223            let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
224            let bh = BasicHeader::decode(bh_bytes);
225
226            match bh.nh {
227                BasicNH::CommonHeader if packet.len() > 4 => {
228                    // The payload to sign = everything after BasicHeader
229                    let inner_payload = &packet[4..];
230
231                    let request = SNSignRequest {
232                        tbs_message: inner_payload.to_vec(),
233                        its_aid: ITS_AID_CAM_VAL,
234                        permissions: vec![],
235                        generation_location: None,
236                    };
237
238                    let sec_message = {
239                        let mut svc = sign_svc_tx.lock().unwrap();
240                        svc.sign_request(&request).sec_message
241                    };
242
243                    // Build new packet: BasicHeader(nh=SecuredPacket) + sec_message
244                    let mut new_bh = bh;
245                    new_bh.nh = BasicNH::SecuredPacket;
246                    let secured_packet: Vec<u8> = new_bh
247                        .encode()
248                        .iter()
249                        .copied()
250                        .chain(sec_message.iter().copied())
251                        .collect();
252                    let _ = secured_ll_tx.send(secured_packet);
253                }
254                _ => {
255                    // Pass through (e.g. beacons)
256                    let _ = secured_ll_tx.send(packet);
257                }
258            }
259        }
260    });
261
262    // The link layer reads from secured_ll_rx (post-signing)
263    let raw_ll = RawLinkLayer::new(ll_to_gn_tx, secured_ll_rx, &iface, mac);
264    raw_ll.start();
265
266    // ── Security middleware: RX path ─────────────────────────────────────
267    //
268    // Intercept packets from link layer → GN router.
269    // If BasicNH::SecuredPacket, verify and extract, then forward
270    // with BasicNH::CommonHeader.
271    let gn_h_rx = gn_handle.clone();
272    let sign_svc_rx = Arc::clone(&sign_service);
273    thread::spawn(move || {
274        while let Ok(packet) = ll_to_gn_rx.recv() {
275            if packet.len() < 4 {
276                gn_h_rx.send_incoming_packet(packet);
277                continue;
278            }
279            let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
280            let bh = BasicHeader::decode(bh_bytes);
281
282            match bh.nh {
283                BasicNH::SecuredPacket if packet.len() > 4 => {
284                    let sec_message = &packet[4..];
285                    let request = SNVerifyRequest {
286                        message: sec_message.to_vec(),
287                    };
288
289                    let (confirm, _events) = {
290                        let mut svc = sign_svc_rx.lock().unwrap();
291                        let svc = &mut *svc;
292                        let result = verify_message(&request, &svc.backend, &mut svc.cert_library);
293                        // Process VerifyEvents for P2PCD
294                        for event in &result.1 {
295                            match event {
296                                VerifyEvent::UnknownAt(h8) => {
297                                    svc.notify_unknown_at(h8);
298                                }
299                                VerifyEvent::InlineP2pcdRequest(h3s) => {
300                                    svc.notify_inline_p2pcd_request(h3s);
301                                }
302                                VerifyEvent::ReceivedCaCertificate(cert) => {
303                                    svc.notify_received_ca_certificate(cert.as_ref().clone());
304                                }
305                            }
306                        }
307                        result
308                    };
309
310                    if confirm.report == ReportVerify::Success {
311                        println!(
312                            "[SEC RX] Verified OK — ITS-AID={}, cert={:02x?}",
313                            confirm.its_aid,
314                            &confirm.certificate_id[..],
315                        );
316                        // Rebuild the packet: BasicHeader(nh=CommonHeader) + plain_message
317                        let mut new_bh = bh;
318                        new_bh.nh = BasicNH::CommonHeader;
319                        let plain_packet: Vec<u8> = new_bh
320                            .encode()
321                            .iter()
322                            .copied()
323                            .chain(confirm.plain_message.iter().copied())
324                            .collect();
325                        gn_h_rx.send_incoming_packet(plain_packet);
326                    } else {
327                        eprintln!("[SEC RX] Verification failed: {:?}", confirm.report);
328                    }
329                }
330                _ => {
331                    // Non-secured packet — forward directly
332                    gn_h_rx.send_incoming_packet(packet);
333                }
334            }
335        }
336    });
337
338    // ── GN → BTP ─────────────────────────────────────────────────────────
339    let btp_h1 = btp_handle.clone();
340    thread::spawn(move || {
341        while let Ok(ind) = gn_to_btp_rx.recv() {
342            btp_h1.send_gn_data_indication(ind);
343        }
344    });
345
346    // ── BTP → GN ─────────────────────────────────────────────────────────
347    let gn_h2 = gn_handle.clone();
348    thread::spawn(move || {
349        while let Ok(req) = btp_to_gn_rx.recv() {
350            gn_h2.send_gn_data_request(req);
351        }
352    });
353
354    // ── LocationService → GN position vector ─────────────────────────────
355    let gn_h3 = gn_handle.clone();
356    thread::spawn(move || {
357        while let Ok(fix) = gn_gps_rx.recv() {
358            let mut epv = LongPositionVector::decode([0u8; 24]);
359            epv.update_from_gps(
360                fix.latitude,
361                fix.longitude,
362                fix.speed_mps,
363                fix.heading_deg,
364                fix.pai,
365            );
366            gn_h3.update_position_vector(epv);
367        }
368    });
369
370    // ── Local Dynamic Map ────────────────────────────────────────────────
371    let ldm = LdmFacility::new(415_520_000, 21_340_000, 5_000.0);
372    ldm.if_ldm_3
373        .register_data_provider(RegisterDataProviderReq {
374            application_id: ITS_AID_CAM,
375        });
376    ldm.if_ldm_4
377        .register_data_consumer(RegisterDataConsumerReq {
378            application_id: ITS_AID_CAM,
379        });
380
381    // ── CA Basic Service ─────────────────────────────────────────────────
382    let station_id = u32::from_be_bytes([mac[2], mac[3], mac[4], mac[5]]);
383    let vehicle_data = VehicleData {
384        station_id,
385        ..VehicleData::default()
386    };
387    let (ca_svc, _cam_rx) =
388        CooperativeAwarenessBasicService::new(btp_handle.clone(), vehicle_data, Some(ldm.clone()));
389    ca_svc.start(ca_gps_rx);
390
391    // ── LDM query printer ────────────────────────────────────────────────
392    let ldm_reader = ldm.clone();
393    thread::spawn(move || loop {
394        thread::sleep(Duration::from_secs(1));
395        let resp = ldm_reader
396            .if_ldm_4
397            .request_data_objects(RequestDataObjectsReq {
398                application_id: ITS_AID_CAM,
399                data_object_types: vec![ITS_AID_CAM],
400                filter: None,
401                order: None,
402                max_results: None,
403            });
404        if resp.data_objects.is_empty() {
405            println!("[LDM] No CAM records in store");
406        } else {
407            println!("[LDM] {} CAM record(s):", resp.data_objects.len());
408            for entry in &resp.data_objects {
409                if let ItsDataObject::Cam(cam) = &entry.data_object {
410                    let lat = cam
411                        .cam
412                        .cam_parameters
413                        .basic_container
414                        .reference_position
415                        .latitude
416                        .0 as f64
417                        / 1e7;
418                    let lon = cam
419                        .cam
420                        .cam_parameters
421                        .basic_container
422                        .reference_position
423                        .longitude
424                        .0 as f64
425                        / 1e7;
426                    println!(
427                        "  [LDM CAM] record={:>5} station={:>10}  lat={:.5}  lon={:.5}",
428                        entry.record_id, cam.header.station_id.0, lat, lon,
429                    );
430                }
431            }
432        }
433    });
434
435    // ── GPS publisher (random trajectory — mirrors Python _RandomTrajectoryLocationService) ──
436    //
437    // Updates every 80 ms (below T_CheckCamGen = 100 ms) with a randomly
438    // changing heading (±5–15° per step, always exceeding the 4° condition-1
439    // threshold) and speed random-walking within [5, 20] m/s.  This sustains
440    // 10 Hz CAM generation via the dynamics triggering conditions.
441    thread::sleep(Duration::from_millis(100));
442    println!("Publishing GPS fixes @ 12.5 Hz (80 ms) with random trajectory — Ctrl+C to stop\n");
443
444    use rand::Rng;
445    let mut rng = rand::thread_rng();
446
447    const PERIOD_S: f64 = 0.08; // 80 ms — below T_CheckCamGen
448    const EARTH_R: f64 = 6_371_000.0;
449
450    let mut lat: f64 = 41.386931;
451    let mut lon: f64 = 2.112104;
452    let mut heading: f64 = rng.gen_range(0.0..360.0);
453    let mut speed: f64 = 10.0; // ~36 km/h
454
455    loop {
456        thread::sleep(Duration::from_millis(80));
457
458        // Heading: random signed change guaranteed to exceed the 4° threshold
459        let delta: f64 = rng.gen_range(5.0..15.0) * if rng.gen_bool(0.5) { 1.0 } else { -1.0 };
460        heading = (heading + delta).rem_euclid(360.0);
461
462        // Speed: small random walk within [5, 20] m/s
463        speed = (speed + rng.gen_range(-0.5..0.5)).clamp(5.0, 20.0);
464
465        // Position update (flat-Earth approximation; step sizes < 2 m)
466        let d = speed * PERIOD_S;
467        let heading_r = heading * PI / 180.0;
468        let lat_r = lat * PI / 180.0;
469        lat += (d * heading_r.cos() / EARTH_R) * (180.0 / PI);
470        lon += (d * heading_r.sin() / (EARTH_R * lat_r.cos())) * (180.0 / PI);
471
472        loc_svc.publish(GpsFix {
473            latitude: lat,
474            longitude: lon,
475            altitude_m: 120.0,
476            speed_mps: speed,
477            heading_deg: heading,
478            pai: true,
479        });
480    }
481}
Source

pub fn initialize_with_mib(mib: &Mib) -> Self

Source

pub fn initialize_with_mib_request_and_rhl( mib: &Mib, max_packet_lifetime: Option<f64>, rhl: u8, ) -> Self

Initialize from MIB with optional max_packet_lifetime and explicit RHL.

Source

pub fn set_nh(self, nh: BasicNH) -> Self

Return a copy with a different next-header value.

Source

pub fn set_rhl(self, rhl: u8) -> Self

Return a copy with a different remaining hop limit.

Source

pub fn encode(&self) -> [u8; 4]

Examples found in repository?
examples/secured_vam_sender_receiver.rs (line 238)
118fn main() {
119    // ── Parse arguments ──────────────────────────────────────────────────
120    let args: Vec<String> = env::args().collect();
121    let mut at_index: usize = 1;
122    let mut iface = "lo".to_string();
123
124    let mut i = 1;
125    while i < args.len() {
126        match args[i].as_str() {
127            "--at" => {
128                i += 1;
129                at_index = args[i].parse::<usize>().expect("--at must be 1 or 2");
130                assert!(at_index == 1 || at_index == 2, "--at must be 1 or 2");
131            }
132            "--iface" | "-i" => {
133                i += 1;
134                iface = args[i].clone();
135            }
136            other => {
137                // Positional argument: interface name
138                iface = other.to_string();
139            }
140        }
141        i += 1;
142    }
143
144    println!("=== Secured VAM Sender/Receiver (VRU Awareness Service) ===");
145    println!("AT index: {}", at_index);
146    println!("Interface: {}", iface);
147
148    // ── Build security stack ─────────────────────────────────────────────
149    let sign_service = build_security_stack(at_index);
150    println!(
151        "Security stack loaded. Own AT HashedId8: {:02x?}",
152        sign_service
153            .cert_library
154            .own_certificates
155            .keys()
156            .next()
157            .unwrap()
158    );
159
160    // Wrap in Arc<Mutex> so it can be shared between threads
161    let sign_service = Arc::new(Mutex::new(sign_service));
162
163    // ── Generate a random locally-administered MAC ───────────────────────
164    let mac = {
165        use std::time::{SystemTime, UNIX_EPOCH};
166        let seed = SystemTime::now()
167            .duration_since(UNIX_EPOCH)
168            .unwrap()
169            .subsec_nanos()
170            ^ (at_index as u32 * 0x1234_5678); // different seed per AT
171        [
172            0x02u8,
173            (seed >> 24) as u8,
174            (seed >> 16) as u8,
175            (seed >> 8) as u8,
176            seed as u8,
177            at_index as u8,
178        ]
179    };
180    println!(
181        "MAC: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
182        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
183    );
184
185    // ── MIB ──────────────────────────────────────────────────────────────
186    let mut mib = Mib::new();
187    mib.itsGnLocalGnAddr = GNAddress::new(M::GnMulticast, ST::Cyclist, MID::new(mac));
188    mib.itsGnBeaconServiceRetransmitTimer = 0;
189
190    // ── Location Service ─────────────────────────────────────────────────
191    let mut loc_svc = LocationService::new();
192    let gn_gps_rx = loc_svc.subscribe();
193    let vru_gps_rx = loc_svc.subscribe();
194
195    // ── Spawn GN router and BTP router ───────────────────────────────────
196    let (gn_handle, gn_to_ll_rx, gn_to_btp_rx) = GNRouter::spawn(mib, None, None, None);
197    let (btp_handle, btp_to_gn_rx) = BTPRouter::spawn(mib);
198
199    // ── Wire RawLinkLayer ────────────────────────────────────────────────
200    let (ll_to_gn_tx, ll_to_gn_rx) = mpsc::channel::<Vec<u8>>();
201
202    // ── Security middleware: TX path ─────────────────────────────────────
203    //
204    // Intercept packets from GN router → link layer.
205    // Sign the payload (CommonHeader + extended header + data) and wrap
206    // in a secured GN packet with BasicNH::SecuredPacket.
207    let (secured_ll_tx, secured_ll_rx) = mpsc::channel::<Vec<u8>>();
208    let sign_svc_tx = Arc::clone(&sign_service);
209    thread::spawn(move || {
210        while let Ok(packet) = gn_to_ll_rx.recv() {
211            if packet.len() < 4 {
212                let _ = secured_ll_tx.send(packet);
213                continue;
214            }
215            let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
216            let bh = BasicHeader::decode(bh_bytes);
217
218            match bh.nh {
219                BasicNH::CommonHeader if packet.len() > 4 => {
220                    let inner_payload = &packet[4..];
221
222                    let request = SNSignRequest {
223                        tbs_message: inner_payload.to_vec(),
224                        its_aid: ITS_AID_VAM,
225                        permissions: vec![],
226                        generation_location: None,
227                    };
228
229                    let sec_message = {
230                        let mut svc = sign_svc_tx.lock().unwrap();
231                        svc.sign_request(&request).sec_message
232                    };
233
234                    // Build new packet: BasicHeader(nh=SecuredPacket) + sec_message
235                    let mut new_bh = bh;
236                    new_bh.nh = BasicNH::SecuredPacket;
237                    let secured_packet: Vec<u8> = new_bh
238                        .encode()
239                        .iter()
240                        .copied()
241                        .chain(sec_message.iter().copied())
242                        .collect();
243                    let _ = secured_ll_tx.send(secured_packet);
244                }
245                _ => {
246                    // Pass through (e.g. beacons)
247                    let _ = secured_ll_tx.send(packet);
248                }
249            }
250        }
251    });
252
253    // The link layer reads from secured_ll_rx (post-signing)
254    let raw_ll = RawLinkLayer::new(ll_to_gn_tx, secured_ll_rx, &iface, mac);
255    raw_ll.start();
256
257    // ── Security middleware: RX path ─────────────────────────────────────
258    //
259    // Intercept packets from link layer → GN router.
260    // If BasicNH::SecuredPacket, verify and extract, then forward
261    // with BasicNH::CommonHeader.
262    let gn_h_rx = gn_handle.clone();
263    let sign_svc_rx = Arc::clone(&sign_service);
264    thread::spawn(move || {
265        while let Ok(packet) = ll_to_gn_rx.recv() {
266            if packet.len() < 4 {
267                gn_h_rx.send_incoming_packet(packet);
268                continue;
269            }
270            let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
271            let bh = BasicHeader::decode(bh_bytes);
272
273            match bh.nh {
274                BasicNH::SecuredPacket if packet.len() > 4 => {
275                    let sec_message = &packet[4..];
276                    let request = SNVerifyRequest {
277                        message: sec_message.to_vec(),
278                    };
279
280                    let (confirm, _events) = {
281                        let mut svc = sign_svc_rx.lock().unwrap();
282                        let svc = &mut *svc;
283                        let result = verify_message(&request, &svc.backend, &mut svc.cert_library);
284                        // Process VerifyEvents for P2PCD
285                        for event in &result.1 {
286                            match event {
287                                VerifyEvent::UnknownAt(h8) => {
288                                    svc.notify_unknown_at(h8);
289                                }
290                                VerifyEvent::InlineP2pcdRequest(h3s) => {
291                                    svc.notify_inline_p2pcd_request(h3s);
292                                }
293                                VerifyEvent::ReceivedCaCertificate(cert) => {
294                                    svc.notify_received_ca_certificate(cert.as_ref().clone());
295                                }
296                            }
297                        }
298                        result
299                    };
300
301                    if confirm.report == ReportVerify::Success {
302                        println!(
303                            "[SEC RX] Verified OK — ITS-AID={}, cert={:02x?}",
304                            confirm.its_aid,
305                            &confirm.certificate_id[..],
306                        );
307                        // Rebuild the packet: BasicHeader(nh=CommonHeader) + plain_message
308                        let mut new_bh = bh;
309                        new_bh.nh = BasicNH::CommonHeader;
310                        let plain_packet: Vec<u8> = new_bh
311                            .encode()
312                            .iter()
313                            .copied()
314                            .chain(confirm.plain_message.iter().copied())
315                            .collect();
316                        gn_h_rx.send_incoming_packet(plain_packet);
317                    } else {
318                        eprintln!("[SEC RX] Verification failed: {:?}", confirm.report);
319                    }
320                }
321                _ => {
322                    // Non-secured packet — forward directly
323                    gn_h_rx.send_incoming_packet(packet);
324                }
325            }
326        }
327    });
328
329    // ── GN → BTP ─────────────────────────────────────────────────────────
330    let btp_h1 = btp_handle.clone();
331    thread::spawn(move || {
332        while let Ok(ind) = gn_to_btp_rx.recv() {
333            btp_h1.send_gn_data_indication(ind);
334        }
335    });
336
337    // ── BTP → GN ─────────────────────────────────────────────────────────
338    let gn_h2 = gn_handle.clone();
339    thread::spawn(move || {
340        while let Ok(req) = btp_to_gn_rx.recv() {
341            gn_h2.send_gn_data_request(req);
342        }
343    });
344
345    // ── LocationService → GN position vector ─────────────────────────────
346    let gn_h3 = gn_handle.clone();
347    thread::spawn(move || {
348        while let Ok(fix) = gn_gps_rx.recv() {
349            let mut epv = LongPositionVector::decode([0u8; 24]);
350            epv.update_from_gps(
351                fix.latitude,
352                fix.longitude,
353                fix.speed_mps,
354                fix.heading_deg,
355                fix.pai,
356            );
357            gn_h3.update_position_vector(epv);
358        }
359    });
360
361    // ── VRU Awareness Service ────────────────────────────────────────────
362    let station_id = u32::from_be_bytes([mac[2], mac[3], mac[4], mac[5]]);
363    let device_data = DeviceData {
364        station_id,
365        station_type: 2, // cyclist
366    };
367
368    let (vru_svc, vam_rx) = VruAwarenessService::new(btp_handle.clone(), device_data);
369    vru_svc.start(vru_gps_rx);
370
371    // ── Decoded VAM printer ──────────────────────────────────────────────
372    thread::spawn(move || {
373        while let Ok(vam) = vam_rx.recv() {
374            let lat = vam
375                .vam
376                .vam_parameters
377                .basic_container
378                .reference_position
379                .latitude
380                .0 as f64
381                / 1e7;
382            let lon = vam
383                .vam
384                .vam_parameters
385                .basic_container
386                .reference_position
387                .longitude
388                .0 as f64
389                / 1e7;
390            println!(
391                "[VAM RX] station={:>10}  lat={:.5}  lon={:.5}",
392                vam.header.0.station_id.0, lat, lon,
393            );
394        }
395    });
396
397    // ── GPS publisher (simulates a VRU GNSS sensor at 10 Hz) ─────────────
398    thread::sleep(Duration::from_millis(100));
399    println!("Publishing GPS fixes @ 10 Hz — Ctrl+C to stop\n");
400
401    loop {
402        thread::sleep(Duration::from_millis(100));
403        // 41.552°N  2.134°E — Parc Tecnològic del Vallès
404        loc_svc.publish(GpsFix {
405            latitude: 41.552,
406            longitude: 2.134,
407            altitude_m: 120.0,
408            speed_mps: 1.5,
409            heading_deg: 90.0,
410            pai: true,
411        });
412    }
413}
More examples
Hide additional examples
examples/secured_cam_sender_receiver.rs (line 247)
125fn main() {
126    // ── Parse arguments ──────────────────────────────────────────────────
127    let args: Vec<String> = env::args().collect();
128    let mut at_index: usize = 1;
129    let mut iface = "lo".to_string();
130
131    let mut i = 1;
132    while i < args.len() {
133        match args[i].as_str() {
134            "--at" => {
135                i += 1;
136                at_index = args[i].parse::<usize>().expect("--at must be 1 or 2");
137                assert!(at_index == 1 || at_index == 2, "--at must be 1 or 2");
138            }
139            "--iface" | "-i" => {
140                i += 1;
141                iface = args[i].clone();
142            }
143            other => {
144                // Positional argument: interface name
145                iface = other.to_string();
146            }
147        }
148        i += 1;
149    }
150
151    println!("=== Secured CAM Sender/Receiver ===");
152    println!("AT index: {}", at_index);
153    println!("Interface: {}", iface);
154
155    // ── Build security stack ─────────────────────────────────────────────
156    let sign_service = build_security_stack(at_index);
157    println!(
158        "Security stack loaded. Own AT HashedId8: {:02x?}",
159        sign_service
160            .cert_library
161            .own_certificates
162            .keys()
163            .next()
164            .unwrap()
165    );
166
167    // Wrap in Arc<Mutex> so it can be shared between threads
168    let sign_service = Arc::new(Mutex::new(sign_service));
169
170    // ── Generate a random locally-administered MAC ───────────────────────
171    let mac = {
172        use std::time::{SystemTime, UNIX_EPOCH};
173        let seed = SystemTime::now()
174            .duration_since(UNIX_EPOCH)
175            .unwrap()
176            .subsec_nanos()
177            ^ (at_index as u32 * 0x1234_5678); // different seed per AT
178        [
179            0x02u8,
180            (seed >> 24) as u8,
181            (seed >> 16) as u8,
182            (seed >> 8) as u8,
183            seed as u8,
184            at_index as u8,
185        ]
186    };
187    println!(
188        "MAC: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
189        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
190    );
191
192    // ── MIB ──────────────────────────────────────────────────────────────
193    let mut mib: Mib = Mib::new();
194    mib.itsGnLocalGnAddr = GNAddress::new(M::GnMulticast, ST::PassengerCar, MID::new(mac));
195    mib.itsGnBeaconServiceRetransmitTimer = 0;
196
197    // ── Location Service ─────────────────────────────────────────────────
198    let mut loc_svc = LocationService::new();
199    let gn_gps_rx = loc_svc.subscribe();
200    let ca_gps_rx = loc_svc.subscribe();
201
202    // ── Spawn GN router and BTP router ───────────────────────────────────
203    let (gn_handle, gn_to_ll_rx, gn_to_btp_rx) = GNRouter::spawn(mib, None, None, None);
204    let (btp_handle, btp_to_gn_rx) = BTPRouter::spawn(mib);
205
206    // ── Wire RawLinkLayer ────────────────────────────────────────────────
207    let (ll_to_gn_tx, ll_to_gn_rx) = mpsc::channel::<Vec<u8>>();
208
209    // ── Security middleware: TX path ─────────────────────────────────────
210    //
211    // Intercept packets from GN router → link layer.
212    // Sign the payload (CommonHeader + extended header + data) and wrap
213    // in a secured GN packet with BasicNH::SecuredPacket.
214    let (secured_ll_tx, secured_ll_rx) = mpsc::channel::<Vec<u8>>();
215    let sign_svc_tx = Arc::clone(&sign_service);
216    thread::spawn(move || {
217        while let Ok(packet) = gn_to_ll_rx.recv() {
218            if packet.len() < 4 {
219                let _ = secured_ll_tx.send(packet);
220                continue;
221            }
222            // Parse BasicHeader to check if it's a CommonHeader packet
223            let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
224            let bh = BasicHeader::decode(bh_bytes);
225
226            match bh.nh {
227                BasicNH::CommonHeader if packet.len() > 4 => {
228                    // The payload to sign = everything after BasicHeader
229                    let inner_payload = &packet[4..];
230
231                    let request = SNSignRequest {
232                        tbs_message: inner_payload.to_vec(),
233                        its_aid: ITS_AID_CAM_VAL,
234                        permissions: vec![],
235                        generation_location: None,
236                    };
237
238                    let sec_message = {
239                        let mut svc = sign_svc_tx.lock().unwrap();
240                        svc.sign_request(&request).sec_message
241                    };
242
243                    // Build new packet: BasicHeader(nh=SecuredPacket) + sec_message
244                    let mut new_bh = bh;
245                    new_bh.nh = BasicNH::SecuredPacket;
246                    let secured_packet: Vec<u8> = new_bh
247                        .encode()
248                        .iter()
249                        .copied()
250                        .chain(sec_message.iter().copied())
251                        .collect();
252                    let _ = secured_ll_tx.send(secured_packet);
253                }
254                _ => {
255                    // Pass through (e.g. beacons)
256                    let _ = secured_ll_tx.send(packet);
257                }
258            }
259        }
260    });
261
262    // The link layer reads from secured_ll_rx (post-signing)
263    let raw_ll = RawLinkLayer::new(ll_to_gn_tx, secured_ll_rx, &iface, mac);
264    raw_ll.start();
265
266    // ── Security middleware: RX path ─────────────────────────────────────
267    //
268    // Intercept packets from link layer → GN router.
269    // If BasicNH::SecuredPacket, verify and extract, then forward
270    // with BasicNH::CommonHeader.
271    let gn_h_rx = gn_handle.clone();
272    let sign_svc_rx = Arc::clone(&sign_service);
273    thread::spawn(move || {
274        while let Ok(packet) = ll_to_gn_rx.recv() {
275            if packet.len() < 4 {
276                gn_h_rx.send_incoming_packet(packet);
277                continue;
278            }
279            let bh_bytes: [u8; 4] = packet[0..4].try_into().unwrap();
280            let bh = BasicHeader::decode(bh_bytes);
281
282            match bh.nh {
283                BasicNH::SecuredPacket if packet.len() > 4 => {
284                    let sec_message = &packet[4..];
285                    let request = SNVerifyRequest {
286                        message: sec_message.to_vec(),
287                    };
288
289                    let (confirm, _events) = {
290                        let mut svc = sign_svc_rx.lock().unwrap();
291                        let svc = &mut *svc;
292                        let result = verify_message(&request, &svc.backend, &mut svc.cert_library);
293                        // Process VerifyEvents for P2PCD
294                        for event in &result.1 {
295                            match event {
296                                VerifyEvent::UnknownAt(h8) => {
297                                    svc.notify_unknown_at(h8);
298                                }
299                                VerifyEvent::InlineP2pcdRequest(h3s) => {
300                                    svc.notify_inline_p2pcd_request(h3s);
301                                }
302                                VerifyEvent::ReceivedCaCertificate(cert) => {
303                                    svc.notify_received_ca_certificate(cert.as_ref().clone());
304                                }
305                            }
306                        }
307                        result
308                    };
309
310                    if confirm.report == ReportVerify::Success {
311                        println!(
312                            "[SEC RX] Verified OK — ITS-AID={}, cert={:02x?}",
313                            confirm.its_aid,
314                            &confirm.certificate_id[..],
315                        );
316                        // Rebuild the packet: BasicHeader(nh=CommonHeader) + plain_message
317                        let mut new_bh = bh;
318                        new_bh.nh = BasicNH::CommonHeader;
319                        let plain_packet: Vec<u8> = new_bh
320                            .encode()
321                            .iter()
322                            .copied()
323                            .chain(confirm.plain_message.iter().copied())
324                            .collect();
325                        gn_h_rx.send_incoming_packet(plain_packet);
326                    } else {
327                        eprintln!("[SEC RX] Verification failed: {:?}", confirm.report);
328                    }
329                }
330                _ => {
331                    // Non-secured packet — forward directly
332                    gn_h_rx.send_incoming_packet(packet);
333                }
334            }
335        }
336    });
337
338    // ── GN → BTP ─────────────────────────────────────────────────────────
339    let btp_h1 = btp_handle.clone();
340    thread::spawn(move || {
341        while let Ok(ind) = gn_to_btp_rx.recv() {
342            btp_h1.send_gn_data_indication(ind);
343        }
344    });
345
346    // ── BTP → GN ─────────────────────────────────────────────────────────
347    let gn_h2 = gn_handle.clone();
348    thread::spawn(move || {
349        while let Ok(req) = btp_to_gn_rx.recv() {
350            gn_h2.send_gn_data_request(req);
351        }
352    });
353
354    // ── LocationService → GN position vector ─────────────────────────────
355    let gn_h3 = gn_handle.clone();
356    thread::spawn(move || {
357        while let Ok(fix) = gn_gps_rx.recv() {
358            let mut epv = LongPositionVector::decode([0u8; 24]);
359            epv.update_from_gps(
360                fix.latitude,
361                fix.longitude,
362                fix.speed_mps,
363                fix.heading_deg,
364                fix.pai,
365            );
366            gn_h3.update_position_vector(epv);
367        }
368    });
369
370    // ── Local Dynamic Map ────────────────────────────────────────────────
371    let ldm = LdmFacility::new(415_520_000, 21_340_000, 5_000.0);
372    ldm.if_ldm_3
373        .register_data_provider(RegisterDataProviderReq {
374            application_id: ITS_AID_CAM,
375        });
376    ldm.if_ldm_4
377        .register_data_consumer(RegisterDataConsumerReq {
378            application_id: ITS_AID_CAM,
379        });
380
381    // ── CA Basic Service ─────────────────────────────────────────────────
382    let station_id = u32::from_be_bytes([mac[2], mac[3], mac[4], mac[5]]);
383    let vehicle_data = VehicleData {
384        station_id,
385        ..VehicleData::default()
386    };
387    let (ca_svc, _cam_rx) =
388        CooperativeAwarenessBasicService::new(btp_handle.clone(), vehicle_data, Some(ldm.clone()));
389    ca_svc.start(ca_gps_rx);
390
391    // ── LDM query printer ────────────────────────────────────────────────
392    let ldm_reader = ldm.clone();
393    thread::spawn(move || loop {
394        thread::sleep(Duration::from_secs(1));
395        let resp = ldm_reader
396            .if_ldm_4
397            .request_data_objects(RequestDataObjectsReq {
398                application_id: ITS_AID_CAM,
399                data_object_types: vec![ITS_AID_CAM],
400                filter: None,
401                order: None,
402                max_results: None,
403            });
404        if resp.data_objects.is_empty() {
405            println!("[LDM] No CAM records in store");
406        } else {
407            println!("[LDM] {} CAM record(s):", resp.data_objects.len());
408            for entry in &resp.data_objects {
409                if let ItsDataObject::Cam(cam) = &entry.data_object {
410                    let lat = cam
411                        .cam
412                        .cam_parameters
413                        .basic_container
414                        .reference_position
415                        .latitude
416                        .0 as f64
417                        / 1e7;
418                    let lon = cam
419                        .cam
420                        .cam_parameters
421                        .basic_container
422                        .reference_position
423                        .longitude
424                        .0 as f64
425                        / 1e7;
426                    println!(
427                        "  [LDM CAM] record={:>5} station={:>10}  lat={:.5}  lon={:.5}",
428                        entry.record_id, cam.header.station_id.0, lat, lon,
429                    );
430                }
431            }
432        }
433    });
434
435    // ── GPS publisher (random trajectory — mirrors Python _RandomTrajectoryLocationService) ──
436    //
437    // Updates every 80 ms (below T_CheckCamGen = 100 ms) with a randomly
438    // changing heading (±5–15° per step, always exceeding the 4° condition-1
439    // threshold) and speed random-walking within [5, 20] m/s.  This sustains
440    // 10 Hz CAM generation via the dynamics triggering conditions.
441    thread::sleep(Duration::from_millis(100));
442    println!("Publishing GPS fixes @ 12.5 Hz (80 ms) with random trajectory — Ctrl+C to stop\n");
443
444    use rand::Rng;
445    let mut rng = rand::thread_rng();
446
447    const PERIOD_S: f64 = 0.08; // 80 ms — below T_CheckCamGen
448    const EARTH_R: f64 = 6_371_000.0;
449
450    let mut lat: f64 = 41.386931;
451    let mut lon: f64 = 2.112104;
452    let mut heading: f64 = rng.gen_range(0.0..360.0);
453    let mut speed: f64 = 10.0; // ~36 km/h
454
455    loop {
456        thread::sleep(Duration::from_millis(80));
457
458        // Heading: random signed change guaranteed to exceed the 4° threshold
459        let delta: f64 = rng.gen_range(5.0..15.0) * if rng.gen_bool(0.5) { 1.0 } else { -1.0 };
460        heading = (heading + delta).rem_euclid(360.0);
461
462        // Speed: small random walk within [5, 20] m/s
463        speed = (speed + rng.gen_range(-0.5..0.5)).clamp(5.0, 20.0);
464
465        // Position update (flat-Earth approximation; step sizes < 2 m)
466        let d = speed * PERIOD_S;
467        let heading_r = heading * PI / 180.0;
468        let lat_r = lat * PI / 180.0;
469        lat += (d * heading_r.cos() / EARTH_R) * (180.0 / PI);
470        lon += (d * heading_r.sin() / (EARTH_R * lat_r.cos())) * (180.0 / PI);
471
472        loc_svc.publish(GpsFix {
473            latitude: lat,
474            longitude: lon,
475            altitude_m: 120.0,
476            speed_mps: speed,
477            heading_deg: heading,
478            pai: true,
479        });
480    }
481}

Trait Implementations§

Source§

impl Clone for BasicHeader

Source§

fn clone(&self) -> BasicHeader

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for BasicHeader

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for BasicHeader

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V