Skip to main content

leodos_protocols/network/isl/
address.rs

1use zerocopy::FromBytes;
2use zerocopy::Immutable;
3use zerocopy::IntoBytes;
4use zerocopy::KnownLayout;
5use zerocopy::Unaligned;
6use zerocopy::network_endian::U32;
7
8use crate::network::isl::torus::Point;
9
10#[repr(transparent)]
11#[derive(
12    Copy, Clone, Debug, PartialEq, Eq, Hash, FromBytes, IntoBytes, Immutable, KnownLayout, Unaligned,
13)]
14/// A unique numeric identifier for a spacecraft in the constellation.
15pub struct SpacecraftId(pub U32);
16
17impl SpacecraftId {
18    /// Creates a new spacecraft ID from a raw `u32` value.
19    pub const fn new(id: u32) -> Self {
20        Self(U32::new(id))
21    }
22
23    /// Returns the underlying `u32` value.
24    pub fn get(&self) -> u32 {
25        self.0.get()
26    }
27
28    /// Encodes an orbital plane and satellite index into a spacecraft ID.
29    pub fn encode(orb: u8, sat: u8, num_sats: u8) -> Self {
30        Self::new((orb as u32 + 1) * num_sats as u32 + sat as u32)
31    }
32
33    /// Decodes this spacecraft ID into an `Address`.
34    pub fn to_address(&self, num_sats: u8) -> Address {
35        let n = num_sats as u32;
36        let orb = self.get() / n;
37        let sat = self.get() % n;
38        if orb == 0 {
39            Address::Ground { station: sat as u8 }
40        } else {
41            Address::Satellite(Point {
42                orb: (orb - 1) as u8,
43                sat: sat as u8,
44            })
45        }
46    }
47}
48
49#[repr(C)]
50#[derive(
51    FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable, Copy, Clone, Debug, PartialEq, Eq, Hash,
52)]
53/// Wire-format address for zerocopy serialization.
54pub struct RawAddress {
55    ground_or_orb: u8,
56    station_or_sat: u8,
57}
58
59/// A logical address in the ISL network.
60#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
61pub enum Address {
62    /// A ground station identified by its station number.
63    Ground {
64        /// Ground station index.
65        station: u8,
66    },
67    /// A specific satellite at a grid position.
68    Satellite(Point),
69    /// All satellites in a given orbital plane (multicast).
70    ServiceArea {
71        /// Orbital plane index.
72        orb: u8,
73    },
74}
75
76impl RawAddress {
77    /// Converts the wire-format address to an [`Address`].
78    pub fn parse(&self) -> Address {
79        if self.ground_or_orb == 0 {
80            Address::Ground {
81                station: self.station_or_sat,
82            }
83        } else if self.station_or_sat == 0 {
84            Address::ServiceArea {
85                orb: self.ground_or_orb - 1,
86            }
87        } else {
88            Address::Satellite(Point {
89                orb: self.ground_or_orb - 1,
90                sat: self.station_or_sat - 1,
91            })
92        }
93    }
94}
95
96impl From<Address> for RawAddress {
97    fn from(addr: Address) -> Self {
98        match addr {
99            Address::Ground { station } => Self {
100                ground_or_orb: 0,
101                station_or_sat: station,
102            },
103            Address::Satellite(Point { orb, sat }) => Self {
104                ground_or_orb: orb + 1,
105                station_or_sat: sat + 1,
106            },
107            Address::ServiceArea { orb } => Self {
108                ground_or_orb: orb + 1,
109                station_or_sat: 0,
110            },
111        }
112    }
113}
114
115impl Address {
116    /// Creates a ground station address.
117    pub fn ground(station: u8) -> Self {
118        Self::Ground { station: station }
119    }
120
121    /// Creates a satellite address from orbital plane and satellite indices.
122    pub fn satellite(orb: u8, sat: u8) -> Self {
123        Self::Satellite(Point { orb, sat })
124    }
125
126    /// Creates a service area (multicast) address for an orbital plane.
127    pub fn service_area(orb: u8) -> Self {
128        Self::ServiceArea { orb }
129    }
130
131    /// Returns `true` if this address can be used as a packet source.
132    pub fn is_valid_source(&self) -> bool {
133        !matches!(self, Address::ServiceArea { .. })
134    }
135
136    /// Returns `true` if this satellite falls within the given service area range.
137    pub fn is_in_service_area(&self, min_sat: u8, max_sat: u8) -> bool {
138        match self {
139            Address::Satellite(Point { sat, .. }) => {
140                if min_sat <= max_sat {
141                    (min_sat..=max_sat).contains(sat)
142                } else {
143                    (min_sat..).contains(sat) || (..=max_sat).contains(sat)
144                }
145            }
146            _ => false,
147        }
148    }
149}
150
151impl From<Address> for Point {
152    fn from(addr: Address) -> Self {
153        match addr {
154            Address::Satellite(Point { orb, sat }) => Point::new(sat, orb),
155            Address::ServiceArea { orb } => Point::new(0, orb),
156            Address::Ground { .. } => Point::new(0, 0),
157        }
158    }
159}
160
161impl From<Point> for Address {
162    fn from(point: Point) -> Self {
163        Address::Satellite(Point {
164            orb: point.orb,
165            sat: point.sat,
166        })
167    }
168}