synapse_rs/models.rs
1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4/// Represents raw network connection data.
5///
6/// This struct serves as the entry point for Synapse metric calculations.
7/// All fields are optional to handle partial cases (e.g., Ethernet without Wi-Fi).
8#[derive(Debug, Clone, Copy, Default)]
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10pub struct NetworkData {
11 /// Download speed in Mbps.
12 pub down_mbps: Option<f64>,
13 /// Upload speed in Mbps.
14 pub up_mbps: Option<f64>,
15 /// Latency (Ping) in milliseconds.
16 pub ping_ms: Option<f64>,
17 /// Jitter (Ping variation) in milliseconds.
18 pub jitter_ms: Option<f64>,
19 /// Packet loss percentage (e.g., 1.0 for 1%).
20 pub packet_loss_percent: Option<f64>,
21 /// Received Signal Strength Indicator in dBm (e.g., -65.0). Wireless only.
22 pub rssi_dbm: Option<f64>,
23 /// Noise floor in dBm (e.g., -90.0). Wireless only.
24 pub noise_dbm: Option<f64>,
25 /// Wi-Fi channel width in MHz (e.g., 20, 40, 80, 160).
26 pub channel_width_mhz: Option<f64>,
27}
28
29impl NetworkData {
30 /// Small value to avoid division by zero in friction calculation.
31 const EPSILON: f64 = 1e-7;
32
33 /// Calculates the **VORTEX** score (Pure flow performance).
34 ///
35 /// The Vortex measures pipe efficiency. It combines volume (speeds)
36 /// and fluidity (latency/jitter), penalized by integrity (packet loss).
37 ///
38 /// # Returns
39 /// * `Some(score)`: If all performance data is present.
40 /// * `None`: If data is missing (ping, speed, etc.).
41 pub fn calculate_vortex(&self) -> Option<f64> {
42 let down = self.down_mbps?;
43 let up = self.up_mbps?;
44 let ping = self.ping_ms?;
45 let jitter = self.jitter_ms?;
46 let lost = self.packet_loss_percent?;
47
48 let down_score = (1.0 + down).log10();
49 let up_score = (1.0 + up).log10();
50 let volume = down_score * up_score;
51
52 let ping_seconds = ping / 1000.0;
53 let jitter_seconds = jitter / 1000.0;
54 let friction = ping_seconds + (3.0 * jitter_seconds) + Self::EPSILON;
55
56 let integrity = (1.0 - (lost / 100.0))
57 .clamp(0.0, 1.0)
58 .powf(10.0);
59
60 Some((volume / friction) * integrity)
61 }
62
63 /// Calculates the **RADIANCE** score (Physical signal quality).
64 ///
65 /// Radiance measures the cleanliness of the radio environment.
66 /// Based on SNR (Signal-to-Noise Ratio) and channel width.
67 ///
68 /// # Returns
69 /// * `Some(score)`: If Wi-Fi data is present.
70 /// * `None`: If wired (Ethernet) or read error.
71 pub fn calculate_radiance(&self) -> Option<f64> {
72 let width = self.channel_width_mhz?;
73 let rssi = self.rssi_dbm?;
74 let noise = self.noise_dbm?;
75
76 let width_factor = width / 20.0;
77 let snr = rssi - noise;
78
79 Some((width_factor * snr).max(0.0))
80 }
81
82 /// Calculates the final **AXON** score (Unified Metric).
83 ///
84 /// Axon is the geometric mean of Vortex (Perf) and Radiance (Phys).
85 /// It represents the "Real Health" of the connection.
86 pub fn calculate_axon(&self) -> Option<f64> {
87 let vx = self.calculate_vortex()?;
88 let rd = self.calculate_radiance()?;
89
90 Some((vx * rd).sqrt())
91 }
92}