1#[allow(unused_imports)]
8use crate::math::FloatMath;
9
10#[derive(Debug, Clone, Copy)]
17pub struct SogiPll {
18 sample_rate_hz: f32,
20 omega_center: f32, k_sogi: f32, v_alpha: f32, v_beta: f32, x1: f32, x2: f32, kp: f32, ki: f32, phase: f32, omega_est: f32, integrator_pi: f32,}
34
35impl SogiPll {
36 pub fn new(center_freq_hz: f32, sample_rate_hz: f32, k_sogi: f32, kp: f32, ki: f32) -> Self {
42 let omega_center = 2.0 * core::f32::consts::PI * center_freq_hz;
43 Self {
44 sample_rate_hz,
45 omega_center,
46 k_sogi,
47 v_alpha: 0.0,
48 v_beta: 0.0,
49 x1: 0.0,
50 x2: 0.0,
51 kp,
52 ki,
53 phase: 0.0,
54 omega_est: omega_center,
55 integrator_pi: 0.0,
56 }
57 }
58
59 pub fn process(&mut self, input: f32) -> f32 {
61 let ts = 1.0 / self.sample_rate_hz;
62 let half_ts = 0.5 * ts;
63
64 let err = input - self.v_alpha;
66 let k_err = self.k_sogi * err;
67 let w = self.omega_est;
68
69 let d_x1 = (k_err - self.v_beta) * w;
71 let d_x2 = self.v_alpha * w;
72
73 let x1_new = self.x1 + half_ts * d_x1;
74 let x2_new = self.x2 + half_ts * d_x2;
75
76 self.v_alpha = x1_new;
77 self.v_beta = x2_new;
78
79 self.x1 += ts * (k_err - self.v_beta) * w;
80 self.x2 += ts * self.v_alpha * w;
81
82 let sin_p = self.phase.sin();
84 let cos_p = self.phase.cos();
85 let v_q = -self.v_alpha * sin_p + self.v_beta * cos_p;
86
87 self.integrator_pi += self.ki * ts * v_q;
89 let delta_omega = self.kp * v_q + self.integrator_pi;
90 self.omega_est = self.omega_center + delta_omega;
91
92 self.phase += self.omega_est * ts;
94
95 let pi = core::f32::consts::PI;
97 let two_pi = 2.0 * pi;
98 while self.phase > pi {
99 self.phase -= two_pi;
100 }
101 while self.phase < -pi {
102 self.phase += two_pi;
103 }
104
105 self.phase
106 }
107
108 #[inline(always)]
110 pub fn frequency_hz(&self) -> f32 {
111 self.omega_est / (2.0 * core::f32::consts::PI)
112 }
113
114 #[inline(always)]
116 pub fn orthogonal_components(&self) -> (f32, f32) {
117 (self.v_alpha, self.v_beta)
118 }
119
120 #[inline(always)]
122 pub fn phase(&self) -> f32 {
123 self.phase
124 }
125
126 pub fn reset(&mut self) {
128 self.v_alpha = 0.0;
129 self.v_beta = 0.0;
130 self.x1 = 0.0;
131 self.x2 = 0.0;
132 self.phase = 0.0;
133 self.omega_est = self.omega_center;
134 self.integrator_pi = 0.0;
135 }
136}
137
138#[derive(Debug, Clone, Copy)]
140pub struct CostasLoop {
141 sample_rate_hz: f32,
142 phase: f32,
143 freq_rad_per_sample: f32,
144 center_freq_rad: f32,
145 alpha: f32, beta: f32, }
148
149impl CostasLoop {
150 pub fn new(center_freq_hz: f32, sample_rate_hz: f32, loop_bandwidth_hz: f32, damping: f32) -> Self {
152 let center_freq_rad = 2.0 * core::f32::consts::PI * center_freq_hz / sample_rate_hz;
153 let theta = 2.0 * core::f32::consts::PI * loop_bandwidth_hz / sample_rate_hz;
154 let d = 1.0 + 2.0 * damping * theta + theta * theta;
155 let alpha = (4.0 * damping * theta) / d;
156 let beta = (4.0 * theta * theta) / d;
157
158 Self {
159 sample_rate_hz,
160 phase: 0.0,
161 freq_rad_per_sample: center_freq_rad,
162 center_freq_rad,
163 alpha,
164 beta,
165 }
166 }
167
168 pub fn process_sample(&mut self, sample: f32) -> (f32, f32) {
170 let cos_val = self.phase.cos();
171 let sin_val = (-self.phase).sin();
172
173 let i_arm = sample * cos_val;
174 let q_arm = sample * sin_val;
175
176 let error = (i_arm * q_arm).clamp(-1.0, 1.0);
178
179 self.freq_rad_per_sample += self.beta * error;
181 self.phase += self.freq_rad_per_sample + self.alpha * error;
182
183 let pi = core::f32::consts::PI;
185 while self.phase > pi {
186 self.phase -= 2.0 * pi;
187 }
188 while self.phase < -pi {
189 self.phase += 2.0 * pi;
190 }
191
192 (i_arm, q_arm)
193 }
194
195 #[inline(always)]
197 pub fn frequency_hz(&self) -> f32 {
198 self.freq_rad_per_sample * self.sample_rate_hz / (2.0 * core::f32::consts::PI)
199 }
200
201 #[inline(always)]
203 pub fn center_frequency_hz(&self) -> f32 {
204 self.center_freq_rad * self.sample_rate_hz / (2.0 * core::f32::consts::PI)
205 }
206}