Skip to main content

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}