1#![doc = document_features::document_features!()]
31#![no_std]
174
175use portable_atomic::AtomicI64;
176
177use zerocopy::little_endian::{U32, U64};
178use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
179
180use embassy_futures::join::{join, join3};
181use embassy_futures::select::{select3, Either3};
182use embassy_sync::pubsub::{PubSubBehavior, Subscriber};
183
184use embassy_time::{with_timeout, Duration, Instant};
185use esp_radio::esp_now::WifiPhyRate;
186use esp_radio::wifi::{ClientConfig, CsiConfig, Interfaces, Protocol, WifiController};
187
188use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
189
190use embassy_sync::pubsub::PubSubChannel;
191use embassy_sync::signal::Signal;
192
193use heapless::Vec;
194extern crate alloc;
195use alloc::collections::BTreeMap;
196use serde::{Deserialize, Serialize};
197
198pub mod central;
199pub mod config;
200pub mod csi;
201pub mod logging;
202pub mod peripheral;
203pub mod time;
204
205use crate::central::esp_now::run_esp_now_central;
206use crate::central::sta::{run_sta_connect, sta_init};
207use crate::config::CsiConfig as CsiConfiguration;
208use crate::csi::{CSIDataPacket, RxCSIFmt};
209use crate::peripheral::esp_now::run_esp_now_peripheral;
210
211const PROC_CSI_CH_CAPACITY: usize = 20;
212const PROC_CSI_CH_SUBS: usize = 2;
213
214static CSI_PACKET: PubSubChannel<
216 CriticalSectionRawMutex,
217 CSIDataPacket,
218 PROC_CSI_CH_CAPACITY,
219 PROC_CSI_CH_SUBS,
220 2,
221> = PubSubChannel::new();
222
223static IS_COLLECTOR: AtomicBool = AtomicBool::new(false);
224static COLLECTION_MODE_CHANGED: Signal<CriticalSectionRawMutex, ()> = Signal::new();
225static CENTRAL_MAGIC_NUMBER: u32 = 0xA8912BF0;
226static PERIPHERAL_MAGIC_NUMBER: u32 = !CENTRAL_MAGIC_NUMBER;
227
228use portable_atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
229#[cfg(feature = "statistics")]
231struct GlobalStats {
232 tx_count: AtomicU64,
234 rx_count: AtomicU64,
236 rx_drop_count: AtomicU32,
238 capture_start_time: AtomicU64,
240 tx_rate_hz: AtomicU32,
242 rx_rate_hz: AtomicU32,
244 one_way_latency: AtomicI64,
246 two_way_latency: AtomicI64,
248}
249
250#[cfg(feature = "statistics")]
251static STATS: GlobalStats = GlobalStats {
252 tx_count: AtomicU64::new(0),
253 rx_count: AtomicU64::new(0),
254 rx_drop_count: AtomicU32::new(0),
255 capture_start_time: AtomicU64::new(0),
256 tx_rate_hz: AtomicU32::new(0),
257 rx_rate_hz: AtomicU32::new(0),
258 one_way_latency: AtomicI64::new(0),
259 two_way_latency: AtomicI64::new(0),
260};
261static STOP_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
272
273fn set_runtime_collection_mode(is_collector: bool) {
275 IS_COLLECTOR.store(is_collector, Ordering::Relaxed);
276 COLLECTION_MODE_CHANGED.signal(());
277}
278
279async fn csi_data_collection(client: &mut CSINodeClient, duration: u64) {
280 with_timeout(Duration::from_secs(duration), async {
281 loop {
282 client.print_csi_w_metadata().await;
283 }
284 })
285 .await
286 .unwrap_err();
287 client.send_stop().await;
288}
289
290pub struct EspNowConfig {
294 phy_rate: WifiPhyRate,
295 channel: u8,
296}
297
298impl Default for EspNowConfig {
299 fn default() -> Self {
300 Self {
301 phy_rate: WifiPhyRate::RateMcs0Lgi,
302 channel: 11,
303 }
304 }
305}
306
307#[derive(Debug, Clone)]
309pub struct WifiSnifferConfig {
310 mac_filter: Option<[u8; 6]>,
311}
312
313impl Default for WifiSnifferConfig {
314 fn default() -> Self {
315 Self { mac_filter: None }
316 }
317}
318
319#[derive(Debug, Clone)]
321pub struct WifiStationConfig {
322 pub client_config: ClientConfig,
323}
324
325pub enum CentralOpMode {
329 EspNow(EspNowConfig),
330 WifiStation(WifiStationConfig),
331}
332
333pub enum PeripheralOpMode {
336 EspNow(EspNowConfig),
337 WifiSniffer(WifiSnifferConfig),
338}
339
340pub enum Node {
342 Peripheral(PeripheralOpMode), Central(CentralOpMode), }
345
346#[derive(PartialEq, Eq, Clone, Copy)]
353pub enum CollectionMode {
354 Collector,
356 Listener,
358}
359
360pub struct CSINodeHardware<'a> {
362 interfaces: &'a mut Interfaces<'static>,
363 controller: &'a mut WifiController<'static>,
364}
365
366impl<'a> CSINodeHardware<'a> {
367 pub fn new(
369 interfaces: &'a mut Interfaces<'static>,
370 controller: &'a mut WifiController<'static>,
371 ) -> Self {
372 Self {
373 interfaces,
374 controller,
375 }
376 }
377}
378
379type CSIRxSubscriber = Subscriber<
380 'static,
381 CriticalSectionRawMutex,
382 CSIDataPacket,
383 PROC_CSI_CH_CAPACITY,
384 PROC_CSI_CH_SUBS,
385 2,
386>;
387
388pub struct CSINodeClient {
390 csi_subscriber: CSIRxSubscriber,
391}
392
393impl CSINodeClient {
394 pub fn new() -> Self {
396 Self {
397 csi_subscriber: CSI_PACKET.subscriber().unwrap(),
398 }
399 }
400
401 pub async fn get_csi_data(&mut self) -> CSIDataPacket {
403 self.csi_subscriber.next_message_pure().await
404 }
405
406 pub async fn print_csi_w_metadata(&mut self) {
408 let packet = self.get_csi_data().await;
409 packet.print_csi_w_metadata();
410 }
411
412 pub async fn send_stop(&self) {
414 STOP_SIGNAL.signal(());
415 }
416}
417
418#[derive(Serialize, Deserialize, Debug, PartialEq)]
420pub struct ControlPacket {
421 magic_number: u32,
422 pub is_collector: bool,
423 pub central_send_uptime: u64,
424 pub latency_offset: i64,
425}
426
427impl ControlPacket {
428 pub fn new(is_collector: bool, latency_offset: i64) -> Self {
430 Self {
431 magic_number: CENTRAL_MAGIC_NUMBER.into(),
432 is_collector,
433 central_send_uptime: Instant::now().as_micros(),
434 latency_offset,
435 }
436 }
437}
438
439#[derive(Serialize, Deserialize, Debug, PartialEq)]
441pub struct PeripheralPacket {
442 magic_number: u32, recv_uptime: u64, send_uptime: u64, central_send_uptime: u64, }
447
448impl PeripheralPacket {
449 pub fn new(recv_uptime: u64, central_send_uptime: u64) -> Self {
451 Self {
452 magic_number: PERIPHERAL_MAGIC_NUMBER,
453 recv_uptime,
454 send_uptime: Instant::now().as_micros(),
455 central_send_uptime,
456 }
457 }
458}
459
460fn reset_globals() {
461 #[cfg(feature = "statistics")]
462 {
463 STATS.tx_count.store(0, Ordering::Relaxed);
464 STATS.rx_drop_count.store(0, Ordering::Relaxed);
465 STATS.tx_count.store(0, Ordering::Relaxed);
466 STATS.tx_rate_hz.store(0, Ordering::Relaxed);
467 STATS.rx_rate_hz.store(0, Ordering::Relaxed);
468 STATS.one_way_latency.store(0, Ordering::Relaxed);
469 STATS.two_way_latency.store(0, Ordering::Relaxed);
470 }
471 #[cfg(feature = "statistics")]
472 reset_global_log_drops();
473}
474
475pub struct CSINode<'a> {
480 kind: Node,
481 collection_mode: CollectionMode,
482 csi_config: Option<CsiConfiguration>,
484 traffic_freq_hz: Option<u16>,
486 hardware: CSINodeHardware<'a>,
487 protocol: Option<Protocol>,
488 rate: Option<WifiPhyRate>,
489}
490
491impl<'a> CSINode<'a> {
492 pub fn new(
494 kind: Node,
495 collection_mode: CollectionMode,
496 csi_config: Option<CsiConfiguration>,
497 traffic_freq_hz: Option<u16>,
498 hardware: CSINodeHardware<'a>,
499 ) -> Self {
500 Self {
501 kind,
502 collection_mode,
503 csi_config,
504 traffic_freq_hz,
505 hardware,
506 protocol: None,
507 rate: Some(WifiPhyRate::RateMcs0Lgi),
508 }
509 }
510
511 pub fn new_central_node(
513 op_mode: CentralOpMode,
514 collection_mode: CollectionMode,
515 csi_config: Option<CsiConfiguration>,
516 traffic_freq_hz: Option<u16>,
517 hardware: CSINodeHardware<'a>,
518 ) -> Self {
519 Self {
520 kind: Node::Central(op_mode),
521 collection_mode,
522 csi_config,
523 traffic_freq_hz,
524 hardware,
525 protocol: None,
526 rate: Some(WifiPhyRate::RateMcs0Lgi),
527 }
528 }
529
530 pub fn get_node_type(&self) -> &Node {
532 &self.kind
533 }
534
535 pub fn get_collection_mode(&self) -> CollectionMode {
537 self.collection_mode
538 }
539
540 pub fn get_central_op_mode(&self) -> Option<&CentralOpMode> {
542 match &self.kind {
543 Node::Central(mode) => Some(mode),
544 Node::Peripheral(_) => None,
545 }
546 }
547
548 pub fn get_peripheral_op_mode(&self) -> Option<&PeripheralOpMode> {
550 match &self.kind {
551 Node::Peripheral(mode) => Some(mode),
552 Node::Central(_) => None,
553 }
554 }
555
556 pub fn set_csi_config(&mut self, config: CsiConfiguration) {
558 self.csi_config = Some(config);
559 }
560
561 pub fn set_station_config(&mut self, config: WifiStationConfig) {
563 if let Node::Central(CentralOpMode::WifiStation(_)) = &mut self.kind {
564 self.kind = Node::Central(CentralOpMode::WifiStation(config));
565 }
566 }
567
568 pub fn set_traffic_frequency(&mut self, freq_hz: u16) {
570 self.traffic_freq_hz = Some(freq_hz);
571 }
572
573 pub fn set_collection_mode(&mut self, mode: CollectionMode) {
575 self.collection_mode = mode;
576 }
577
578 pub fn set_op_mode(&mut self, mode: Node) {
580 self.kind = mode;
581 }
582
583 pub fn set_protocol(&mut self, protocol: Protocol) {
585 self.protocol = Some(protocol);
586 }
587
588 pub fn set_rate(&mut self, rate: WifiPhyRate) {
590 self.rate = Some(rate);
591 }
592
593 pub async fn run_duration(&mut self, duration: u64, mut client: &mut CSINodeClient) {
597 let interfaces = &mut self.hardware.interfaces;
598 let controller = &mut self.hardware.controller;
599
600 let sta_interface = if let Node::Central(CentralOpMode::WifiStation(config)) = &self.kind {
602 Some(sta_init(&mut interfaces.sta, config, controller))
603 } else {
604 None
605 };
606
607 controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap();
609
610 let config = match self.csi_config {
612 Some(ref config) => {
613 log_ln!("CSI Configuration Set: {:?}", config);
614 build_csi_config(config)
615 }
616 None => {
617 let default_config = CsiConfiguration::default();
618 log_ln!(
619 "No CSI Configuration Provided. Going with defaults: {:?}",
620 default_config
621 );
622 build_csi_config(&default_config)
623 }
624 };
625
626 if let Some(protocol) = self.protocol.take() {
628 let old_protocol = reconstruct_protocol(&protocol);
629 controller.set_protocol(protocol.into()).unwrap();
630 self.protocol = Some(old_protocol);
631 }
632
633 controller.start_async().await.unwrap();
635 log_ln!("Wi-Fi Controller Started");
636 let is_collector = self.collection_mode == CollectionMode::Collector;
637 IS_COLLECTOR.store(is_collector, Ordering::Relaxed);
638
639 set_csi(controller, config);
641 let sniffer: &esp_radio::wifi::Sniffer<'_> = &interfaces.sniffer;
642
643 match &self.kind {
645 Node::Peripheral(op_mode) => match op_mode {
646 PeripheralOpMode::EspNow(esp_now_config) => {
647 if let Some(rate) = self.rate.take() {
649 let old_rate = reconstruct_wifi_rate(&rate);
650 let _ = interfaces.esp_now.set_rate(rate);
651 self.rate = Some(old_rate);
652 }
653
654 let main_task = run_esp_now_peripheral(
655 &mut interfaces.esp_now,
656 esp_now_config,
657 self.traffic_freq_hz,
658 );
659 join3(
660 main_task,
661 run_process_csi_packet(),
662 csi_data_collection(client, duration),
663 )
664 .await;
665 }
666 PeripheralOpMode::WifiSniffer(sniffer_config) => {
667 let sniffer = &interfaces.sniffer;
668 sniffer.set_promiscuous_mode(true).unwrap();
669 join(
670 run_process_csi_packet(),
671 csi_data_collection(client, duration),
672 )
673 .await;
674 run_process_csi_packet().await;
675 sniffer.set_promiscuous_mode(false).unwrap();
676 }
677 },
678 Node::Central(op_mode) => match op_mode {
679 CentralOpMode::EspNow(esp_now_config) => {
680 if let Some(rate) = self.rate.take() {
682 let old_rate = reconstruct_wifi_rate(&rate);
683 let _ = interfaces.esp_now.set_rate(rate);
684 self.rate = Some(old_rate);
685 }
686
687 let main_task = run_esp_now_central(
688 &mut interfaces.esp_now,
689 interfaces.sta.mac_address(),
690 esp_now_config,
691 self.traffic_freq_hz,
692 is_collector,
693 );
694 join3(
695 main_task,
696 run_process_csi_packet(),
697 csi_data_collection(client, duration),
698 )
699 .await;
700 }
701 CentralOpMode::WifiStation(sta_config) => {
702 let (sta_stack, sta_runner) = sta_interface.unwrap();
708
709 let main_task =
710 run_sta_connect(controller, self.traffic_freq_hz, sta_stack, sta_runner);
711 join3(
712 main_task,
713 run_process_csi_packet(),
714 csi_data_collection(client, duration),
715 )
716 .await;
717 }
718 },
719 }
720
721 STOP_SIGNAL.reset();
722 let _ = controller.stop_async().await;
723 reset_globals();
724 }
725
726 pub async fn run(&mut self) {
730 let interfaces = &mut self.hardware.interfaces;
731 let controller = &mut self.hardware.controller;
732
733 let sta_interface = if let Node::Central(CentralOpMode::WifiStation(config)) = &self.kind {
735 Some(sta_init(&mut interfaces.sta, config, controller))
736 } else {
737 None
738 };
739
740 controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap();
742
743 let config = match self.csi_config {
745 Some(ref config) => {
746 log_ln!("CSI Configuration Set: {:?}", config);
747 build_csi_config(config)
748 }
749 None => {
750 let default_config = CsiConfiguration::default();
751 log_ln!(
752 "No CSI Configuration Provided. Going with defaults: {:?}",
753 default_config
754 );
755 build_csi_config(&default_config)
756 }
757 };
758
759 if let Some(protocol) = self.protocol.take() {
761 let old_protocol = reconstruct_protocol(&protocol);
762 controller.set_protocol(protocol.into()).unwrap();
763 self.protocol = Some(old_protocol);
764 }
765
766 controller.start_async().await.unwrap();
768 log_ln!("Wi-Fi Controller Started");
769 let is_collector = self.collection_mode == CollectionMode::Collector;
770 IS_COLLECTOR.store(is_collector, Ordering::Relaxed);
771
772 set_csi(controller, config);
774 let sniffer: &esp_radio::wifi::Sniffer<'_> = &interfaces.sniffer;
775
776 match &self.kind {
778 Node::Peripheral(op_mode) => match op_mode {
779 PeripheralOpMode::EspNow(esp_now_config) => {
780 if let Some(rate) = self.rate.take() {
782 let old_rate = reconstruct_wifi_rate(&rate);
783 let _ = interfaces.esp_now.set_rate(rate);
784 self.rate = Some(old_rate);
785 }
786
787 let main_task = run_esp_now_peripheral(
788 &mut interfaces.esp_now,
789 esp_now_config,
790 self.traffic_freq_hz,
791 );
792 join(main_task, run_process_csi_packet()).await;
793 }
794 PeripheralOpMode::WifiSniffer(sniffer_config) => {
795 let sniffer = &interfaces.sniffer;
796 sniffer.set_promiscuous_mode(true).unwrap();
797 run_process_csi_packet().await;
798 sniffer.set_promiscuous_mode(false).unwrap();
799 }
800 },
801 Node::Central(op_mode) => match op_mode {
802 CentralOpMode::EspNow(esp_now_config) => {
803 if let Some(rate) = self.rate.take() {
805 let old_rate = reconstruct_wifi_rate(&rate);
806 let _ = interfaces.esp_now.set_rate(rate);
807 self.rate = Some(old_rate);
808 }
809
810 let main_task = run_esp_now_central(
811 &mut interfaces.esp_now,
812 interfaces.sta.mac_address(),
813 esp_now_config,
814 self.traffic_freq_hz,
815 is_collector,
816 );
817 join(main_task, run_process_csi_packet()).await;
818 }
819 CentralOpMode::WifiStation(sta_config) => {
820 let (sta_stack, sta_runner) = sta_interface.unwrap();
826
827 let main_task =
828 run_sta_connect(controller, self.traffic_freq_hz, sta_stack, sta_runner);
829 join(main_task, run_process_csi_packet()).await;
830 }
831 },
832 }
833
834 STOP_SIGNAL.reset();
835 let _ = controller.stop_async().await;
836 reset_globals();
837 }
838}
839
840#[cfg(feature = "esp32c6")]
841fn build_csi_config(csi_config: &CsiConfiguration) -> CsiConfig {
842 CsiConfig {
843 enable: csi_config.enable,
844 acquire_csi_legacy: csi_config.acquire_csi_legacy,
845 acquire_csi_ht20: csi_config.acquire_csi_ht20,
846 acquire_csi_ht40: csi_config.acquire_csi_ht40,
847 acquire_csi_su: csi_config.acquire_csi_su,
848 acquire_csi_mu: csi_config.acquire_csi_mu,
849 acquire_csi_dcm: csi_config.acquire_csi_dcm,
850 acquire_csi_beamformed: csi_config.acquire_csi_beamformed,
851 acquire_csi_he_stbc: csi_config.acquire_csi_he_stbc,
852 val_scale_cfg: csi_config.val_scale_cfg,
853 dump_ack_en: csi_config.dump_ack_en,
854 reserved: csi_config.reserved,
855 }
856}
857
858#[cfg(not(feature = "esp32c6"))]
859fn build_csi_config(csi_config: &CsiConfiguration) -> CsiConfig {
860 CsiConfig {
861 lltf_en: csi_config.lltf_en,
862 htltf_en: csi_config.htltf_en,
863 stbc_htltf2_en: csi_config.stbc_htltf2_en,
864 ltf_merge_en: csi_config.ltf_merge_en,
865 channel_filter_en: csi_config.channel_filter_en,
866 manu_scale: csi_config.manu_scale,
867 shift: csi_config.shift,
868 dump_ack_en: csi_config.dump_ack_en,
869 }
870}
871
872#[cfg(feature = "statistics")]
874pub fn get_total_rx_packets() -> u64 {
875 STATS.rx_count.load(Ordering::Relaxed)
876}
877
878#[cfg(feature = "statistics")]
880pub fn get_total_tx_packets() -> u64 {
881 STATS.tx_count.load(Ordering::Relaxed)
882}
883
884#[cfg(feature = "statistics")]
886pub fn get_rx_rate_hz() -> u32 {
887 STATS.rx_rate_hz.load(Ordering::Relaxed)
888}
889
890#[cfg(feature = "statistics")]
892pub fn get_tx_rate_hz() -> u32 {
893 STATS.tx_rate_hz.load(Ordering::Relaxed)
894}
895
896#[cfg(feature = "statistics")]
898pub fn get_pps_rx() -> u64 {
899 let start_time = Instant::from_ticks(STATS.capture_start_time.load(Ordering::Relaxed));
900 let elapsed_secs = start_time.elapsed().as_secs() as u64;
901 let total_packets = STATS.rx_count.load(Ordering::Relaxed);
902 if elapsed_secs == 0 {
903 return total_packets;
904 }
905 total_packets / elapsed_secs
906}
907
908#[cfg(feature = "statistics")]
910pub fn get_pps_tx() -> u64 {
911 let start_time = Instant::from_ticks(STATS.capture_start_time.load(Ordering::Relaxed));
912 let elapsed_secs = start_time.elapsed().as_secs() as u64;
913 let total_packets = STATS.tx_count.load(Ordering::Relaxed);
914 if elapsed_secs == 0 {
915 return total_packets;
916 }
917 total_packets / elapsed_secs
918}
919
920#[cfg(feature = "statistics")]
922pub fn get_dropped_packets_rx() -> u32 {
923 STATS.rx_drop_count.load(Ordering::Relaxed)
924}
925
926#[cfg(feature = "statistics")]
928pub fn get_one_way_latency() -> i64 {
929 STATS.one_way_latency.load(Ordering::Relaxed)
930}
931
932#[cfg(feature = "statistics")]
934pub fn get_two_way_latency() -> i64 {
935 STATS.two_way_latency.load(Ordering::Relaxed)
936}
937
938fn set_csi(controller: &mut WifiController, config: CsiConfig) {
940 controller
942 .set_csi(config, |info: esp_radio::wifi::wifi_csi_info_t| {
943 capture_csi_info(info);
944 })
945 .unwrap();
946}
947
948fn capture_csi_info(info: esp_radio::wifi::wifi_csi_info_t) {
950 if IS_COLLECTOR.load(Ordering::Relaxed) == false {
951 return;
952 }
953
954 let rssi = if info.rx_ctrl.rssi() > 127 {
955 info.rx_ctrl.rssi() - 256
956 } else {
957 info.rx_ctrl.rssi()
958 };
959
960 let mut csi_data = Vec::<i8, 612>::new();
961 let csi_buf_len = info.len;
963 let csi_slice =
964 unsafe { core::slice::from_raw_parts(info.buf as *const i8, csi_buf_len as usize) };
965 match csi_data.extend_from_slice(csi_slice) {
966 Ok(_) => {}
967 Err(_) => {
968 #[cfg(feature = "statistics")]
969 STATS.rx_drop_count.fetch_add(1, Ordering::Relaxed);
970 return;
971 }
972 }
973
974 #[cfg(not(feature = "esp32c6"))]
975 let csi_packet = CSIDataPacket {
976 sequence_number: info.rx_seq,
977 data_format: RxCSIFmt::Undefined,
978 date_time: None,
979 mac: [
980 info.mac[0],
981 info.mac[1],
982 info.mac[2],
983 info.mac[3],
984 info.mac[4],
985 info.mac[5],
986 ],
987 rssi,
988 bandwidth: info.rx_ctrl.cwb(),
989 antenna: info.rx_ctrl.ant(),
990 rate: info.rx_ctrl.rate(),
991 sig_mode: info.rx_ctrl.sig_mode(),
992 mcs: info.rx_ctrl.mcs(),
993 smoothing: info.rx_ctrl.smoothing(),
994 not_sounding: info.rx_ctrl.not_sounding(),
995 aggregation: info.rx_ctrl.aggregation(),
996 stbc: info.rx_ctrl.stbc(),
997 fec_coding: info.rx_ctrl.fec_coding(),
998 sgi: info.rx_ctrl.sgi(),
999 noise_floor: info.rx_ctrl.noise_floor(),
1000 ampdu_cnt: info.rx_ctrl.ampdu_cnt(),
1001 channel: info.rx_ctrl.channel(),
1002 secondary_channel: info.rx_ctrl.secondary_channel(),
1003 timestamp: info.rx_ctrl.timestamp(),
1004 rx_state: info.rx_ctrl.rx_state(),
1005 sig_len: info.rx_ctrl.sig_len(),
1006 csi_data_len: csi_buf_len,
1007 csi_data: csi_data,
1008 };
1009
1010 #[cfg(feature = "esp32c6")]
1011 let csi_packet = CSIDataPacket {
1012 mac: [
1013 info.mac[0],
1014 info.mac[1],
1015 info.mac[2],
1016 info.mac[3],
1017 info.mac[4],
1018 info.mac[5],
1019 ],
1020 rssi,
1021 timestamp: info.rx_ctrl.timestamp(),
1022 rate: info.rx_ctrl.rate(),
1023 noise_floor: info.rx_ctrl.noise_floor(),
1024 sig_len: info.rx_ctrl.sig_len(),
1025 rx_state: info.rx_ctrl.rx_state(),
1026 dump_len: info.rx_ctrl.dump_len(),
1027 he_sigb_len: info.rx_ctrl.he_sigb_len(),
1028 cur_single_mpdu: info.rx_ctrl.cur_single_mpdu(),
1029 cur_bb_format: info.rx_ctrl.cur_bb_format(),
1030 rx_channel_estimate_info_vld: info.rx_ctrl.rx_channel_estimate_info_vld(),
1031 rx_channel_estimate_len: info.rx_ctrl.rx_channel_estimate_len(),
1032 second: info.rx_ctrl.second(),
1033 channel: info.rx_ctrl.channel(),
1034 is_group: info.rx_ctrl.is_group(),
1035 rxend_state: info.rx_ctrl.rxend_state(),
1036 rxmatch3: info.rx_ctrl.rxmatch3(),
1037 rxmatch2: info.rx_ctrl.rxmatch2(),
1038 rxmatch1: info.rx_ctrl.rxmatch1(),
1039 rxmatch0: info.rx_ctrl.rxmatch0(),
1040 date_time: None,
1041 sequence_number: info.rx_seq,
1042 data_format: RxCSIFmt::Undefined,
1043 csi_data_len: info.len as u16,
1044 csi_data: csi_data,
1045 };
1046
1047 CSI_PACKET.publish_immediate(csi_packet);
1048 #[cfg(feature = "statistics")]
1049 STATS.rx_count.fetch_add(1, Ordering::Relaxed);
1050}
1051
1052pub async fn run_process_csi_packet() {
1054 #[cfg(feature = "statistics")]
1056 STATS
1057 .capture_start_time
1058 .store(Instant::now().as_ticks(), Ordering::Relaxed);
1059 let mut csi_packet_sub = CSI_PACKET.subscriber().unwrap();
1061 let mut peer_tracker: BTreeMap<[u8; 6], u16> = BTreeMap::new();
1063 let mut is_collector = IS_COLLECTOR.load(Ordering::Relaxed);
1064
1065 loop {
1066 match select3(
1067 STOP_SIGNAL.wait(),
1068 COLLECTION_MODE_CHANGED.wait(),
1069 csi_packet_sub.next_message_pure(),
1070 )
1071 .await
1072 {
1073 Either3::First(_) => {
1074 STOP_SIGNAL.signal(());
1075 break;
1076 }
1077 Either3::Second(_) => {
1078 COLLECTION_MODE_CHANGED.reset();
1079 is_collector = IS_COLLECTOR.load(Ordering::Relaxed);
1080 reset_globals();
1081 #[cfg(feature = "statistics")]
1082 STATS
1083 .capture_start_time
1084 .store(Instant::now().as_ticks(), Ordering::Relaxed);
1085 }
1086 Either3::Third(csi_packet) => {
1087 #[cfg(feature = "statistics")]
1088 {
1089 if is_collector {
1090 let current_seq = csi_packet.sequence_number;
1091
1092 if let Some(&last_seq) = peer_tracker.get(&csi_packet.mac) {
1094 let diff = (current_seq.wrapping_sub(last_seq)) & 0x0FFF;
1098
1099 if diff > 1 {
1100 let lost = (diff - 1) as u32;
1101
1102 if lost < 500 {
1104 STATS.rx_drop_count.fetch_add(lost, Ordering::Relaxed);
1105 }
1106 }
1107 }
1108
1109 peer_tracker.insert(csi_packet.mac, current_seq);
1111 }
1113 }
1114 }
1115 }
1116 }
1117}
1118
1119#[cfg(feature = "statistics")]
1120use crate::logging::logging::{get_log_packet_drops, reset_global_log_drops};
1121
1122fn reconstruct_wifi_rate(rate: &WifiPhyRate) -> WifiPhyRate {
1123 match rate {
1124 WifiPhyRate::Rate1mL => WifiPhyRate::Rate1mL,
1125 WifiPhyRate::Rate2m => WifiPhyRate::Rate2m,
1126 WifiPhyRate::Rate5mL => WifiPhyRate::Rate5mL,
1127 WifiPhyRate::Rate11mL => WifiPhyRate::Rate11mL,
1128 WifiPhyRate::Rate2mS => WifiPhyRate::Rate2mS,
1129 WifiPhyRate::Rate5mS => WifiPhyRate::Rate5mS,
1130 WifiPhyRate::Rate11mS => WifiPhyRate::Rate11mS,
1131 WifiPhyRate::Rate48m => WifiPhyRate::Rate48m,
1132 WifiPhyRate::Rate24m => WifiPhyRate::Rate24m,
1133 WifiPhyRate::Rate12m => WifiPhyRate::Rate12m,
1134 WifiPhyRate::Rate6m => WifiPhyRate::Rate6m,
1135 WifiPhyRate::Rate54m => WifiPhyRate::Rate54m,
1136 WifiPhyRate::Rate36m => WifiPhyRate::Rate36m,
1137 WifiPhyRate::Rate18m => WifiPhyRate::Rate18m,
1138 WifiPhyRate::Rate9m => WifiPhyRate::Rate9m,
1139 WifiPhyRate::RateMcs0Lgi => WifiPhyRate::RateMcs0Lgi,
1140 WifiPhyRate::RateMcs1Lgi => WifiPhyRate::RateMcs1Lgi,
1141 WifiPhyRate::RateMcs2Lgi => WifiPhyRate::RateMcs2Lgi,
1142 WifiPhyRate::RateMcs3Lgi => WifiPhyRate::RateMcs3Lgi,
1143 WifiPhyRate::RateMcs4Lgi => WifiPhyRate::RateMcs4Lgi,
1144 WifiPhyRate::RateMcs5Lgi => WifiPhyRate::RateMcs5Lgi,
1145 WifiPhyRate::RateMcs6Lgi => WifiPhyRate::RateMcs6Lgi,
1146 WifiPhyRate::RateMcs7Lgi => WifiPhyRate::RateMcs7Lgi,
1147 WifiPhyRate::RateMcs0Sgi => WifiPhyRate::RateMcs0Sgi,
1148 WifiPhyRate::RateMcs1Sgi => WifiPhyRate::RateMcs1Sgi,
1149 WifiPhyRate::RateMcs2Sgi => WifiPhyRate::RateMcs2Sgi,
1150 WifiPhyRate::RateMcs3Sgi => WifiPhyRate::RateMcs3Sgi,
1151 WifiPhyRate::RateMcs4Sgi => WifiPhyRate::RateMcs4Sgi,
1152 WifiPhyRate::RateMcs5Sgi => WifiPhyRate::RateMcs5Sgi,
1153 WifiPhyRate::RateMcs6Sgi => WifiPhyRate::RateMcs6Sgi,
1154 WifiPhyRate::RateMcs7Sgi => WifiPhyRate::RateMcs7Sgi,
1155 WifiPhyRate::RateLora250k => WifiPhyRate::RateLora250k,
1156 WifiPhyRate::RateLora500k => WifiPhyRate::RateLora500k,
1157 WifiPhyRate::RateMax => WifiPhyRate::RateMax,
1158 }
1159}
1160
1161fn reconstruct_protocol(protocol: &Protocol) -> Protocol {
1162 match protocol {
1163 Protocol::P802D11B => Protocol::P802D11B,
1164 Protocol::P802D11BG => Protocol::P802D11BG,
1165 Protocol::P802D11BGN => Protocol::P802D11BGN,
1166 Protocol::P802D11BGNLR => Protocol::P802D11BGNLR,
1167 Protocol::P802D11LR => Protocol::P802D11LR,
1168 Protocol::P802D11BGNAX => Protocol::P802D11BGNAX,
1169 _ => Protocol::P802D11BGNLR,
1170 }
1171}