• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use std::convert::{TryFrom, TryInto};
16 
17 use log::{debug, error};
18 use pdl_runtime::Packet;
19 use uwb_uci_packets::{
20     parse_diagnostics_ntf, radar_bytes_per_sample_value, RadarDataRcv, RadarSweepDataRaw,
21     UCI_PACKET_HEADER_LEN, UCI_RADAR_SEQUENCE_NUMBER_LEN, UCI_RADAR_TIMESTAMP_LEN,
22     UCI_RADAR_VENDOR_DATA_LEN_LEN,
23 };
24 
25 use crate::error::{Error, Result};
26 use crate::params::fira_app_config_params::UwbAddress;
27 use crate::params::uci_packets::{
28     BitsPerSample, ControleeStatusV1, ControleeStatusV2, CreditAvailability, DataRcvStatusCode,
29     DataTransferNtfStatusCode, DataTransferPhaseConfigUpdateStatusCode, DeviceState,
30     ExtendedAddressDlTdoaRangingMeasurement, ExtendedAddressOwrAoaRangingMeasurement,
31     ExtendedAddressTwoWayRangingMeasurement, RadarDataType, RangingMeasurementType, RawUciMessage,
32     SessionId, SessionState, SessionToken, SessionUpdateControllerMulticastListNtfV1Payload,
33     SessionUpdateControllerMulticastListNtfV2Payload, ShortAddressDlTdoaRangingMeasurement,
34     ShortAddressOwrAoaRangingMeasurement, ShortAddressTwoWayRangingMeasurement, StatusCode,
35     UCIMajorVersion,
36 };
37 
38 /// enum of all UCI notifications with structured fields.
39 #[derive(Debug, Clone, PartialEq)]
40 pub enum UciNotification {
41     /// CoreNotification equivalent.
42     Core(CoreNotification),
43     /// SessionNotification equivalent.
44     Session(SessionNotification),
45     /// UciVendor_X_Notification equivalent.
46     Vendor(RawUciMessage),
47     /// RfTestNotification equivalent
48     RfTest(RfTestNotification),
49 }
50 
51 /// UCI CoreNotification.
52 #[derive(Debug, Clone, PartialEq, Eq)]
53 pub enum CoreNotification {
54     /// DeviceStatusNtf equivalent.
55     DeviceStatus(DeviceState),
56     /// GenericErrorPacket equivalent.
57     GenericError(StatusCode),
58 }
59 
60 /// UCI SessionNotification.
61 #[derive(Debug, Clone, PartialEq)]
62 pub enum SessionNotification {
63     /// SessionStatusNtf equivalent.
64     Status {
65         /// SessionId : u32
66         session_id: SessionId,
67         /// SessionToken : u32
68         session_token: SessionToken,
69         /// uwb_uci_packets::SessionState.
70         session_state: SessionState,
71         /// uwb_uci_packets::Reasoncode.
72         reason_code: u8,
73     },
74     /// SessionUpdateControllerMulticastListNtfV1 equivalent.
75     UpdateControllerMulticastListV1 {
76         /// SessionToken : u32
77         session_token: SessionToken,
78         /// count of controlees: u8
79         remaining_multicast_list_size: usize,
80         /// list of controlees.
81         status_list: Vec<ControleeStatusV1>,
82     },
83     /// SessionUpdateControllerMulticastListNtfV2 equivalent.
84     UpdateControllerMulticastListV2 {
85         /// SessionToken : u32
86         session_token: SessionToken,
87         /// list of controlees.
88         status_list: Vec<ControleeStatusV2>,
89     },
90     /// (Short/Extended)Mac()SessionInfoNtf equivalent
91     SessionInfo(SessionRangeData),
92     /// DataCreditNtf equivalent.
93     DataCredit {
94         /// SessionToken : u32
95         session_token: SessionToken,
96         /// Credit Availability (for sending Data packets on UWB Session)
97         credit_availability: CreditAvailability,
98     },
99     /// DataTransferStatusNtf equivalent.
100     DataTransferStatus {
101         /// SessionToken : u32
102         session_token: SessionToken,
103         /// Sequence Number: u16
104         uci_sequence_number: u16,
105         /// Data Transfer Status Code
106         status: DataTransferNtfStatusCode,
107         /// Transmission count
108         tx_count: u8,
109     },
110     /// SessionDataTransferPhaseConfigNtf equivalent.
111     DataTransferPhaseConfig {
112         /// SessionToken : u32
113         session_token: SessionToken,
114         /// status
115         status: DataTransferPhaseConfigUpdateStatusCode,
116     },
117 }
118 
119 /// UCI RfTest Notification.
120 #[derive(Debug, Clone, PartialEq)]
121 pub enum RfTestNotification {
122     ///TestPeriodicTxNtf equivalent
123     TestPeriodicTxNtf {
124         /// Status
125         status: StatusCode,
126         /// The raw data of the notification message.
127         /// It's not at FiRa specification, only used by vendor's extension.
128         raw_notification_data: Vec<u8>,
129     },
130     /// TestPerRxNtf equivalent
131     TestPerRxNtf(RfTestPerRxData),
132 }
133 
134 /// The session range data.
135 #[derive(Debug, Clone, PartialEq)]
136 pub struct SessionRangeData {
137     /// The sequence counter that starts with 0 when the session is started.
138     pub sequence_number: u32,
139 
140     /// The identifier of the session.
141     pub session_token: SessionToken,
142 
143     /// The current ranging interval setting in the unit of ms.
144     pub current_ranging_interval_ms: u32,
145 
146     /// The ranging measurement type.
147     pub ranging_measurement_type: RangingMeasurementType,
148 
149     /// Hus primary session Session Id
150     pub hus_primary_session_id: SessionToken,
151 
152     /// The ranging measurement data.
153     pub ranging_measurements: RangingMeasurements,
154 
155     /// Indication that a RCR was sent/received in the current ranging round.
156     pub rcr_indicator: u8,
157 
158     /// The raw data of the notification message.
159     /// (b/243555651): It's not at FiRa specification, only used by vendor's extension.
160     pub raw_ranging_data: Vec<u8>,
161 }
162 
163 /// PER RX NTF Data
164 #[derive(Debug, Clone, PartialEq)]
165 pub struct RfTestPerRxData {
166     /// Status
167     pub status: StatusCode,
168 
169     /// Number of RX attempts.
170     pub attempts: u32,
171 
172     /// Number of times signal was detected.
173     pub acq_detect: u32,
174 
175     /// Number of times signal was rejected.
176     pub acq_reject: u32,
177 
178     /// Number of times RX did not go beyound ACQ stage.
179     pub rx_fail: u32,
180 
181     /// Number of times sync CIR ready event was received.
182     pub sync_cir_ready: u32,
183 
184     /// Number of times RX was stuck at either ACQ detect or sync CIR ready.
185     pub sfd_fail: u32,
186 
187     /// Number of times SFD was found.
188     pub sfd_found: u32,
189 
190     /// Number of times PHR decode failed.
191     pub phr_dec_error: u32,
192 
193     /// Number of times PHR bits in error.
194     pub phr_bit_error: u32,
195 
196     /// Number of times payload decode failed.
197     pub psdu_dec_error: u32,
198 
199     /// Number of times payload bits in error.
200     pub psdu_bit_error: u32,
201 
202     /// Number of times STS detection was successful.
203     pub sts_found: u32,
204 
205     /// Number of times end of frame event was triggered.
206     pub eof: u32,
207 
208     /// The raw data of the notification message.
209     /// It's not at FiRa specification, only used by vendor's extension.
210     pub raw_notification_data: Vec<u8>,
211 }
212 
213 /// The ranging measurements.
214 #[derive(Debug, Clone, PartialEq)]
215 pub enum RangingMeasurements {
216     /// A Two-Way measurement with short address.
217     ShortAddressTwoWay(Vec<ShortAddressTwoWayRangingMeasurement>),
218 
219     /// A Two-Way measurement with extended address.
220     ExtendedAddressTwoWay(Vec<ExtendedAddressTwoWayRangingMeasurement>),
221 
222     /// Dl-TDoA measurement with short address.
223     ShortAddressDltdoa(Vec<ShortAddressDlTdoaRangingMeasurement>),
224 
225     /// Dl-TDoA measurement with extended address.
226     ExtendedAddressDltdoa(Vec<ExtendedAddressDlTdoaRangingMeasurement>),
227 
228     /// OWR for AoA measurement with short address.
229     ShortAddressOwrAoa(ShortAddressOwrAoaRangingMeasurement),
230 
231     /// OWR for AoA measurement with extended address.
232     ExtendedAddressOwrAoa(ExtendedAddressOwrAoaRangingMeasurement),
233 }
234 
235 /// The DATA_RCV packet
236 #[derive(Debug, Clone, std::cmp::PartialEq)]
237 pub struct DataRcvNotification {
238     /// The identifier of the session on which data transfer is happening.
239     pub session_token: SessionToken,
240 
241     /// The status of the data rx.
242     pub status: StatusCode,
243 
244     /// The sequence number of the data packet.
245     pub uci_sequence_num: u16,
246 
247     /// MacAddress of the sender of the application data.
248     pub source_address: UwbAddress,
249 
250     /// Application Payload Data
251     pub payload: Vec<u8>,
252 }
253 
254 /// The Radar sweep data struct
255 #[derive(Debug, Clone, std::cmp::PartialEq)]
256 pub struct RadarSweepData {
257     /// Counter of a single radar sweep per receiver. Starting
258     /// with 0 when the radar session is started.
259     pub sequence_number: u32,
260 
261     /// Timestamp when this radar sweep is received. Unit is
262     /// based on the PRF.
263     pub timestamp: u32,
264 
265     /// The radar vendor specific data.
266     pub vendor_specific_data: Vec<u8>,
267 
268     /// The radar sample data.
269     pub sample_data: Vec<u8>,
270 }
271 
272 /// The RADAR_DATA_RCV packet
273 #[derive(Debug, Clone, std::cmp::PartialEq)]
274 pub struct RadarDataRcvNotification {
275     /// The identifier of the session on which radar data transfer is happening.
276     pub session_token: SessionToken,
277 
278     /// The status of the radar data rx.
279     pub status: DataRcvStatusCode,
280 
281     /// The radar data type.
282     pub radar_data_type: RadarDataType,
283 
284     /// The number of sweeps.
285     pub number_of_sweeps: u8,
286 
287     /// Number of samples captured for each radar sweep.
288     pub samples_per_sweep: u8,
289 
290     /// Bits per sample in the radar sweep.
291     pub bits_per_sample: BitsPerSample,
292 
293     /// Defines the start offset with respect to 0cm distance. Unit in samples.
294     pub sweep_offset: u16,
295 
296     /// Radar sweep data.
297     pub sweep_data: Vec<RadarSweepData>,
298 }
299 
300 impl From<&uwb_uci_packets::RadarSweepDataRaw> for RadarSweepData {
from(evt: &uwb_uci_packets::RadarSweepDataRaw) -> Self301     fn from(evt: &uwb_uci_packets::RadarSweepDataRaw) -> Self {
302         Self {
303             sequence_number: evt.sequence_number,
304             timestamp: evt.timestamp,
305             vendor_specific_data: evt.vendor_specific_data.clone(),
306             sample_data: evt.sample_data.clone(),
307         }
308     }
309 }
310 
311 impl TryFrom<uwb_uci_packets::UciDataPacket> for RadarDataRcvNotification {
312     type Error = Error;
try_from(evt: uwb_uci_packets::UciDataPacket) -> std::result::Result<Self, Self::Error>313     fn try_from(evt: uwb_uci_packets::UciDataPacket) -> std::result::Result<Self, Self::Error> {
314         match evt.specialize() {
315             uwb_uci_packets::UciDataPacketChild::RadarDataRcv(evt) => parse_radar_data(evt),
316             _ => Err(Error::Unknown),
317         }
318     }
319 }
320 
parse_radar_data(data: RadarDataRcv) -> Result<RadarDataRcvNotification>321 fn parse_radar_data(data: RadarDataRcv) -> Result<RadarDataRcvNotification> {
322     let session_token = data.get_session_handle();
323     let status = data.get_status();
324     let radar_data_type = data.get_radar_data_type();
325     let number_of_sweeps = data.get_number_of_sweeps();
326     let samples_per_sweep = data.get_samples_per_sweep();
327     let bits_per_sample = data.get_bits_per_sample();
328     let bytes_per_sample_value = radar_bytes_per_sample_value(bits_per_sample);
329     let sweep_offset = data.get_sweep_offset();
330 
331     Ok(RadarDataRcvNotification {
332         session_token,
333         status,
334         radar_data_type,
335         number_of_sweeps,
336         samples_per_sweep,
337         bits_per_sample,
338         sweep_offset,
339         sweep_data: parse_radar_sweep_data(
340             number_of_sweeps,
341             samples_per_sweep,
342             bytes_per_sample_value,
343             data.get_sweep_data().clone(),
344         )?,
345     })
346 }
347 
parse_radar_sweep_data( number_of_sweeps: u8, samples_per_sweep: u8, bytes_per_sample_value: u8, data: Vec<u8>, ) -> Result<Vec<RadarSweepData>>348 fn parse_radar_sweep_data(
349     number_of_sweeps: u8,
350     samples_per_sweep: u8,
351     bytes_per_sample_value: u8,
352     data: Vec<u8>,
353 ) -> Result<Vec<RadarSweepData>> {
354     let mut radar_sweep_data: Vec<RadarSweepData> = Vec::new();
355     let mut sweep_data_cursor = 0;
356     for _ in 0..number_of_sweeps {
357         let vendor_data_len_index =
358             sweep_data_cursor + UCI_RADAR_SEQUENCE_NUMBER_LEN + UCI_RADAR_TIMESTAMP_LEN;
359         if data.len() <= vendor_data_len_index {
360             error!("Invalid radar sweep data length for vendor, data: {:?}", &data);
361             return Err(Error::BadParameters);
362         }
363         let vendor_specific_data_len = data[vendor_data_len_index] as usize;
364         let sweep_data_len = UCI_RADAR_SEQUENCE_NUMBER_LEN
365             + UCI_RADAR_TIMESTAMP_LEN
366             + UCI_RADAR_VENDOR_DATA_LEN_LEN
367             + vendor_specific_data_len
368             + samples_per_sweep as usize * bytes_per_sample_value as usize;
369         if data.len() < sweep_data_cursor + sweep_data_len {
370             error!("Invalid radar sweep data length, data: {:?}", &data);
371             return Err(Error::BadParameters);
372         }
373         radar_sweep_data.push(
374             (&RadarSweepDataRaw::parse(
375                 &data[sweep_data_cursor..sweep_data_cursor + sweep_data_len],
376             )
377             .map_err(|e| {
378                 error!("Failed to parse raw Radar Sweep Data {:?}, data: {:?}", e, &data);
379                 Error::BadParameters
380             })?)
381                 .into(),
382         );
383 
384         sweep_data_cursor += sweep_data_len;
385     }
386 
387     Ok(radar_sweep_data)
388 }
389 
390 impl TryFrom<uwb_uci_packets::UciDataPacket> for DataRcvNotification {
391     type Error = Error;
try_from(evt: uwb_uci_packets::UciDataPacket) -> std::result::Result<Self, Self::Error>392     fn try_from(evt: uwb_uci_packets::UciDataPacket) -> std::result::Result<Self, Self::Error> {
393         match evt.specialize() {
394             uwb_uci_packets::UciDataPacketChild::UciDataRcv(evt) => Ok(DataRcvNotification {
395                 session_token: evt.get_session_token(),
396                 status: evt.get_status(),
397                 uci_sequence_num: evt.get_uci_sequence_number(),
398                 source_address: UwbAddress::Extended(evt.get_source_mac_address().to_le_bytes()),
399                 payload: evt.get_data().to_vec(),
400             }),
401             _ => Err(Error::Unknown),
402         }
403     }
404 }
405 
406 impl UciNotification {
need_retry(&self) -> bool407     pub(crate) fn need_retry(&self) -> bool {
408         matches!(
409             self,
410             Self::Core(CoreNotification::GenericError(StatusCode::UciStatusCommandRetry))
411         )
412     }
413 }
414 
415 impl TryFrom<(uwb_uci_packets::UciNotification, UCIMajorVersion, bool)> for UciNotification {
416     type Error = Error;
try_from( pair: (uwb_uci_packets::UciNotification, UCIMajorVersion, bool), ) -> std::result::Result<Self, Self::Error>417     fn try_from(
418         pair: (uwb_uci_packets::UciNotification, UCIMajorVersion, bool),
419     ) -> std::result::Result<Self, Self::Error> {
420         use uwb_uci_packets::UciNotificationChild;
421         let evt = pair.0;
422         let uci_fira_major_ver = pair.1;
423         let is_multicast_list_ntf_v2_supported = pair.2;
424 
425         match evt.specialize() {
426             UciNotificationChild::CoreNotification(evt) => Ok(Self::Core(evt.try_into()?)),
427             UciNotificationChild::SessionConfigNotification(evt) => Ok(Self::Session(
428                 (evt, uci_fira_major_ver, is_multicast_list_ntf_v2_supported).try_into()?,
429             )),
430             UciNotificationChild::SessionControlNotification(evt) => {
431                 Ok(Self::Session(evt.try_into()?))
432             }
433             UciNotificationChild::AndroidNotification(evt) => evt.try_into(),
434             UciNotificationChild::UciVendor_9_Notification(evt) => vendor_notification(evt.into()),
435             UciNotificationChild::UciVendor_A_Notification(evt) => vendor_notification(evt.into()),
436             UciNotificationChild::UciVendor_B_Notification(evt) => vendor_notification(evt.into()),
437             UciNotificationChild::UciVendor_E_Notification(evt) => vendor_notification(evt.into()),
438             UciNotificationChild::UciVendor_F_Notification(evt) => vendor_notification(evt.into()),
439             UciNotificationChild::TestNotification(evt) => Ok(Self::RfTest(evt.try_into()?)),
440             _ => {
441                 error!("Unknown UciNotification: {:?}", evt);
442                 Err(Error::Unknown)
443             }
444         }
445     }
446 }
447 
448 impl TryFrom<uwb_uci_packets::CoreNotification> for CoreNotification {
449     type Error = Error;
try_from(evt: uwb_uci_packets::CoreNotification) -> std::result::Result<Self, Self::Error>450     fn try_from(evt: uwb_uci_packets::CoreNotification) -> std::result::Result<Self, Self::Error> {
451         use uwb_uci_packets::CoreNotificationChild;
452         match evt.specialize() {
453             CoreNotificationChild::DeviceStatusNtf(evt) => {
454                 Ok(Self::DeviceStatus(evt.get_device_state()))
455             }
456             CoreNotificationChild::GenericError(evt) => Ok(Self::GenericError(evt.get_status())),
457             _ => {
458                 error!("Unknown CoreNotification: {:?}", evt);
459                 Err(Error::Unknown)
460             }
461         }
462     }
463 }
464 
465 impl TryFrom<(uwb_uci_packets::SessionConfigNotification, UCIMajorVersion, bool)>
466     for SessionNotification
467 {
468     type Error = Error;
try_from( pair: (uwb_uci_packets::SessionConfigNotification, UCIMajorVersion, bool), ) -> std::result::Result<Self, Self::Error>469     fn try_from(
470         pair: (uwb_uci_packets::SessionConfigNotification, UCIMajorVersion, bool),
471     ) -> std::result::Result<Self, Self::Error> {
472         use uwb_uci_packets::SessionConfigNotificationChild;
473         let evt = pair.0;
474         let uci_fira_major_ver = pair.1;
475         let is_multicast_list_ntf_v2_supported = pair.2;
476         match evt.specialize() {
477             SessionConfigNotificationChild::SessionStatusNtf(evt) => Ok(Self::Status {
478                 //no sessionId recieved, assign from sessionIdToToken map in uci_manager
479                 session_id: 0,
480                 session_token: evt.get_session_token(),
481                 session_state: evt.get_session_state(),
482                 reason_code: evt.get_reason_code(),
483             }),
484             SessionConfigNotificationChild::SessionUpdateControllerMulticastListNtf(evt)
485                 if uci_fira_major_ver == UCIMajorVersion::V1
486                     || !is_multicast_list_ntf_v2_supported =>
487             {
488                 let payload = evt.get_payload();
489                 let multicast_update_list_payload_v1 =
490                     SessionUpdateControllerMulticastListNtfV1Payload::parse(payload).map_err(
491                         |e| {
492                             error!(
493                                 "Failed to parse Multicast list ntf v1 {:?}, payload: {:?}",
494                                 e, &payload
495                             );
496                             Error::BadParameters
497                         },
498                     )?;
499                 Ok(Self::UpdateControllerMulticastListV1 {
500                     session_token: evt.get_session_token(),
501                     remaining_multicast_list_size: multicast_update_list_payload_v1
502                         .remaining_multicast_list_size
503                         as usize,
504                     status_list: multicast_update_list_payload_v1.controlee_status,
505                 })
506             }
507             SessionConfigNotificationChild::SessionUpdateControllerMulticastListNtf(evt)
508                 if uci_fira_major_ver >= UCIMajorVersion::V2 =>
509             {
510                 let payload = evt.get_payload();
511                 let multicast_update_list_payload_v2 =
512                     SessionUpdateControllerMulticastListNtfV2Payload::parse(payload).map_err(
513                         |e| {
514                             error!(
515                                 "Failed to parse Multicast list ntf v2 {:?}, payload: {:?}",
516                                 e, &payload
517                             );
518                             Error::BadParameters
519                         },
520                     )?;
521                 Ok(Self::UpdateControllerMulticastListV2 {
522                     session_token: evt.get_session_token(),
523                     status_list: multicast_update_list_payload_v2.controlee_status,
524                 })
525             }
526             SessionConfigNotificationChild::SessionDataTransferPhaseConfigNtf(evt) => {
527                 Ok(Self::DataTransferPhaseConfig {
528                     session_token: evt.get_session_token(),
529                     status: evt.get_status(),
530                 })
531             }
532             _ => {
533                 error!("Unknown SessionConfigNotification: {:?}", evt);
534                 Err(Error::Unknown)
535             }
536         }
537     }
538 }
539 
540 impl TryFrom<uwb_uci_packets::SessionControlNotification> for SessionNotification {
541     type Error = Error;
try_from( evt: uwb_uci_packets::SessionControlNotification, ) -> std::result::Result<Self, Self::Error>542     fn try_from(
543         evt: uwb_uci_packets::SessionControlNotification,
544     ) -> std::result::Result<Self, Self::Error> {
545         use uwb_uci_packets::SessionControlNotificationChild;
546         match evt.specialize() {
547             SessionControlNotificationChild::SessionInfoNtf(evt) => evt.try_into(),
548             SessionControlNotificationChild::DataCreditNtf(evt) => Ok(Self::DataCredit {
549                 session_token: evt.get_session_token(),
550                 credit_availability: evt.get_credit_availability(),
551             }),
552             SessionControlNotificationChild::DataTransferStatusNtf(evt) => {
553                 Ok(Self::DataTransferStatus {
554                     session_token: evt.get_session_token(),
555                     uci_sequence_number: evt.get_uci_sequence_number(),
556                     status: evt.get_status(),
557                     tx_count: evt.get_tx_count(),
558                 })
559             }
560             _ => {
561                 error!("Unknown SessionControlNotification: {:?}", evt);
562                 Err(Error::Unknown)
563             }
564         }
565     }
566 }
567 
568 impl TryFrom<uwb_uci_packets::SessionInfoNtf> for SessionNotification {
569     type Error = Error;
try_from(evt: uwb_uci_packets::SessionInfoNtf) -> std::result::Result<Self, Self::Error>570     fn try_from(evt: uwb_uci_packets::SessionInfoNtf) -> std::result::Result<Self, Self::Error> {
571         let raw_ranging_data = evt.encode_to_bytes().unwrap()[UCI_PACKET_HEADER_LEN..].to_vec();
572         use uwb_uci_packets::SessionInfoNtfChild;
573         let ranging_measurements = match evt.specialize() {
574             SessionInfoNtfChild::ShortMacTwoWaySessionInfoNtf(evt) => {
575                 RangingMeasurements::ShortAddressTwoWay(
576                     evt.get_two_way_ranging_measurements().clone(),
577                 )
578             }
579             SessionInfoNtfChild::ExtendedMacTwoWaySessionInfoNtf(evt) => {
580                 RangingMeasurements::ExtendedAddressTwoWay(
581                     evt.get_two_way_ranging_measurements().clone(),
582                 )
583             }
584             SessionInfoNtfChild::ShortMacOwrAoaSessionInfoNtf(evt) => {
585                 if evt.get_owr_aoa_ranging_measurements().clone().len() == 1 {
586                     RangingMeasurements::ShortAddressOwrAoa(
587                         match evt.get_owr_aoa_ranging_measurements().clone().pop() {
588                             Some(r) => r,
589                             None => {
590                                 error!(
591                                     "Unable to parse ShortAddress OwrAoA measurement: {:?}",
592                                     evt
593                                 );
594                                 return Err(Error::BadParameters);
595                             }
596                         },
597                     )
598                 } else {
599                     error!("Wrong count of OwrAoA ranging measurements {:?}", evt);
600                     return Err(Error::BadParameters);
601                 }
602             }
603             SessionInfoNtfChild::ExtendedMacOwrAoaSessionInfoNtf(evt) => {
604                 if evt.get_owr_aoa_ranging_measurements().clone().len() == 1 {
605                     RangingMeasurements::ExtendedAddressOwrAoa(
606                         match evt.get_owr_aoa_ranging_measurements().clone().pop() {
607                             Some(r) => r,
608                             None => {
609                                 error!(
610                                     "Unable to parse ExtendedAddress OwrAoA measurement: {:?}",
611                                     evt
612                                 );
613                                 return Err(Error::BadParameters);
614                             }
615                         },
616                     )
617                 } else {
618                     error!("Wrong count of OwrAoA ranging measurements {:?}", evt);
619                     return Err(Error::BadParameters);
620                 }
621             }
622             SessionInfoNtfChild::ShortMacDlTDoASessionInfoNtf(evt) => {
623                 match ShortAddressDlTdoaRangingMeasurement::parse(
624                     evt.get_dl_tdoa_measurements(),
625                     evt.get_no_of_ranging_measurements(),
626                 ) {
627                     Some(v) => {
628                         if v.len() == evt.get_no_of_ranging_measurements().into() {
629                             RangingMeasurements::ShortAddressDltdoa(v)
630                         } else {
631                             error!("Wrong count of ranging measurements {:?}", evt);
632                             return Err(Error::BadParameters);
633                         }
634                     }
635                     None => return Err(Error::BadParameters),
636                 }
637             }
638             SessionInfoNtfChild::ExtendedMacDlTDoASessionInfoNtf(evt) => {
639                 match ExtendedAddressDlTdoaRangingMeasurement::parse(
640                     evt.get_dl_tdoa_measurements(),
641                     evt.get_no_of_ranging_measurements(),
642                 ) {
643                     Some(v) => {
644                         if v.len() == evt.get_no_of_ranging_measurements().into() {
645                             RangingMeasurements::ExtendedAddressDltdoa(v)
646                         } else {
647                             error!("Wrong count of ranging measurements {:?}", evt);
648                             return Err(Error::BadParameters);
649                         }
650                     }
651                     None => return Err(Error::BadParameters),
652                 }
653             }
654             _ => {
655                 error!("Unknown SessionInfoNtf: {:?}", evt);
656                 return Err(Error::Unknown);
657             }
658         };
659         Ok(Self::SessionInfo(SessionRangeData {
660             sequence_number: evt.get_sequence_number(),
661             session_token: evt.get_session_token(),
662             current_ranging_interval_ms: evt.get_current_ranging_interval(),
663             ranging_measurement_type: evt.get_ranging_measurement_type(),
664             hus_primary_session_id: evt.get_hus_primary_session_id(),
665             ranging_measurements,
666             rcr_indicator: evt.get_rcr_indicator(),
667             raw_ranging_data,
668         }))
669     }
670 }
671 
672 impl TryFrom<uwb_uci_packets::AndroidNotification> for UciNotification {
673     type Error = Error;
try_from( evt: uwb_uci_packets::AndroidNotification, ) -> std::result::Result<Self, Self::Error>674     fn try_from(
675         evt: uwb_uci_packets::AndroidNotification,
676     ) -> std::result::Result<Self, Self::Error> {
677         use uwb_uci_packets::AndroidNotificationChild;
678 
679         // (b/241336806): Currently we don't process the diagnostic packet, just log it only.
680         if let AndroidNotificationChild::AndroidRangeDiagnosticsNtf(ntf) = evt.specialize() {
681             debug!("Received diagnostic packet: {:?}", parse_diagnostics_ntf(ntf));
682         } else {
683             error!("Received unknown AndroidNotification: {:?}", evt);
684         }
685         Err(Error::Unknown)
686     }
687 }
688 
vendor_notification(evt: uwb_uci_packets::UciNotification) -> Result<UciNotification>689 fn vendor_notification(evt: uwb_uci_packets::UciNotification) -> Result<UciNotification> {
690     Ok(UciNotification::Vendor(RawUciMessage {
691         gid: evt.get_group_id().into(),
692         oid: evt.get_opcode().into(),
693         payload: get_vendor_uci_payload(evt)?,
694     }))
695 }
696 
697 impl TryFrom<uwb_uci_packets::TestNotification> for RfTestNotification {
698     type Error = Error;
try_from(evt: uwb_uci_packets::TestNotification) -> std::result::Result<Self, Self::Error>699     fn try_from(evt: uwb_uci_packets::TestNotification) -> std::result::Result<Self, Self::Error> {
700         use uwb_uci_packets::TestNotificationChild;
701         let raw_ntf_data = evt.clone().encode_to_bytes().unwrap()[UCI_PACKET_HEADER_LEN..].to_vec();
702         match evt.specialize() {
703             TestNotificationChild::TestPeriodicTxNtf(evt) => Ok(Self::TestPeriodicTxNtf {
704                 status: evt.get_status(),
705                 raw_notification_data: raw_ntf_data,
706             }),
707             TestNotificationChild::TestPerRxNtf(evt) => Ok(Self::TestPerRxNtf (RfTestPerRxData {
708                 status: evt.get_status(),
709                 attempts: evt.get_attempts(),
710                 acq_detect: evt.get_acq_detect(),
711                 acq_reject: evt.get_acq_reject(),
712                 rx_fail: evt.get_rx_fail(),
713                 sync_cir_ready: evt.get_sync_cir_ready(),
714                 sfd_fail: evt.get_sfd_fail(),
715                 sfd_found: evt.get_sfd_found(),
716                 phr_dec_error: evt.get_phr_dec_error(),
717                 phr_bit_error: evt.get_phr_bit_error(),
718                 psdu_dec_error: evt.get_psdu_dec_error(),
719                 psdu_bit_error: evt.get_psdu_bit_error(),
720                 sts_found: evt.get_sts_found(),
721                 eof: evt.get_eof(),
722                 raw_notification_data: raw_ntf_data,
723             })),
724             _ => {
725                 error!("Unknown RfTestNotification: {:?}", evt);
726                 Err(Error::Unknown)
727             }
728         }
729     }
730 }
731 
get_vendor_uci_payload(evt: uwb_uci_packets::UciNotification) -> Result<Vec<u8>>732 fn get_vendor_uci_payload(evt: uwb_uci_packets::UciNotification) -> Result<Vec<u8>> {
733     match evt.specialize() {
734         uwb_uci_packets::UciNotificationChild::UciVendor_9_Notification(evt) => {
735             match evt.specialize() {
736                 uwb_uci_packets::UciVendor_9_NotificationChild::Payload(payload) => {
737                     Ok(payload.to_vec())
738                 }
739                 uwb_uci_packets::UciVendor_9_NotificationChild::None => Ok(Vec::new()),
740             }
741         }
742         uwb_uci_packets::UciNotificationChild::UciVendor_A_Notification(evt) => {
743             match evt.specialize() {
744                 uwb_uci_packets::UciVendor_A_NotificationChild::Payload(payload) => {
745                     Ok(payload.to_vec())
746                 }
747                 uwb_uci_packets::UciVendor_A_NotificationChild::None => Ok(Vec::new()),
748             }
749         }
750         uwb_uci_packets::UciNotificationChild::UciVendor_B_Notification(evt) => {
751             match evt.specialize() {
752                 uwb_uci_packets::UciVendor_B_NotificationChild::Payload(payload) => {
753                     Ok(payload.to_vec())
754                 }
755                 uwb_uci_packets::UciVendor_B_NotificationChild::None => Ok(Vec::new()),
756             }
757         }
758         uwb_uci_packets::UciNotificationChild::UciVendor_E_Notification(evt) => {
759             match evt.specialize() {
760                 uwb_uci_packets::UciVendor_E_NotificationChild::Payload(payload) => {
761                     Ok(payload.to_vec())
762                 }
763                 uwb_uci_packets::UciVendor_E_NotificationChild::None => Ok(Vec::new()),
764             }
765         }
766         uwb_uci_packets::UciNotificationChild::UciVendor_F_Notification(evt) => {
767             match evt.specialize() {
768                 uwb_uci_packets::UciVendor_F_NotificationChild::Payload(payload) => {
769                     Ok(payload.to_vec())
770                 }
771                 uwb_uci_packets::UciVendor_F_NotificationChild::None => Ok(Vec::new()),
772             }
773         }
774         _ => {
775             error!("Unknown UciVendor packet: {:?}", evt);
776             Err(Error::Unknown)
777         }
778     }
779 }
780 #[cfg(test)]
781 mod tests {
782     use super::*;
783     use bytes::{BufMut, BytesMut};
784 
785     #[test]
test_ranging_measurements_trait()786     fn test_ranging_measurements_trait() {
787         let empty_short_ranging_measurements = RangingMeasurements::ShortAddressTwoWay(vec![]);
788         assert_eq!(empty_short_ranging_measurements, empty_short_ranging_measurements);
789         let extended_ranging_measurements = RangingMeasurements::ExtendedAddressTwoWay(vec![
790             ExtendedAddressTwoWayRangingMeasurement {
791                 mac_address: 0x1234_5678_90ab,
792                 status: StatusCode::UciStatusOk,
793                 nlos: 0,
794                 distance: 4,
795                 aoa_azimuth: 5,
796                 aoa_azimuth_fom: 6,
797                 aoa_elevation: 7,
798                 aoa_elevation_fom: 8,
799                 aoa_destination_azimuth: 9,
800                 aoa_destination_azimuth_fom: 10,
801                 aoa_destination_elevation: 11,
802                 aoa_destination_elevation_fom: 12,
803                 slot_index: 0,
804                 rssi: u8::MAX,
805             },
806         ]);
807         assert_eq!(extended_ranging_measurements, extended_ranging_measurements.clone());
808         let empty_extended_ranging_measurements =
809             RangingMeasurements::ExtendedAddressTwoWay(vec![]);
810         assert_eq!(empty_short_ranging_measurements, empty_short_ranging_measurements);
811         //short and extended measurements are unequal even if both are empty:
812         assert_ne!(empty_short_ranging_measurements, empty_extended_ranging_measurements);
813     }
814     #[test]
test_core_notification_casting_from_generic_error()815     fn test_core_notification_casting_from_generic_error() {
816         let generic_error_packet = uwb_uci_packets::GenericErrorBuilder {
817             status: uwb_uci_packets::StatusCode::UciStatusRejected,
818         }
819         .build();
820         let core_notification =
821             uwb_uci_packets::CoreNotification::try_from(generic_error_packet).unwrap();
822         let core_notification = CoreNotification::try_from(core_notification).unwrap();
823         let uci_notification_from_generic_error = UciNotification::Core(core_notification);
824         assert_eq!(
825             uci_notification_from_generic_error,
826             UciNotification::Core(CoreNotification::GenericError(
827                 uwb_uci_packets::StatusCode::UciStatusRejected
828             ))
829         );
830     }
831     #[test]
test_core_notification_casting_from_device_status_ntf()832     fn test_core_notification_casting_from_device_status_ntf() {
833         let device_status_ntf_packet = uwb_uci_packets::DeviceStatusNtfBuilder {
834             device_state: uwb_uci_packets::DeviceState::DeviceStateActive,
835         }
836         .build();
837         let core_notification =
838             uwb_uci_packets::CoreNotification::try_from(device_status_ntf_packet).unwrap();
839         let uci_notification = CoreNotification::try_from(core_notification).unwrap();
840         let uci_notification_from_device_status_ntf = UciNotification::Core(uci_notification);
841         assert_eq!(
842             uci_notification_from_device_status_ntf,
843             UciNotification::Core(CoreNotification::DeviceStatus(
844                 uwb_uci_packets::DeviceState::DeviceStateActive
845             ))
846         );
847     }
848 
849     #[test]
test_session_notification_casting_from_extended_mac_two_way_session_info_ntf()850     fn test_session_notification_casting_from_extended_mac_two_way_session_info_ntf() {
851         let extended_measurement = uwb_uci_packets::ExtendedAddressTwoWayRangingMeasurement {
852             mac_address: 0x1234_5678_90ab,
853             status: StatusCode::UciStatusOk,
854             nlos: 0,
855             distance: 4,
856             aoa_azimuth: 5,
857             aoa_azimuth_fom: 6,
858             aoa_elevation: 7,
859             aoa_elevation_fom: 8,
860             aoa_destination_azimuth: 9,
861             aoa_destination_azimuth_fom: 10,
862             aoa_destination_elevation: 11,
863             aoa_destination_elevation_fom: 12,
864             slot_index: 0,
865             rssi: u8::MAX,
866         };
867         let extended_two_way_session_info_ntf =
868             uwb_uci_packets::ExtendedMacTwoWaySessionInfoNtfBuilder {
869                 sequence_number: 0x10,
870                 session_token: 0x11,
871                 rcr_indicator: 0x12,
872                 current_ranging_interval: 0x13,
873                 hus_primary_session_id: 0x00,
874                 two_way_ranging_measurements: vec![extended_measurement.clone()],
875                 vendor_data: vec![],
876             }
877             .build();
878         let raw_ranging_data = extended_two_way_session_info_ntf.encode_to_bytes().unwrap()
879             [UCI_PACKET_HEADER_LEN..]
880             .to_vec();
881         let range_notification =
882             uwb_uci_packets::SessionInfoNtf::try_from(extended_two_way_session_info_ntf).unwrap();
883         let session_notification = SessionNotification::try_from(range_notification).unwrap();
884         let uci_notification_from_extended_two_way_session_info_ntf =
885             UciNotification::Session(session_notification);
886         assert_eq!(
887             uci_notification_from_extended_two_way_session_info_ntf,
888             UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData {
889                 sequence_number: 0x10,
890                 session_token: 0x11,
891                 ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::TwoWay,
892                 hus_primary_session_id: 0x00,
893                 current_ranging_interval_ms: 0x13,
894                 ranging_measurements: RangingMeasurements::ExtendedAddressTwoWay(vec![
895                     extended_measurement
896                 ]),
897                 rcr_indicator: 0x12,
898                 raw_ranging_data,
899             }))
900         );
901     }
902 
903     #[test]
test_session_notification_casting_from_short_mac_two_way_session_info_ntf()904     fn test_session_notification_casting_from_short_mac_two_way_session_info_ntf() {
905         let short_measurement = uwb_uci_packets::ShortAddressTwoWayRangingMeasurement {
906             mac_address: 0x1234,
907             status: StatusCode::UciStatusOk,
908             nlos: 0,
909             distance: 4,
910             aoa_azimuth: 5,
911             aoa_azimuth_fom: 6,
912             aoa_elevation: 7,
913             aoa_elevation_fom: 8,
914             aoa_destination_azimuth: 9,
915             aoa_destination_azimuth_fom: 10,
916             aoa_destination_elevation: 11,
917             aoa_destination_elevation_fom: 12,
918             slot_index: 0,
919             rssi: u8::MAX,
920         };
921         let short_two_way_session_info_ntf = uwb_uci_packets::ShortMacTwoWaySessionInfoNtfBuilder {
922             sequence_number: 0x10,
923             session_token: 0x11,
924             rcr_indicator: 0x12,
925             current_ranging_interval: 0x13,
926             hus_primary_session_id: 0x00,
927             two_way_ranging_measurements: vec![short_measurement.clone()],
928             vendor_data: vec![0x02, 0x01],
929         }
930         .build();
931         let raw_ranging_data = short_two_way_session_info_ntf.encode_to_bytes().unwrap()
932             [UCI_PACKET_HEADER_LEN..]
933             .to_vec();
934         let range_notification =
935             uwb_uci_packets::SessionInfoNtf::try_from(short_two_way_session_info_ntf).unwrap();
936         let session_notification = SessionNotification::try_from(range_notification).unwrap();
937         let uci_notification_from_short_two_way_session_info_ntf =
938             UciNotification::Session(session_notification);
939         assert_eq!(
940             uci_notification_from_short_two_way_session_info_ntf,
941             UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData {
942                 sequence_number: 0x10,
943                 session_token: 0x11,
944                 ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::TwoWay,
945                 hus_primary_session_id: 0x00,
946                 current_ranging_interval_ms: 0x13,
947                 ranging_measurements: RangingMeasurements::ShortAddressTwoWay(vec![
948                     short_measurement
949                 ]),
950                 rcr_indicator: 0x12,
951                 raw_ranging_data,
952             }))
953         );
954     }
955 
956     #[test]
test_session_notification_casting_from_extended_mac_owr_aoa_session_info_ntf()957     fn test_session_notification_casting_from_extended_mac_owr_aoa_session_info_ntf() {
958         let extended_measurement = uwb_uci_packets::ExtendedAddressOwrAoaRangingMeasurement {
959             mac_address: 0x1234_5678_90ab,
960             status: StatusCode::UciStatusOk,
961             nlos: 0,
962             frame_sequence_number: 1,
963             block_index: 1,
964             aoa_azimuth: 5,
965             aoa_azimuth_fom: 6,
966             aoa_elevation: 7,
967             aoa_elevation_fom: 8,
968         };
969         let extended_owr_aoa_session_info_ntf =
970             uwb_uci_packets::ExtendedMacOwrAoaSessionInfoNtfBuilder {
971                 sequence_number: 0x10,
972                 session_token: 0x11,
973                 rcr_indicator: 0x12,
974                 current_ranging_interval: 0x13,
975                 hus_primary_session_id: 0x00,
976                 owr_aoa_ranging_measurements: vec![extended_measurement.clone()],
977                 vendor_data: vec![],
978             }
979             .build();
980         let raw_ranging_data = extended_owr_aoa_session_info_ntf.encode_to_bytes().unwrap()
981             [UCI_PACKET_HEADER_LEN..]
982             .to_vec();
983         let range_notification =
984             uwb_uci_packets::SessionInfoNtf::try_from(extended_owr_aoa_session_info_ntf).unwrap();
985         let session_notification = SessionNotification::try_from(range_notification).unwrap();
986         let uci_notification_from_extended_owr_aoa_session_info_ntf =
987             UciNotification::Session(session_notification);
988         assert_eq!(
989             uci_notification_from_extended_owr_aoa_session_info_ntf,
990             UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData {
991                 sequence_number: 0x10,
992                 session_token: 0x11,
993                 ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::OwrAoa,
994                 hus_primary_session_id: 0x00,
995                 current_ranging_interval_ms: 0x13,
996                 ranging_measurements: RangingMeasurements::ExtendedAddressOwrAoa(
997                     extended_measurement
998                 ),
999                 rcr_indicator: 0x12,
1000                 raw_ranging_data,
1001             }))
1002         );
1003     }
1004 
1005     #[test]
test_session_notification_casting_from_short_mac_owr_aoa_session_info_ntf()1006     fn test_session_notification_casting_from_short_mac_owr_aoa_session_info_ntf() {
1007         let short_measurement = uwb_uci_packets::ShortAddressOwrAoaRangingMeasurement {
1008             mac_address: 0x1234,
1009             status: StatusCode::UciStatusOk,
1010             nlos: 0,
1011             frame_sequence_number: 1,
1012             block_index: 1,
1013             aoa_azimuth: 5,
1014             aoa_azimuth_fom: 6,
1015             aoa_elevation: 7,
1016             aoa_elevation_fom: 8,
1017         };
1018         let short_owr_aoa_session_info_ntf = uwb_uci_packets::ShortMacOwrAoaSessionInfoNtfBuilder {
1019             sequence_number: 0x10,
1020             session_token: 0x11,
1021             rcr_indicator: 0x12,
1022             current_ranging_interval: 0x13,
1023             hus_primary_session_id: 0x00,
1024             owr_aoa_ranging_measurements: vec![short_measurement.clone()],
1025             vendor_data: vec![],
1026         }
1027         .build();
1028         let raw_ranging_data = short_owr_aoa_session_info_ntf.encode_to_bytes().unwrap()
1029             [UCI_PACKET_HEADER_LEN..]
1030             .to_vec();
1031         let range_notification =
1032             uwb_uci_packets::SessionInfoNtf::try_from(short_owr_aoa_session_info_ntf).unwrap();
1033         let session_notification = SessionNotification::try_from(range_notification).unwrap();
1034         let uci_notification_from_short_owr_aoa_session_info_ntf =
1035             UciNotification::Session(session_notification);
1036         assert_eq!(
1037             uci_notification_from_short_owr_aoa_session_info_ntf,
1038             UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData {
1039                 sequence_number: 0x10,
1040                 session_token: 0x11,
1041                 ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::OwrAoa,
1042                 hus_primary_session_id: 0x00,
1043                 current_ranging_interval_ms: 0x13,
1044                 ranging_measurements: RangingMeasurements::ShortAddressOwrAoa(short_measurement),
1045                 rcr_indicator: 0x12,
1046                 raw_ranging_data,
1047             }))
1048         );
1049     }
1050 
1051     #[test]
test_session_notification_casting_from_session_status_ntf()1052     fn test_session_notification_casting_from_session_status_ntf() {
1053         let session_status_ntf = uwb_uci_packets::SessionStatusNtfBuilder {
1054             session_token: 0x20,
1055             session_state: uwb_uci_packets::SessionState::SessionStateActive,
1056             reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands
1057                 .into(),
1058         }
1059         .build();
1060         let session_notification_packet =
1061             uwb_uci_packets::SessionConfigNotification::try_from(session_status_ntf).unwrap();
1062         let uci_fira_major_version = UCIMajorVersion::V1;
1063         let session_notification = SessionNotification::try_from((
1064             session_notification_packet,
1065             uci_fira_major_version,
1066             false,
1067         ))
1068         .unwrap();
1069         let uci_notification_from_session_status_ntf =
1070             UciNotification::Session(session_notification);
1071         assert_eq!(
1072             uci_notification_from_session_status_ntf,
1073             UciNotification::Session(SessionNotification::Status {
1074                 session_id: 0x0,
1075                 session_token: 0x20,
1076                 session_state: uwb_uci_packets::SessionState::SessionStateActive,
1077                 reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands
1078                     .into(),
1079             })
1080         );
1081     }
1082 
write_multicast_ntf_v1_payload( payload: &SessionUpdateControllerMulticastListNtfV1Payload, buffer: &mut BytesMut, )1083     fn write_multicast_ntf_v1_payload(
1084         payload: &SessionUpdateControllerMulticastListNtfV1Payload,
1085         buffer: &mut BytesMut,
1086     ) {
1087         buffer.put_u8(payload.remaining_multicast_list_size);
1088         buffer.put_u8(payload.controlee_status.len() as u8);
1089         for elem in &payload.controlee_status {
1090             write_v1_controlee_status(elem, buffer);
1091         }
1092     }
1093 
write_v1_controlee_status(status: &ControleeStatusV1, buffer: &mut BytesMut)1094     fn write_v1_controlee_status(status: &ControleeStatusV1, buffer: &mut BytesMut) {
1095         for elem in &status.mac_address {
1096             buffer.put_u8(*elem);
1097         }
1098         buffer.put_u32_le(status.subsession_id);
1099         buffer.put_u8(u8::from(status.status));
1100     }
1101 
write_multicast_ntf_v2_payload( payload: &SessionUpdateControllerMulticastListNtfV2Payload, buffer: &mut BytesMut, )1102     fn write_multicast_ntf_v2_payload(
1103         payload: &SessionUpdateControllerMulticastListNtfV2Payload,
1104         buffer: &mut BytesMut,
1105     ) {
1106         buffer.put_u8(payload.controlee_status.len() as u8);
1107         for elem in &payload.controlee_status {
1108             write_v2_controlee_status(elem, buffer);
1109         }
1110     }
1111 
write_v2_controlee_status(status: &ControleeStatusV2, buffer: &mut BytesMut)1112     fn write_v2_controlee_status(status: &ControleeStatusV2, buffer: &mut BytesMut) {
1113         for elem in &status.mac_address {
1114             buffer.put_u8(*elem);
1115         }
1116         buffer.put_u8(u8::from(status.status));
1117     }
1118 
1119     #[test]
test_session_notification_casting_from_session_update_controller_multicast_list_ntf_v1_packet( )1120     fn test_session_notification_casting_from_session_update_controller_multicast_list_ntf_v1_packet(
1121     ) {
1122         let controlee_status_v1 = uwb_uci_packets::ControleeStatusV1 {
1123             mac_address: [0x0c, 0xa8],
1124             subsession_id: 0x30,
1125             status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate,
1126         };
1127         let another_controlee_status_v1 = uwb_uci_packets::ControleeStatusV1 {
1128             mac_address: [0x0c, 0xa9],
1129             subsession_id: 0x31,
1130             status: uwb_uci_packets::MulticastUpdateStatusCode::StatusErrorKeyFetchFail,
1131         };
1132         let payload = uwb_uci_packets::SessionUpdateControllerMulticastListNtfV1Payload {
1133             remaining_multicast_list_size: 0x2,
1134             controlee_status: vec![
1135                 controlee_status_v1.clone(),
1136                 another_controlee_status_v1.clone(),
1137             ],
1138         };
1139         let mut buf = BytesMut::new();
1140         write_multicast_ntf_v1_payload(&payload, &mut buf);
1141         let session_update_controller_multicast_list_ntf_v1 =
1142             uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder {
1143                 session_token: 0x32,
1144                 payload: Some(buf.freeze()),
1145             }
1146             .build();
1147         let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from(
1148             session_update_controller_multicast_list_ntf_v1,
1149         )
1150         .unwrap();
1151         let uci_fira_major_version = UCIMajorVersion::V1;
1152         let session_notification = SessionNotification::try_from((
1153             session_notification_packet,
1154             uci_fira_major_version,
1155             false,
1156         ))
1157         .unwrap();
1158         let uci_notification_from_session_update_controller_multicast_list_ntf =
1159             UciNotification::Session(session_notification);
1160         assert_eq!(
1161             uci_notification_from_session_update_controller_multicast_list_ntf,
1162             UciNotification::Session(SessionNotification::UpdateControllerMulticastListV1 {
1163                 session_token: 0x32,
1164                 remaining_multicast_list_size: 0x2,
1165                 status_list: vec![controlee_status_v1, another_controlee_status_v1],
1166             })
1167         );
1168     }
1169 
1170     #[test]
test_cast_failed_from_session_update_controller_multicast_list_ntf_v1_packet_v2_payload()1171     fn test_cast_failed_from_session_update_controller_multicast_list_ntf_v1_packet_v2_payload() {
1172         let controlee_status_v2 = uwb_uci_packets::ControleeStatusV2 {
1173             mac_address: [0x0c, 0xa8],
1174             status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate,
1175         };
1176         let another_controlee_status_v2 = uwb_uci_packets::ControleeStatusV2 {
1177             mac_address: [0x0c, 0xa9],
1178             status: uwb_uci_packets::MulticastUpdateStatusCode::StatusErrorKeyFetchFail,
1179         };
1180         let payload = uwb_uci_packets::SessionUpdateControllerMulticastListNtfV2Payload {
1181             controlee_status: vec![controlee_status_v2, another_controlee_status_v2],
1182         };
1183         let mut buf = BytesMut::new();
1184         write_multicast_ntf_v2_payload(&payload, &mut buf);
1185         let session_update_controller_multicast_list_ntf_v1 =
1186             uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder {
1187                 session_token: 0x32,
1188                 payload: Some(buf.freeze()),
1189             }
1190             .build();
1191         let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from(
1192             session_update_controller_multicast_list_ntf_v1,
1193         )
1194         .unwrap();
1195         let uci_fira_major_version = UCIMajorVersion::V1;
1196         let session_notification = SessionNotification::try_from((
1197             session_notification_packet,
1198             uci_fira_major_version,
1199             false,
1200         ));
1201         assert_eq!(session_notification, Err(Error::BadParameters));
1202     }
1203 
1204     #[test]
test_cast_failed_from_session_update_controller_multicast_list_ntf_v2_packet_v1_payload()1205     fn test_cast_failed_from_session_update_controller_multicast_list_ntf_v2_packet_v1_payload() {
1206         let controlee_status_v1 = uwb_uci_packets::ControleeStatusV1 {
1207             mac_address: [0x0c, 0xa8],
1208             subsession_id: 0x30,
1209             status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate,
1210         };
1211         let payload = uwb_uci_packets::SessionUpdateControllerMulticastListNtfV1Payload {
1212             remaining_multicast_list_size: 0x4,
1213             controlee_status: vec![controlee_status_v1],
1214         };
1215         let mut buf = BytesMut::new();
1216         write_multicast_ntf_v1_payload(&payload, &mut buf);
1217         let session_update_controller_multicast_list_ntf_v1 =
1218             uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder {
1219                 session_token: 0x32,
1220                 payload: Some(buf.freeze()),
1221             }
1222             .build();
1223         let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from(
1224             session_update_controller_multicast_list_ntf_v1,
1225         )
1226         .unwrap();
1227         let uci_fira_major_version = UCIMajorVersion::V2;
1228         let session_notification = SessionNotification::try_from((
1229             session_notification_packet,
1230             uci_fira_major_version,
1231             true,
1232         ));
1233         assert_eq!(session_notification, Err(Error::BadParameters));
1234     }
1235 
1236     #[test]
test_session_notification_casting_from_session_update_controller_multicast_list_ntf_v2_packet( )1237     fn test_session_notification_casting_from_session_update_controller_multicast_list_ntf_v2_packet(
1238     ) {
1239         let controlee_status_v2 = uwb_uci_packets::ControleeStatusV2 {
1240             mac_address: [0x0c, 0xa8],
1241             status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate,
1242         };
1243         let another_controlee_status_v2 = uwb_uci_packets::ControleeStatusV2 {
1244             mac_address: [0x0c, 0xa9],
1245             status: uwb_uci_packets::MulticastUpdateStatusCode::StatusErrorKeyFetchFail,
1246         };
1247         let payload = uwb_uci_packets::SessionUpdateControllerMulticastListNtfV2Payload {
1248             controlee_status: vec![
1249                 controlee_status_v2.clone(),
1250                 another_controlee_status_v2.clone(),
1251             ],
1252         };
1253         let mut buf = BytesMut::new();
1254         write_multicast_ntf_v2_payload(&payload, &mut buf);
1255         let session_update_controller_multicast_list_ntf_v2 =
1256             uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder {
1257                 session_token: 0x32,
1258                 payload: Some(buf.freeze()),
1259             }
1260             .build();
1261         let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from(
1262             session_update_controller_multicast_list_ntf_v2,
1263         )
1264         .unwrap();
1265         let uci_fira_major_version = UCIMajorVersion::V2;
1266         let session_notification = SessionNotification::try_from((
1267             session_notification_packet,
1268             uci_fira_major_version,
1269             true,
1270         ))
1271         .unwrap();
1272         let uci_notification_from_session_update_controller_multicast_list_ntf =
1273             UciNotification::Session(session_notification);
1274         assert_eq!(
1275             uci_notification_from_session_update_controller_multicast_list_ntf,
1276             UciNotification::Session(SessionNotification::UpdateControllerMulticastListV2 {
1277                 session_token: 0x32,
1278                 status_list: vec![controlee_status_v2, another_controlee_status_v2],
1279             })
1280         );
1281     }
1282 
1283     #[test]
test_session_notification_casting_from_session_data_transfer_phase_config_ntf_packet()1284     fn test_session_notification_casting_from_session_data_transfer_phase_config_ntf_packet() {
1285         let session_data_transfer_phase_config_ntf =
1286             uwb_uci_packets::SessionDataTransferPhaseConfigNtfBuilder {
1287                 session_token: 0x32,
1288                 status: DataTransferPhaseConfigUpdateStatusCode::UciDtpcmConfigSuccessStatusOk,
1289             }
1290             .build();
1291         let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from(
1292             session_data_transfer_phase_config_ntf,
1293         )
1294         .unwrap();
1295         let uci_fira_major_version = UCIMajorVersion::V1;
1296         let session_notification = SessionNotification::try_from((
1297             session_notification_packet,
1298             uci_fira_major_version,
1299             false,
1300         ))
1301         .unwrap();
1302         let uci_notification_from_session_data_transfer_phase_config_ntf =
1303             UciNotification::Session(session_notification);
1304         assert_eq!(
1305             uci_notification_from_session_data_transfer_phase_config_ntf,
1306             UciNotification::Session(SessionNotification::DataTransferPhaseConfig {
1307                 session_token: 0x32,
1308                 status: DataTransferPhaseConfigUpdateStatusCode::UciDtpcmConfigSuccessStatusOk
1309             })
1310         );
1311     }
1312 
1313     #[test]
test_session_notification_casting_from_short_mac_dl_tdoa_session_info_ntf_packet()1314     fn test_session_notification_casting_from_short_mac_dl_tdoa_session_info_ntf_packet() {
1315         let dl_tdoa_measurements = vec![
1316             0x0a, 0x01, 0x33, 0x05, // 2(Mac address), Status, Message Type
1317             0x53, 0x05, 0x02, 0x05, // 2(Message control), 2(Block Index)
1318             0x07, 0x09, 0x0a, 0x01, // Round Index, NLoS, 2(AoA Azimuth)
1319             0x02, 0x05, 0x07, 0x09, // AoA Azimuth FOM, 2(AoA Elevation), AoA Elevation FOM
1320             0x0a, 0x01, 0x02, 0x05, // RSSI, 3(Tx Timestamp..)
1321             0x07, 0x09, 0x0a, 0x01, // 4(Tx Timestamp..)
1322             0x02, 0x05, 0x07, 0x09, // Tx Timestamp, 3(Rx Timestamp..)
1323             0x05, 0x07, 0x09, 0x0a, // 2(Rx Timestamp), 2(Anchor Cfo)
1324             0x01, 0x02, 0x05, 0x07, // 2(Cfo), 2(Initiator Reply Time..)
1325             0x09, 0x05, 0x07, 0x09, // 2(Initiator Reply Time), 2(Responder Reply Time..)
1326             0x0a, 0x01, 0x02, 0x05, // 2(Responder Reply Time), 2(Initiator-Responder ToF)
1327             0x07, 0x09, 0x07, 0x09, // 4(Anchor Location..)
1328             0x05, 0x07, 0x09, 0x0a, // 4(Anchor Location..)
1329             0x01, 0x02, 0x05, 0x07, // 2(Anchor Location..), 2(Active Ranging Rounds..)
1330             0x09, 0x0a, 0x01, 0x02, // 4(Active Ranging Rounds..)
1331             0x05, 0x07, 0x09, 0x05, // 4(Active Ranging Rounds)
1332         ];
1333         let short_mac_dl_tdoa_session_info_ntf =
1334             uwb_uci_packets::ShortMacDlTDoASessionInfoNtfBuilder {
1335                 current_ranging_interval: 0x13,
1336                 hus_primary_session_id: 0x00,
1337                 dl_tdoa_measurements: dl_tdoa_measurements.clone(),
1338                 no_of_ranging_measurements: 1,
1339                 rcr_indicator: 0x12,
1340                 sequence_number: 0x10,
1341                 session_token: 0x11,
1342             }
1343             .build();
1344         let raw_ranging_data = short_mac_dl_tdoa_session_info_ntf.encode_to_bytes().unwrap()
1345             [UCI_PACKET_HEADER_LEN..]
1346             .to_vec();
1347         let short_measurement =
1348             ShortAddressDlTdoaRangingMeasurement::parse(&dl_tdoa_measurements, 1).unwrap();
1349         let range_notification_packet =
1350             uwb_uci_packets::SessionInfoNtf::try_from(short_mac_dl_tdoa_session_info_ntf).unwrap();
1351         let session_notification =
1352             SessionNotification::try_from(range_notification_packet).unwrap();
1353         let uci_notification_from_short_mac_dl_tdoa_session_info_ntf =
1354             UciNotification::Session(session_notification);
1355         assert_eq!(
1356             uci_notification_from_short_mac_dl_tdoa_session_info_ntf,
1357             UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData {
1358                 sequence_number: 0x10,
1359                 session_token: 0x11,
1360                 ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::DlTdoa,
1361                 current_ranging_interval_ms: 0x13,
1362                 hus_primary_session_id: 0x00,
1363                 ranging_measurements: RangingMeasurements::ShortAddressDltdoa(short_measurement),
1364                 rcr_indicator: 0x12,
1365                 raw_ranging_data,
1366             }))
1367         );
1368     }
1369 
1370     #[test]
test_session_notification_casting_from_extended_mac_dltdoa_session_info_ntf_packet()1371     fn test_session_notification_casting_from_extended_mac_dltdoa_session_info_ntf_packet() {
1372         let dl_tdoa_measurements = vec![
1373             // All Fields in Little Endian (LE)
1374             0x0a, 0x01, 0x33, 0x05, // 4(Mac address..)
1375             0x33, 0x05, 0x02, 0x05, // 4(Mac address)
1376             0x07, 0x09, 0x0a, 0x01, // Status, Message Type, 2(Message control),
1377             0x02, 0x05, 0x07, 0x09, // 2(Block Index), Round Index, NLoS,
1378             0x0a, 0x01, 0x02, 0x05, // 2(AoA Azimuth), AoA Azimuth FOM, 1(AoA Elevation..)
1379             0x07, 0x09, 0x0a, // 1(AoA Elevation), AoA Elevation FOM, RSSI,
1380             0x01, 0x02, 0x05, 0x07, // 4(Tx Timestamp..)
1381             0x09, 0x05, 0x07, 0x09, // 4(Tx Timestamp),
1382             0x0a, 0x01, 0x02, 0x05, // 4(Rx Timestamp..)
1383             0x07, 0x09, 0x05, 0x07, // 4(Rx Timestamp)
1384             0x09, 0x0a, 0x01, 0x02, // 2(Anchor Cfo), 2(Cfo),
1385             0x05, 0x07, 0x09, 0x05, // 4(Initiator Reply Time)
1386             0x07, 0x09, 0x0a, 0x01, // 4(Responder Reply Time),
1387             0x02, 0x05, 0x02, 0x05, // 2(Initiator-Responder ToF), 2(Active Ranging Rounds)
1388         ];
1389         let extended_mac_dl_tdoa_session_info_ntf =
1390             uwb_uci_packets::ExtendedMacDlTDoASessionInfoNtfBuilder {
1391                 current_ranging_interval: 0x13,
1392                 hus_primary_session_id: 0x00,
1393                 dl_tdoa_measurements: dl_tdoa_measurements.clone(),
1394                 no_of_ranging_measurements: 1,
1395                 rcr_indicator: 0x12,
1396                 sequence_number: 0x10,
1397                 session_token: 0x11,
1398             }
1399             .build();
1400         let raw_ranging_data = extended_mac_dl_tdoa_session_info_ntf.encode_to_bytes().unwrap()
1401             [UCI_PACKET_HEADER_LEN..]
1402             .to_vec();
1403         let short_measurement =
1404             ExtendedAddressDlTdoaRangingMeasurement::parse(&dl_tdoa_measurements, 1).unwrap();
1405         let range_notification_packet =
1406             uwb_uci_packets::SessionInfoNtf::try_from(extended_mac_dl_tdoa_session_info_ntf)
1407                 .unwrap();
1408         let session_notification =
1409             SessionNotification::try_from(range_notification_packet).unwrap();
1410         let uci_notification_from_extended_mac_dl_tdoa_session_info_ntf =
1411             UciNotification::Session(session_notification);
1412         assert_eq!(
1413             uci_notification_from_extended_mac_dl_tdoa_session_info_ntf,
1414             UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData {
1415                 sequence_number: 0x10,
1416                 session_token: 0x11,
1417                 ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::DlTdoa,
1418                 current_ranging_interval_ms: 0x13,
1419                 hus_primary_session_id: 0x00,
1420                 ranging_measurements: RangingMeasurements::ExtendedAddressDltdoa(short_measurement),
1421                 rcr_indicator: 0x12,
1422                 raw_ranging_data,
1423             }))
1424         );
1425     }
1426 
1427     #[test]
1428     #[allow(non_snake_case)] //override snake case for vendor_A
test_vendor_notification_casting()1429     fn test_vendor_notification_casting() {
1430         let vendor_9_empty_notification: uwb_uci_packets::UciNotification =
1431             uwb_uci_packets::UciVendor_9_NotificationBuilder { opcode: 0x40, payload: None }
1432                 .build()
1433                 .into();
1434         let vendor_A_nonempty_notification: uwb_uci_packets::UciNotification =
1435             uwb_uci_packets::UciVendor_A_NotificationBuilder {
1436                 opcode: 0x41,
1437                 payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")),
1438             }
1439             .build()
1440             .into();
1441         let vendor_B_nonempty_notification: uwb_uci_packets::UciNotification =
1442             uwb_uci_packets::UciVendor_B_NotificationBuilder {
1443                 opcode: 0x41,
1444                 payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")),
1445             }
1446             .build()
1447             .into();
1448         let vendor_E_nonempty_notification: uwb_uci_packets::UciNotification =
1449             uwb_uci_packets::UciVendor_E_NotificationBuilder {
1450                 opcode: 0x41,
1451                 payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")),
1452             }
1453             .build()
1454             .into();
1455         let vendor_F_nonempty_notification: uwb_uci_packets::UciNotification =
1456             uwb_uci_packets::UciVendor_F_NotificationBuilder {
1457                 opcode: 0x41,
1458                 payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")),
1459             }
1460             .build()
1461             .into();
1462         let uci_fira_major_version = UCIMajorVersion::V1;
1463         let uci_notification_from_vendor_9 = UciNotification::try_from((
1464             vendor_9_empty_notification,
1465             uci_fira_major_version.clone(),
1466             false,
1467         ))
1468         .unwrap();
1469         let uci_notification_from_vendor_A = UciNotification::try_from((
1470             vendor_A_nonempty_notification,
1471             uci_fira_major_version.clone(),
1472             false,
1473         ))
1474         .unwrap();
1475         let uci_notification_from_vendor_B = UciNotification::try_from((
1476             vendor_B_nonempty_notification,
1477             uci_fira_major_version.clone(),
1478             false,
1479         ))
1480         .unwrap();
1481         let uci_notification_from_vendor_E = UciNotification::try_from((
1482             vendor_E_nonempty_notification,
1483             uci_fira_major_version.clone(),
1484             false,
1485         ))
1486         .unwrap();
1487         let uci_notification_from_vendor_F = UciNotification::try_from((
1488             vendor_F_nonempty_notification,
1489             uci_fira_major_version,
1490             false,
1491         ))
1492         .unwrap();
1493         assert_eq!(
1494             uci_notification_from_vendor_9,
1495             UciNotification::Vendor(RawUciMessage {
1496                 gid: 0x9, // per enum GroupId in uci_packets.pdl
1497                 oid: 0x40,
1498                 payload: vec![],
1499             })
1500         );
1501         assert_eq!(
1502             uci_notification_from_vendor_A,
1503             UciNotification::Vendor(RawUciMessage {
1504                 gid: 0xa,
1505                 oid: 0x41,
1506                 payload: b"Placeholder notification.".to_owned().into(),
1507             })
1508         );
1509         assert_eq!(
1510             uci_notification_from_vendor_B,
1511             UciNotification::Vendor(RawUciMessage {
1512                 gid: 0xb,
1513                 oid: 0x41,
1514                 payload: b"Placeholder notification.".to_owned().into(),
1515             })
1516         );
1517         assert_eq!(
1518             uci_notification_from_vendor_E,
1519             UciNotification::Vendor(RawUciMessage {
1520                 gid: 0xe,
1521                 oid: 0x41,
1522                 payload: b"Placeholder notification.".to_owned().into(),
1523             })
1524         );
1525         assert_eq!(
1526             uci_notification_from_vendor_F,
1527             UciNotification::Vendor(RawUciMessage {
1528                 gid: 0xf,
1529                 oid: 0x41,
1530                 payload: b"Placeholder notification.".to_owned().into(),
1531             })
1532         );
1533     }
1534 
1535     #[test]
test_rf_test_notification_casting_from_rf_periodic_tx_ntf()1536     fn test_rf_test_notification_casting_from_rf_periodic_tx_ntf() {
1537         let test_periodic_tx_ntf_packet = uwb_uci_packets::TestPeriodicTxNtfBuilder {
1538             status: uwb_uci_packets::StatusCode::UciStatusOk,
1539             vendor_data: vec![],
1540         }
1541         .build();
1542         let raw_notification_data = test_periodic_tx_ntf_packet.clone().encode_to_bytes().unwrap()
1543             [UCI_PACKET_HEADER_LEN..]
1544             .to_vec();
1545         let rf_test_notification =
1546             uwb_uci_packets::TestNotification::try_from(test_periodic_tx_ntf_packet).unwrap();
1547         let uci_notification = RfTestNotification::try_from(rf_test_notification).unwrap();
1548         let uci_notification_from_periodic_tx_ntf = UciNotification::RfTest(uci_notification);
1549         let status = uwb_uci_packets::StatusCode::UciStatusOk;
1550         assert_eq!(
1551             uci_notification_from_periodic_tx_ntf,
1552             UciNotification::RfTest(RfTestNotification::TestPeriodicTxNtf {
1553                 status,
1554                 raw_notification_data
1555             })
1556         );
1557     }
1558 
1559     #[test]
test_rf_test_notification_casting_from_rf_per_rx_ntf()1560     fn test_rf_test_notification_casting_from_rf_per_rx_ntf() {
1561         let test_per_rx_ntf_packet = uwb_uci_packets::TestPerRxNtfBuilder {
1562             status: uwb_uci_packets::StatusCode::UciStatusOk,
1563             attempts: 1,
1564             acq_detect: 2,
1565             acq_reject: 3,
1566             rx_fail: 4,
1567             sync_cir_ready: 5,
1568             sfd_fail: 6,
1569             sfd_found: 7,
1570             phr_dec_error: 8,
1571             phr_bit_error: 9,
1572             psdu_dec_error: 10,
1573             psdu_bit_error: 11,
1574             sts_found: 12,
1575             eof: 13,
1576             vendor_data: vec![],
1577         }
1578             .build();
1579         let raw_notification_data = test_per_rx_ntf_packet.clone().encode_to_bytes().unwrap()
1580             [UCI_PACKET_HEADER_LEN..]
1581             .to_vec();
1582         let rf_test_notification =
1583             uwb_uci_packets::TestNotification::try_from(test_per_rx_ntf_packet).unwrap();
1584         let uci_notification = RfTestNotification::try_from(rf_test_notification).unwrap();
1585         let uci_notification_from_per_rx_ntf = UciNotification::RfTest(uci_notification);
1586         let status = uwb_uci_packets::StatusCode::UciStatusOk;
1587         let attempts = 1;
1588         let acq_detect = 2;
1589         let acq_reject = 3;
1590         let rx_fail = 4;
1591         let sync_cir_ready = 5;
1592         let sfd_fail = 6;
1593         let sfd_found = 7;
1594         let phr_dec_error = 8;
1595         let phr_bit_error = 9;
1596         let psdu_dec_error = 10;
1597         let psdu_bit_error = 11;
1598         let sts_found = 12;
1599         let eof = 13;
1600         assert_eq!(
1601             uci_notification_from_per_rx_ntf,
1602             UciNotification::RfTest(RfTestNotification::TestPerRxNtf(RfTestPerRxData {
1603                 status,
1604                 attempts,
1605                 acq_detect,
1606                 acq_reject,
1607                 rx_fail,
1608                 sync_cir_ready,
1609                 sfd_fail,
1610                 sfd_found,
1611                 phr_dec_error,
1612                 phr_bit_error,
1613                 psdu_dec_error,
1614                 psdu_bit_error,
1615                 sts_found,
1616                 eof,
1617                 raw_notification_data
1618             }))
1619         );
1620     }
1621 }
1622