1use super::ParsingError;
4use super::hex_extractor::extract_binary_from_lines;
5use crate::data::{AdditionalInfos, Trace};
6use crate::interface::association::TraceRelation;
7use crate::interface::{layer::Layer, types::Direction};
8
9use super::{FileParser, LayerParser, ParsedHeader};
10
11#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum PHYChannelType {
14 PDSCH,
16 PUSCH,
18 PUCCH,
20 PDCCH,
22 PRACH,
24 Other,
26}
27
28#[derive(Debug, Clone)]
30pub enum PHYChannelData {
31 Pdcch {
33 dci: String,
35 harq_process: Option<u8>,
37 ndi: Option<u8>,
39 rv_idx: Option<u8>,
41 harq_feedback_timing: Option<u8>,
43 },
44 Pdsch {
46 retx: Option<u8>,
48 rv_idx: Option<u8>,
50 },
51 Pusch {
53 retx: Option<u8>,
55 rv_idx: Option<u8>,
57 crc: Option<bool>,
59 ack: Option<bool>,
61 measurements: UlMeasurements,
63 },
64 Pucch {
66 format: Option<u8>,
68 ack: Option<bool>,
70 measurements: UlMeasurements,
72 },
73 None,
75}
76
77#[derive(Debug, Clone, Default, PartialEq)]
79pub struct UlMeasurements {
80 pub epre: Option<f32>,
82 pub ta: Option<f32>,
84 pub csi: Option<u32>,
87}
88
89#[derive(Debug, Clone)]
91pub struct PHYInfos {
92 pub direction: Direction,
94 pub channel_type: PHYChannelType,
96 pub frame: u16,
98 pub slot: u8,
100 pub prb_start: u16,
102 pub prb_length: u16,
104 pub symb_start: u8,
106 pub symb_length: u8,
108 pub harq: Option<u8>,
110 pub harq_si: bool,
112 pub channel_data: PHYChannelData,
114}
115
116pub struct PHYParser;
118
119impl PHYParser {
120 fn parse_lines(lines: &[String]) -> Vec<String> {
122 lines.to_vec()
123 }
124}
125
126impl FileParser for PHYParser {
127 fn parse_additional_infos(lines: &[String]) -> Result<AdditionalInfos, ParsingError> {
128 if lines.is_empty() {
129 return Ok(AdditionalInfos::None);
130 }
131
132 let first_line = &lines[0];
133
134 let direction = if first_line.contains(" DL ") {
136 Direction::DL
137 } else if first_line.contains(" UL ") {
138 Direction::UL
139 } else {
140 Direction::DL };
142
143 match parse_phy_lines(lines, direction) {
145 Some(phy_infos) => {
146 if matches!(
148 phy_infos.channel_type,
149 PHYChannelType::PDCCH
150 | PHYChannelType::PDSCH
151 | PHYChannelType::PUSCH
152 | PHYChannelType::PUCCH
153 | PHYChannelType::PRACH
154 ) {
155 Ok(AdditionalInfos::PHYInfos(phy_infos))
156 } else {
157 Ok(AdditionalInfos::None)
158 }
159 }
160 None => Ok(AdditionalInfos::None),
161 }
162 }
163
164 fn parse(lines: &[String]) -> Result<Trace, ParsingError> {
165 let additional_infos = match Self::parse_additional_infos(lines) {
166 Ok(m) => m,
167 Err(e) => {
168 return Err(e);
169 }
170 };
171
172 let text = Self::parse_lines(lines);
173
174 let binary = extract_binary_from_lines(lines);
175
176 let timestamp = if let Some(first_line) = lines.first() {
178 super::parse_timestamp(first_line)?
179 } else {
180 0
181 };
182
183 let trace = Trace {
184 timestamp,
185 layer: Layer::PHY,
186 additional_infos,
187 text: Some(text),
188 binary,
189 relation: TraceRelation::default(),
190 };
191 Ok(trace)
192 }
193}
194
195pub fn parse_phy_lines(lines: &[String], direction: Direction) -> Option<PHYInfos> {
211 if lines.is_empty() {
212 return None;
213 }
214 let first_line = &lines[0];
215
216 let channel_type = if first_line.contains("PDSCH:") {
218 PHYChannelType::PDSCH
219 } else if first_line.contains("PUSCH:") {
220 PHYChannelType::PUSCH
221 } else if first_line.contains("PUCCH:") {
222 PHYChannelType::PUCCH
223 } else if first_line.contains("PDCCH:") {
224 PHYChannelType::PDCCH
225 } else if first_line.contains("PRACH:") {
226 PHYChannelType::PRACH
227 } else {
228 PHYChannelType::Other
229 };
230
231 let (frame, slot) = parse_frame_slot(first_line)?;
233
234 let (prb_start, prb_length) = if matches!(channel_type, PHYChannelType::PDCCH) {
236 (0, 0)
237 } else {
238 parse_prb(first_line)?
239 };
240
241 let (symb_start, symb_length) = parse_symb(first_line).unwrap_or((0, 14));
243
244 let (harq, harq_si) = parse_harq(first_line);
246
247 let channel_data = match channel_type {
249 PHYChannelType::PDCCH => parse_pdcch_data(lines),
250 PHYChannelType::PDSCH => parse_pdsch_data(first_line),
251 PHYChannelType::PUSCH => parse_pusch_data(first_line),
252 PHYChannelType::PUCCH => parse_pucch_data(first_line),
253 _ => PHYChannelData::None,
254 };
255
256 Some(PHYInfos {
257 direction,
258 channel_type,
259 frame,
260 slot,
261 prb_start,
262 prb_length,
263 symb_start,
264 symb_length,
265 harq,
266 harq_si,
267 channel_data,
268 })
269}
270
271impl LayerParser for PHYParser {
272 fn parse_layer(header: &ParsedHeader, data_lines: &[String]) -> Result<AdditionalInfos, ParsingError> {
276 let channel_name = header.channel.as_deref().unwrap_or("");
277 let channel_type = match channel_name {
278 "PDSCH" => PHYChannelType::PDSCH,
279 "PUSCH" => PHYChannelType::PUSCH,
280 "PUCCH" => PHYChannelType::PUCCH,
281 "PDCCH" => PHYChannelType::PDCCH,
282 "PRACH" => PHYChannelType::PRACH,
283 _ => PHYChannelType::Other,
284 };
285
286 if matches!(channel_type, PHYChannelType::Other) {
287 return Ok(AdditionalInfos::None);
288 }
289
290 let frame = header.frame.unwrap_or(0);
291 let slot = header.slot.unwrap_or(0);
292
293 let first_line = data_lines.first().map(|s| s.as_str()).unwrap_or("");
296
297 let (prb_start, prb_length) = if matches!(channel_type, PHYChannelType::PDCCH) {
299 (0, 0)
300 } else {
301 parse_prb(first_line).unwrap_or((0, 0))
302 };
303
304 let (symb_start, symb_length) = parse_symb(first_line).unwrap_or((0, 14));
305 let (harq, harq_si) = parse_harq(first_line);
306
307 let channel_data = match channel_type {
309 PHYChannelType::PDCCH => parse_pdcch_data(data_lines),
310 PHYChannelType::PDSCH => parse_pdsch_data(first_line),
311 PHYChannelType::PUSCH => parse_pusch_data(first_line),
312 PHYChannelType::PUCCH => parse_pucch_data(first_line),
313 _ => PHYChannelData::None,
314 };
315
316 Ok(AdditionalInfos::PHYInfos(PHYInfos {
317 direction: header.direction.clone(),
318 channel_type,
319 frame,
320 slot,
321 prb_start,
322 prb_length,
323 symb_start,
324 symb_length,
325 harq,
326 harq_si,
327 channel_data,
328 }))
329 }
330}
331
332pub fn parse_phy_line(line: &str, direction: Direction) -> Option<PHYInfos> {
334 parse_phy_lines(&[line.to_string()], direction)
335}
336
337fn parse_frame_slot(line: &str) -> Option<(u16, u8)> {
343 let parts: Vec<&str> = line.split_whitespace().collect();
348
349 for part in &parts {
350 if let Some(dot_pos) = part.find('.') {
352 let frame_part = &part[..dot_pos];
353 let slot_part = &part[dot_pos + 1..];
354
355 let slot_part: String = slot_part.chars().take_while(|c| c.is_ascii_digit()).collect();
357
358 if let (Ok(frame), Ok(slot)) = (frame_part.parse::<u16>(), slot_part.parse::<u8>()) {
359 if slot < 20 {
361 return Some((frame, slot));
362 }
363 }
364 }
365 }
366
367 None
368}
369
370fn parse_harq(line: &str) -> (Option<u8>, bool) {
375 let harq_start_idx = match line.find("harq=") {
376 Some(idx) => idx,
377 None => return (None, false),
378 };
379 let harq_value_start = harq_start_idx + 5; let value_part: String = line[harq_value_start..]
382 .chars()
383 .take_while(|c| c.is_ascii_alphanumeric())
384 .collect();
385
386 if value_part == "si" {
387 return (None, true);
388 }
389
390 (value_part.parse::<u8>().ok(), false)
391}
392
393fn extract_field_u8(line: &str, prefix: &str) -> Option<u8> {
395 let start = line.find(prefix)? + prefix.len();
396 let value: String = line[start..].chars().take_while(|c| c.is_ascii_digit()).collect();
397 value.parse::<u8>().ok()
398}
399
400fn extract_field_str(line: &str, prefix: &str) -> Option<String> {
402 let start = line.find(prefix)? + prefix.len();
403 let value: String = line[start..].chars().take_while(|c| !c.is_whitespace()).collect();
404 if value.is_empty() { None } else { Some(value) }
405}
406
407fn parse_pdcch_data(lines: &[String]) -> PHYChannelData {
412 let first_line = &lines[0];
413 let dci = extract_field_str(first_line, "dci=").unwrap_or_default();
414 let is_dci_1_1 = dci == "1_1";
415
416 let mut harq_process = None;
417 let mut ndi = None;
418 let mut rv_idx = None;
419 let mut harq_feedback_timing = None;
420
421 for line in lines.iter().skip(1) {
422 let trimmed = line.trim();
423 if let Some(trimmed_prefix) = trimmed.strip_prefix("harq_process=") {
424 harq_process = trimmed_prefix.split_whitespace().next().and_then(|v| v.parse::<u8>().ok());
425 } else if is_dci_1_1 {
426 if let Some(trimmed_prefix) = trimmed.strip_prefix("ndi1=") {
428 ndi = trimmed_prefix.split_whitespace().next().and_then(|v| v.parse::<u8>().ok());
429 } else if let Some(trimmed_prefix) = trimmed.strip_prefix("rv_idx1=") {
430 rv_idx = trimmed_prefix.split_whitespace().next().and_then(|v| v.parse::<u8>().ok());
431 } else if let Some(trimmed_prefix) = trimmed.strip_prefix("harq_feedback_timing=") {
432 harq_feedback_timing = trimmed_prefix.split_whitespace().next().and_then(|v| v.parse::<u8>().ok());
433 }
434 } else {
435 if let Some(trimmed_prefix) = trimmed.strip_prefix("ndi=") {
437 ndi = trimmed_prefix.split_whitespace().next().and_then(|v| v.parse::<u8>().ok());
438 } else if let Some(trimmed_prefix) = trimmed.strip_prefix("rv_idx=") {
439 rv_idx = trimmed_prefix.split_whitespace().next().and_then(|v| v.parse::<u8>().ok());
440 }
441 }
442 }
443
444 PHYChannelData::Pdcch {
445 dci,
446 harq_process,
447 ndi,
448 rv_idx,
449 harq_feedback_timing,
450 }
451}
452
453fn parse_pdsch_data(line: &str) -> PHYChannelData {
455 let retx = extract_field_u8(line, "retx=");
456 let rv_idx = extract_field_u8(line, "rv_idx=");
457 PHYChannelData::Pdsch { retx, rv_idx }
458}
459
460fn parse_pusch_data(line: &str) -> PHYChannelData {
462 let retx = extract_field_u8(line, "retx=");
463 let rv_idx = extract_field_u8(line, "rv_idx=");
464 let crc = extract_field_str(line, "crc=").map(|s| s != "KO");
465 let ack = extract_field_str(line, "ack=").map(|s| s != "0");
466 PHYChannelData::Pusch {
467 retx,
468 rv_idx,
469 crc,
470 ack,
471 measurements: parse_ul_measurements(line),
472 }
473}
474
475fn parse_ul_measurements(line: &str) -> UlMeasurements {
477 UlMeasurements {
478 epre: extract_field_str(line, "epre=").and_then(|s| s.parse::<f32>().ok()),
479 ta: extract_field_str(line, "ta=").and_then(|s| s.parse::<f32>().ok()),
480 csi: extract_field_str(line, "csi=").and_then(|s| u32::from_str_radix(&s, 2).ok()),
481 }
482}
483
484fn parse_pucch_data(line: &str) -> PHYChannelData {
489 let format = extract_field_u8(line, "format=");
490 let ack = extract_field_str(line, "ack=").map(|s| s != "0");
492 PHYChannelData::Pucch {
493 format,
494 ack,
495 measurements: parse_ul_measurements(line),
496 }
497}
498
499fn parse_prb(line: &str) -> Option<(u16, u16)> {
506 let prb_start_idx = line.find("prb=")?;
508 let prb_value_start = prb_start_idx + 4; let value_part: String = line[prb_value_start..]
512 .chars()
513 .take_while(|c| c.is_ascii_digit() || *c == ':' || *c == ' ')
514 .collect();
515
516 let value_trimmed = value_part.trim();
517
518 if let Some(colon_pos) = value_trimmed.find(':') {
519 let start = value_trimmed[..colon_pos].parse::<u16>().ok()?;
521 let length = value_trimmed[colon_pos + 1..].parse::<u16>().ok()?;
522 Some((start, length))
523 } else {
524 let start = value_trimmed.parse::<u16>().ok()?;
526 Some((start, 1))
527 }
528}
529
530fn parse_symb(line: &str) -> Option<(u8, u8)> {
539 let symb_start_idx = line.find("symb=")?;
541 let symb_value_start = symb_start_idx + 5; let value_part: String = line[symb_value_start..]
545 .chars()
546 .take_while(|c| c.is_ascii_digit() || *c == ':' || *c == ' ')
547 .collect();
548
549 let value_trimmed = value_part.trim();
550
551 if let Some(colon_pos) = value_trimmed.find(':') {
552 let start = value_trimmed[..colon_pos].parse::<u8>().ok()?;
554 let length = value_trimmed[colon_pos + 1..].parse::<u8>().ok()?;
555 Some((start, length))
556 } else {
557 let start = value_trimmed.parse::<u8>().ok()?;
559 Some((start, 1))
560 }
561}
562
563#[cfg(test)]
564mod tests {
565 use super::*;
566
567 #[test]
568 fn test_parse_5g_pdsch() {
569 let line = "10:32:34.715 [PHY] DL 0001 01 4601 431.16 PDSCH: harq=0 prb=50 symb=1:13 k1=12 CW0: tb_len=133 mod=8 rv_idx=0 cr=0.94 retx=0";
570 let info = parse_phy_line(line, Direction::DL).unwrap();
571
572 assert_eq!(info.frame, 431);
573 assert_eq!(info.slot, 16);
574 assert_eq!(info.prb_start, 50);
575 assert_eq!(info.prb_length, 1);
576 assert_eq!(info.symb_start, 1);
577 assert_eq!(info.symb_length, 13);
578 assert!(matches!(info.channel_type, PHYChannelType::PDSCH));
579 assert_eq!(info.harq, Some(0));
580 assert!(!info.harq_si);
581 assert!(matches!(
582 info.channel_data,
583 PHYChannelData::Pdsch {
584 retx: Some(0),
585 rv_idx: Some(0)
586 }
587 ));
588 }
589
590 #[test]
591 fn test_parse_4g_pdsch_no_symb() {
592 let line = "10:37:50.654 [PHY] DL 0001 01 003d 421.0 PDSCH: harq=0 k1=4 prb=23:2 tx=port0 CW0: tb_len=185 mod=6 rv_idx=0 retx=0";
593 let info = parse_phy_line(line, Direction::DL).unwrap();
594
595 assert_eq!(info.frame, 421);
596 assert_eq!(info.slot, 0);
597 assert_eq!(info.prb_start, 23);
598 assert_eq!(info.prb_length, 2);
599 assert_eq!(info.symb_start, 0);
600 assert_eq!(info.symb_length, 14); assert!(matches!(info.channel_type, PHYChannelType::PDSCH));
602 assert_eq!(info.harq, Some(0));
603 }
604
605 #[test]
606 fn test_parse_pusch() {
607 let line = "10:37:38.884 [PHY] UL 0001 01 003d 267.5 PUSCH: harq=3 prb=2:4 symb=0:13 CW0: tb_len=11 mod=2 rv_idx=0 retx=0 crc=OK snr=26.2 epre=-35.6 ta=0.1 ack=1";
608 let info = parse_phy_line(line, Direction::UL).unwrap();
609 assert_eq!(info.frame, 267);
611 assert_eq!(info.slot, 5);
612 assert_eq!(info.prb_start, 2);
613 assert_eq!(info.prb_length, 4);
614 assert_eq!(info.symb_start, 0);
615 assert_eq!(info.symb_length, 13);
616 assert!(matches!(info.channel_type, PHYChannelType::PUSCH));
617 assert_eq!(info.harq, Some(3));
618 match &info.channel_data {
619 PHYChannelData::Pusch {
620 retx,
621 rv_idx,
622 crc,
623 ack,
624 measurements,
625 } => {
626 assert_eq!(*retx, Some(0));
627 assert_eq!(*rv_idx, Some(0));
628 assert_eq!(*crc, Some(true));
629 assert_eq!(*ack, Some(true));
630 assert_eq!(measurements.epre, Some(-35.6));
631 assert_eq!(measurements.ta, Some(0.1));
632 assert_eq!(measurements.csi, None);
633 }
634 _ => panic!("Expected Pusch channel data"),
635 }
636 }
637
638 #[test]
639 fn test_parse_pusch_measurements_with_csi() {
640 let line = "13:20:46.485 [PHY] UL 003d 01 4644 820.9 PUSCH: harq=0 prb=2 symb=0:14 CW0: tb_len=141 mod=8 rv_idx=0 cr=0.92 retx=0 crc=KO snr=24.4 epre=-45.4 ta=0.1 csi=0101";
641 let info = parse_phy_line(line, Direction::UL).unwrap();
642 match &info.channel_data {
643 PHYChannelData::Pusch { measurements, .. } => {
644 assert_eq!(measurements.epre, Some(-45.4));
645 assert_eq!(measurements.ta, Some(0.1));
646 assert_eq!(measurements.csi, Some(5));
647 }
648 _ => panic!("Expected Pusch channel data"),
649 }
650 }
651
652 #[test]
653 fn test_parse_pusch_crc_ko() {
654 let line = "10:32:34.569 [PHY] UL 0001 01 4601 416.19 PUSCH: harq=0 prb=2 symb=0:14 CW0: tb_len=145 mod=8 rv_idx=0 cr=0.94 retx=0 crc=KO snr=37.1 epre=-86.9 ta=-0.5";
655 let info = parse_phy_line(line, Direction::UL).unwrap();
656 assert!(matches!(info.channel_data, PHYChannelData::Pusch { crc: Some(false), .. }));
657 }
658
659 #[test]
660 fn test_parse_prb_single() {
661 let line = "10:37:50.654 [PHY] DL 0001 01 003d 421.0 PDSCH: prb=50 symb=1:13";
662 let info = parse_phy_line(line, Direction::DL).unwrap();
663 assert_eq!(info.prb_start, 50);
664 assert_eq!(info.prb_length, 1);
665 }
666
667 #[test]
668 fn test_parse_pdcch_dci_0_1() {
669 let lines: Vec<String> = vec![
670 "10:32:29.584 [PHY] DL 0001 01 4601 942.15 PDCCH: ss_id=2 cce_index=6 al=2 dci=0_1 k2=4".into(),
671 "\t\trb_alloc=0x30".into(),
672 "\t\ttime_domain_rsc=1".into(),
673 "\t\tmcs=27".into(),
674 "\t\tndi=1".into(),
675 "\t\trv_idx=0".into(),
676 "\t\tharq_process=0".into(),
677 "\t\tdai=3".into(),
678 "\t\ttpc_command=1".into(),
679 "\t\tantenna_ports=0".into(),
680 "\t\tsrs_request=0".into(),
681 "\t\tdmrs_seq_init=0".into(),
682 "\t\tul_sch_indicator=1".into(),
683 ];
684 let info = parse_phy_lines(&lines, Direction::DL).unwrap();
685 assert!(matches!(info.channel_type, PHYChannelType::PDCCH));
686 assert_eq!(info.frame, 942);
687 assert_eq!(info.slot, 15);
688 assert_eq!(info.prb_start, 0); match &info.channel_data {
690 PHYChannelData::Pdcch {
691 dci,
692 harq_process,
693 ndi,
694 rv_idx,
695 harq_feedback_timing,
696 } => {
697 assert_eq!(dci, "0_1");
698 assert_eq!(*harq_process, Some(0));
699 assert_eq!(*ndi, Some(1));
700 assert_eq!(*rv_idx, Some(0));
701 assert_eq!(*harq_feedback_timing, None);
702 }
703 _ => panic!("Expected Pdcch channel data"),
704 }
705 }
706
707 #[test]
708 fn test_parse_pdcch_dci_1_1() {
709 let lines: Vec<String> = vec![
710 "10:32:22.293 [PHY] DL 0001 01 4601 213.13 PDCCH: ss_id=2 cce_index=4 al=2 dci=1_1".into(),
711 "\t\trb_alloc=0x32".into(),
712 "\t\tmcs1=27".into(),
713 "\t\tndi1=0".into(),
714 "\t\trv_idx1=0".into(),
715 "\t\tharq_process=0".into(),
716 "\t\tharq_feedback_timing=2".into(),
717 ];
718 let info = parse_phy_lines(&lines, Direction::DL).unwrap();
719 match &info.channel_data {
720 PHYChannelData::Pdcch {
721 dci,
722 harq_process,
723 ndi,
724 rv_idx,
725 harq_feedback_timing,
726 } => {
727 assert_eq!(dci, "1_1");
728 assert_eq!(*harq_process, Some(0));
729 assert_eq!(*ndi, Some(0));
730 assert_eq!(*rv_idx, Some(0));
731 assert_eq!(*harq_feedback_timing, Some(2));
732 }
733 _ => panic!("Expected Pdcch channel data"),
734 }
735 }
736
737 #[test]
738 fn test_parse_pucch_ack() {
739 let line = "10:32:22.299 [PHY] UL 0001 01 4601 213.19 PUCCH: format=1 prb=50 prb2=0 symb=0:14 cs=1 occ=0 ack=1 snr=35.7 epre=-88.5";
740 let info = parse_phy_line(line, Direction::UL).unwrap();
741 assert!(matches!(info.channel_type, PHYChannelType::PUCCH));
742 match &info.channel_data {
743 PHYChannelData::Pucch {
744 format,
745 ack,
746 measurements,
747 } => {
748 assert_eq!(*format, Some(1));
749 assert_eq!(*ack, Some(true));
750 assert_eq!(measurements.epre, Some(-88.5));
751 assert_eq!(measurements.ta, None);
752 }
753 _ => panic!("Expected Pucch channel data"),
754 }
755 }
756
757 #[test]
758 fn test_parse_pucch_sr() {
759 let line = "13:20:47.884 [PHY] UL 003d 01 4644 960.8 PUCCH: format=1 prb=50 prb2=0 symb=0:14 cs=9 occ=2 sr=1 snr=18.9 epre=-53.8";
760 let info = parse_phy_line(line, Direction::UL).unwrap();
761 match &info.channel_data {
762 PHYChannelData::Pucch { format, ack, .. } => {
763 assert_eq!(*format, Some(1));
764 assert_eq!(*ack, None); }
766 _ => panic!("Expected Pucch channel data"),
767 }
768 }
769
770 #[test]
771 fn test_parse_harq_si() {
772 let line = "13:20:47.877 [PHY] DL - 01 ffff 960.0 PDSCH: harq=si prb=41:7 symb=2:12 CW0: tb_len=84 mod=2 rv_idx=0 cr=0.44";
773 let info = parse_phy_line(line, Direction::DL).unwrap();
774 assert_eq!(info.harq, None);
775 assert!(info.harq_si);
776 assert!(matches!(info.channel_type, PHYChannelType::PDSCH));
777 }
778
779 #[test]
780 fn test_parse_pucch_format2_no_ack() {
781 let line = "13:20:47.885 [PHY] UL 003d 01 4644 960.9 PUCCH: format=2 prb=1 prb2=49 symb=8:2 csi=0101 epre=-51.3";
782 let info = parse_phy_line(line, Direction::UL).unwrap();
783 match &info.channel_data {
784 PHYChannelData::Pucch {
785 format,
786 ack,
787 measurements,
788 } => {
789 assert_eq!(*format, Some(2));
790 assert_eq!(*ack, None); assert_eq!(measurements.csi, Some(5));
792 assert_eq!(measurements.epre, Some(-51.3));
793 }
794 _ => panic!("Expected Pucch channel data"),
795 }
796 }
797}