Skip to main content

leodos_protocols/network/isl/
aoi.rs

1//! Area of Interest (AOI) for SpaceCoMP task scheduling.
2//!
3//! An AOI defines a rectangular region on the satellite mesh where data
4//! collection and processing occurs. The AOI is specified as a bounding box
5//! with upper-left and lower-right corners, handling wraparound on the torus.
6
7use crate::network::isl::torus::{Point, Torus};
8
9#[derive(Debug, Clone, Copy)]
10struct WrappedRange {
11    start: u8,
12    end: u8,
13    modulus: u8,
14}
15
16impl WrappedRange {
17    fn new(start: u8, end: u8, modulus: u8) -> Self {
18        Self { start, end, modulus }
19    }
20
21    fn contains(&self, val: u8) -> bool {
22        if self.start <= self.end {
23            val >= self.start && val <= self.end
24        } else {
25            val >= self.start || val <= self.end
26        }
27    }
28
29    fn span(&self) -> u8 {
30        if self.end >= self.start {
31            self.end - self.start + 1
32        } else {
33            self.modulus - self.start + self.end + 1
34        }
35    }
36
37    fn midpoint(&self) -> u8 {
38        let forward = Torus::distance(self.start, self.end, self.modulus);
39        let backward = Torus::distance(self.end, self.start, self.modulus);
40
41        if forward <= backward {
42            (self.start as u16 + forward as u16 / 2) as u8 % self.modulus
43        } else {
44            self.start.wrapping_sub(backward / 2) % self.modulus
45        }
46    }
47}
48
49/// A rectangular area of interest on the satellite torus grid.
50#[derive(Debug, Clone, Copy)]
51pub struct Aoi {
52    /// Upper-left corner of the bounding box (inclusive).
53    pub upper_left: Point,
54    /// Lower-right corner of the bounding box (inclusive).
55    pub lower_right: Point,
56}
57
58impl Aoi {
59    /// Creates a new AOI from upper-left and lower-right corners.
60    pub fn new(upper_left: Point, lower_right: Point) -> Self {
61        Self { upper_left, lower_right }
62    }
63
64    fn y_range(&self, torus: &Torus) -> WrappedRange {
65        WrappedRange::new(self.upper_left.orb, self.lower_right.orb, torus.num_orbs)
66    }
67
68    fn x_range(&self, torus: &Torus) -> WrappedRange {
69        WrappedRange::new(self.upper_left.sat, self.lower_right.sat, torus.num_sats)
70    }
71
72    /// Returns the center point of this AOI on the torus.
73    pub fn center(&self, torus: &Torus) -> Point {
74        Point::new(self.y_range(torus).midpoint(), self.x_range(torus).midpoint())
75    }
76
77    /// Returns whether the given point lies within this AOI.
78    pub fn contains(&self, torus: &Torus, point: Point) -> bool {
79        self.y_range(torus).contains(point.orb) && self.x_range(torus).contains(point.sat)
80    }
81
82    /// Returns the width of this AOI in grid cells.
83    pub fn width(&self, torus: &Torus) -> u8 {
84        self.x_range(torus).span()
85    }
86
87    /// Returns the height of this AOI in grid cells.
88    pub fn height(&self, torus: &Torus) -> u8 {
89        self.y_range(torus).span()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_center_simple() {
99        let torus = Torus::new(8, 8);
100        let aoi = Aoi::new(Point::new(1, 1), Point::new(3, 3));
101        assert_eq!(aoi.center(&torus), Point::new(2, 2));
102    }
103
104    #[test]
105    fn test_center_wraparound() {
106        let torus = Torus::new(8, 8);
107        let aoi = Aoi::new(Point::new(6, 6), Point::new(2, 2));
108        assert_eq!(aoi.center(&torus), Point::new(0, 0));
109    }
110
111    #[test]
112    fn test_contains_simple() {
113        let torus = Torus::new(8, 8);
114        let aoi = Aoi::new(Point::new(1, 1), Point::new(3, 3));
115
116        assert!(aoi.contains(&torus, Point::new(2, 2)));
117        assert!(aoi.contains(&torus, Point::new(1, 1)));
118        assert!(aoi.contains(&torus, Point::new(3, 3)));
119        assert!(!aoi.contains(&torus, Point::new(0, 0)));
120        assert!(!aoi.contains(&torus, Point::new(4, 4)));
121    }
122
123    #[test]
124    fn test_contains_wraparound() {
125        let torus = Torus::new(8, 8);
126        let aoi = Aoi::new(Point::new(6, 6), Point::new(2, 2));
127
128        assert!(aoi.contains(&torus, Point::new(0, 0)));
129        assert!(aoi.contains(&torus, Point::new(7, 7)));
130        assert!(aoi.contains(&torus, Point::new(6, 6)));
131        assert!(aoi.contains(&torus, Point::new(2, 2)));
132        assert!(!aoi.contains(&torus, Point::new(4, 4)));
133    }
134
135    #[test]
136    fn test_dimensions() {
137        let torus = Torus::new(8, 8);
138
139        let aoi = Aoi::new(Point::new(1, 1), Point::new(3, 5));
140        assert_eq!(aoi.height(&torus), 3);
141        assert_eq!(aoi.width(&torus), 5);
142
143        let aoi_wrap = Aoi::new(Point::new(6, 6), Point::new(2, 2));
144        assert_eq!(aoi_wrap.height(&torus), 5);
145        assert_eq!(aoi_wrap.width(&torus), 5);
146    }
147}