leodos_protocols/transport/srspp/
rto.rs1pub trait RtoPolicy {
3 fn rto_ticks(&self, now_secs: u32) -> u32;
5}
6
7pub struct FixedRto {
9 rto_ticks: u32,
11}
12
13impl FixedRto {
14 pub fn new(rto_ticks: u32) -> Self {
16 Self { rto_ticks }
17 }
18}
19
20impl RtoPolicy for FixedRto {
21 fn rto_ticks(&self, _now_secs: u32) -> u32 {
22 self.rto_ticks
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct ContactWindow {
29 pub station_id: u8,
31 pub start_secs: u32,
33 pub end_secs: u32,
35}
36
37pub struct ContactSchedule<const N: usize> {
39 windows: heapless::Vec<ContactWindow, N>,
41}
42
43impl<const N: usize> ContactSchedule<N> {
44 pub fn new() -> Self {
46 Self {
47 windows: heapless::Vec::new(),
48 }
49 }
50
51 pub fn add_window(&mut self, window: ContactWindow) -> Result<(), ContactWindow> {
53 let pos = self
54 .windows
55 .iter()
56 .position(|w| w.start_secs > window.start_secs)
57 .unwrap_or(self.windows.len());
58
59 self.windows.insert(pos, window).map_err(|e| e)
60 }
61
62 pub fn in_window(&self, now_secs: u32) -> bool {
64 self.windows
65 .iter()
66 .any(|w| now_secs >= w.start_secs && now_secs < w.end_secs)
67 }
68
69 pub fn next_window(&self, now_secs: u32) -> Option<&ContactWindow> {
71 self.windows.iter().find(|w| w.start_secs > now_secs)
72 }
73}
74
75pub struct OrbitAwareRto<const N: usize> {
77 isl_rto_ticks: u32,
79 margin_ticks: u32,
81 schedule: ContactSchedule<N>,
83}
84
85impl<const N: usize> OrbitAwareRto<N> {
86 pub fn new(isl_rto_ticks: u32, margin_ticks: u32, schedule: ContactSchedule<N>) -> Self {
88 Self {
89 isl_rto_ticks,
90 margin_ticks,
91 schedule,
92 }
93 }
94}
95
96impl<const N: usize> RtoPolicy for OrbitAwareRto<N> {
97 fn rto_ticks(&self, now_secs: u32) -> u32 {
98 if self.schedule.in_window(now_secs) {
99 return self.isl_rto_ticks;
100 }
101
102 match self.schedule.next_window(now_secs) {
103 Some(window) => {
104 let secs_until = window.start_secs - now_secs;
105 secs_until * 1000 + self.margin_ticks
106 }
107 None => self.isl_rto_ticks,
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn test_fixed_rto() {
118 let policy = FixedRto::new(500);
119 assert_eq!(policy.rto_ticks(0), 500);
120 assert_eq!(policy.rto_ticks(1000), 500);
121 assert_eq!(policy.rto_ticks(u32::MAX), 500);
122 }
123
124 #[test]
125 fn test_orbit_aware_in_window() {
126 let mut schedule = ContactSchedule::<4>::new();
127 schedule
128 .add_window(ContactWindow {
129 station_id: 1,
130 start_secs: 100,
131 end_secs: 200,
132 })
133 .unwrap();
134
135 let policy = OrbitAwareRto::new(50, 500, schedule);
136
137 assert_eq!(policy.rto_ticks(100), 50);
138 assert_eq!(policy.rto_ticks(150), 50);
139 assert_eq!(policy.rto_ticks(199), 50);
140 }
141
142 #[test]
143 fn test_orbit_aware_between_windows() {
144 let mut schedule = ContactSchedule::<4>::new();
145 schedule
146 .add_window(ContactWindow {
147 station_id: 1,
148 start_secs: 100,
149 end_secs: 200,
150 })
151 .unwrap();
152 schedule
153 .add_window(ContactWindow {
154 station_id: 2,
155 start_secs: 500,
156 end_secs: 600,
157 })
158 .unwrap();
159
160 let policy = OrbitAwareRto::new(50, 500, schedule);
161
162 assert_eq!(policy.rto_ticks(0), 100 * 1000 + 500);
163 assert_eq!(policy.rto_ticks(250), 250 * 1000 + 500);
164 }
165
166 #[test]
167 fn test_orbit_aware_no_future_windows() {
168 let mut schedule = ContactSchedule::<4>::new();
169 schedule
170 .add_window(ContactWindow {
171 station_id: 1,
172 start_secs: 100,
173 end_secs: 200,
174 })
175 .unwrap();
176
177 let policy = OrbitAwareRto::new(50, 500, schedule);
178
179 assert_eq!(policy.rto_ticks(300), 50);
180 }
181
182 #[test]
183 fn test_contact_schedule_queries() {
184 let mut schedule = ContactSchedule::<4>::new();
185 schedule
186 .add_window(ContactWindow {
187 station_id: 1,
188 start_secs: 100,
189 end_secs: 200,
190 })
191 .unwrap();
192 schedule
193 .add_window(ContactWindow {
194 station_id: 2,
195 start_secs: 300,
196 end_secs: 400,
197 })
198 .unwrap();
199
200 assert!(!schedule.in_window(50));
201 assert!(schedule.in_window(100));
202 assert!(schedule.in_window(150));
203 assert!(!schedule.in_window(200));
204 assert!(!schedule.in_window(250));
205 assert!(schedule.in_window(300));
206 assert!(schedule.in_window(350));
207 assert!(!schedule.in_window(400));
208
209 assert_eq!(schedule.next_window(0).unwrap().station_id, 1);
210 assert_eq!(schedule.next_window(150).unwrap().station_id, 2);
211 assert!(schedule.next_window(350).is_none());
212 }
213}