leodos_protocols/physical/modulator/
bpsk.rs1use super::clamp_i16;
8
9pub fn modulate(
14 bits: &[u8],
15 n_bits: usize,
16 symbols: &mut [f32],
17) {
18 assert!(bits.len() * 8 >= n_bits);
19 assert!(symbols.len() >= n_bits);
20
21 for i in 0..n_bits {
22 let byte = bits[i / 8];
23 let bit = (byte >> (7 - (i % 8))) & 1;
24 symbols[i] = 1.0 - 2.0 * bit as f32;
25 }
26}
27
28pub fn demodulate(
37 symbols: &[f32],
38 n_bits: usize,
39 noise_var: f32,
40 scale: f32,
41 llr: &mut [i16],
42) {
43 assert!(symbols.len() >= n_bits);
44 assert!(llr.len() >= n_bits);
45
46 let factor = scale * 2.0 / noise_var;
47 for i in 0..n_bits {
48 let v = symbols[i] * factor;
49 llr[i] = clamp_i16(v);
50 }
51}
52
53pub struct Bpsk {
55 noise_var: f32,
56 scale: f32,
57}
58
59impl Bpsk {
60 pub fn new(noise_var: f32, scale: f32) -> Self {
63 Self { noise_var, scale }
64 }
65}
66
67impl super::Modulator for Bpsk {
68 fn modulate(
69 &self,
70 bits: &[u8],
71 n_bits: usize,
72 symbols: &mut [f32],
73 ) -> usize {
74 modulate(bits, n_bits, symbols);
75 n_bits
76 }
77}
78
79impl super::Demodulator for Bpsk {
80 fn demodulate_soft(
81 &self,
82 symbols: &[f32],
83 n_bits: usize,
84 llr: &mut [i16],
85 ) {
86 demodulate(symbols, n_bits, self.noise_var, self.scale, llr);
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn modulate_basic() {
96 let bits = [0xA5u8];
98 let mut sym = [0f32; 8];
99 modulate(&bits, 8, &mut sym);
100 let expected = [-1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0];
101 assert_eq!(sym, expected);
102 }
103
104 #[test]
105 fn roundtrip_hard() {
106 let bits = [0xC3, 0x5A]; let mut sym = [0f32; 16];
108 modulate(&bits, 16, &mut sym);
109
110 let mut llr = [0i16; 16];
112 demodulate(&sym, 16, 0.5, 100.0, &mut llr);
113
114 let mut recovered = [0u8; 2];
116 for i in 0..16 {
117 if llr[i] < 0 {
118 recovered[i / 8] |= 1 << (7 - (i % 8));
119 }
120 }
121 assert_eq!(recovered, bits);
122 }
123
124 #[test]
125 fn partial_byte() {
126 let bits = [0xE0u8];
128 let mut sym = [0f32; 3];
129 modulate(&bits, 3, &mut sym);
130 assert_eq!(sym, [-1.0, -1.0, -1.0]);
131 }
132
133 #[test]
134 fn with_awgn() {
135 let bits = [0xA5u8]; let mut sym = [0f32; 8];
138 modulate(&bits, 8, &mut sym);
139
140 for (i, s) in sym.iter_mut().enumerate() {
142 *s += 0.1 * (i as f32 - 4.0) / 4.0;
143 }
144
145 let mut llr = [0i16; 8];
146 demodulate(&sym, 8, 0.5, 100.0, &mut llr);
147
148 let mut recovered = [0u8; 1];
150 for i in 0..8 {
151 if llr[i] < 0 {
152 recovered[0] |= 1 << (7 - i);
153 }
154 }
155 assert_eq!(recovered[0], 0xA5);
156 }
157}