Skip to main content

leodos_protocols/physical/
proximity1.rs

1//! Proximity-1 Physical Layer parameters (CCSDS 211.1-B-4).
2//!
3//! Defines the RF and modulation parameters for Proximity-1
4//! space links. These are short-range, bidirectional links used
5//! between landers, rovers, orbiters, and CubeSat formations.
6//!
7//! Modulation: GMSK with BT = 0.25.
8//! Band: UHF 390-450 MHz.
9//! Data rates: 8 kbps to 256 kbps (powers of 2).
10
11use crate::physical::modulator::gmsk::Gmsk;
12
13/// GMSK bandwidth-time product for Proximity-1.
14pub const BT: f32 = 0.25;
15
16/// Default samples per symbol for simulation.
17pub const DEFAULT_SPS: usize = 8;
18
19/// Forward link frequency band lower bound (Hz).
20pub const FORWARD_BAND_LOW: u32 = 435_000_000;
21
22/// Forward link frequency band upper bound (Hz).
23pub const FORWARD_BAND_HIGH: u32 = 450_000_000;
24
25/// Return link frequency band lower bound (Hz).
26pub const RETURN_BAND_LOW: u32 = 390_000_000;
27
28/// Return link frequency band upper bound (Hz).
29pub const RETURN_BAND_HIGH: u32 = 405_000_000;
30
31/// Channel spacing (Hz).
32pub const CHANNEL_SPACING: u32 = 20_000;
33
34/// Supported data rates (bits per second).
35pub const DATA_RATES: [u32; 6] = [8_000, 16_000, 32_000, 64_000, 128_000, 256_000];
36
37/// Data encoding options.
38#[derive(Debug, Copy, Clone, Eq, PartialEq)]
39pub enum DataEncoding {
40    /// Non-Return-to-Zero (natural binary).
41    Nrz,
42    /// Bi-Phase Level (Manchester).
43    BiPhaseL,
44}
45
46/// Default noise variance for simulation.
47pub const DEFAULT_NOISE_VAR: f32 = 0.1;
48
49/// Default LLR scale factor.
50pub const DEFAULT_SCALE: f32 = 1.0;
51
52/// Creates a GMSK modulator configured for Proximity-1.
53pub fn modulator() -> Gmsk {
54    Gmsk::new(BT, DEFAULT_SPS, DEFAULT_NOISE_VAR, DEFAULT_SCALE)
55}
56
57/// Creates a GMSK modulator with a custom samples-per-symbol.
58pub fn modulator_with_sps(sps: usize) -> Gmsk {
59    Gmsk::new(BT, sps, DEFAULT_NOISE_VAR, DEFAULT_SCALE)
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use crate::physical::modulator::gmsk;
66
67    #[test]
68    fn modulator_creates_valid_gmsk() {
69        let m = modulator();
70        let bits: [u8; 4] = [0xA5, 0x3C, 0xFF, 0x00];
71        let n_bits = 32;
72        let out_len = gmsk::output_len(n_bits, DEFAULT_SPS);
73        let mut out_i = [0.0f32; 512];
74        let mut out_q = [0.0f32; 512];
75        gmsk::modulate_gmsk(&bits, n_bits, BT, DEFAULT_SPS, &mut out_i[..out_len], &mut out_q[..out_len]);
76        assert!(out_i[..out_len].iter().any(|&s| s != 0.0));
77        let _ = m;
78    }
79
80    #[test]
81    fn data_rates_are_powers_of_two_kbps() {
82        for rate in DATA_RATES {
83            assert!((rate / 1000).is_power_of_two());
84        }
85    }
86
87    #[test]
88    fn frequency_bands_valid() {
89        assert!(FORWARD_BAND_LOW < FORWARD_BAND_HIGH);
90        assert!(RETURN_BAND_LOW < RETURN_BAND_HIGH);
91        assert!(RETURN_BAND_HIGH < FORWARD_BAND_LOW);
92    }
93}