ac_power/reference_frames/
transforms.rs

1// Copyright 2023 Enphase Energy, Inc and Universal Interoperability for
2// Grid-Forming Inverters (UNIFI) Consortium.
3//
4//    Licensed under the Apache License, Version 2.0 (the "License");
5//    you may not use this file except in compliance with the License.
6//    You may obtain a copy of the License at
7//
8//        http://www.apache.org/licenses/LICENSE-2.0
9//
10//    Unless required by applicable law or agreed to in writing, software
11//    distributed under the License is distributed on an "AS IS" BASIS,
12//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13//    See the License for the specific language governing permissions and
14//    limitations under the License.
15
16use crate::constants::{ONE_HALF, ONE_THIRD, SQRT_3_OVER_2, SQRT_3_OVER_3, TWO_THIRDS};
17use crate::number::Num;
18use crate::reference_frames::{Abc, AlphaBeta, AlphaBeta0, Dq, Dq0, Polar};
19use crate::trig::{shift_left_120, shift_right_120, Cos, Sin};
20
21impl<T: Num> From<Polar<T>> for Abc<T> {
22    fn from(polar: Polar<T>) -> Self {
23        Self::from_polar(polar.amplitude, polar.theta)
24    }
25}
26
27impl<T: Num> From<Abc<T>> for AlphaBeta<T> {
28    fn from(abc: Abc<T>) -> Self {
29        let alpha = (abc.a * TWO_THIRDS) - (abc.b * ONE_THIRD) - (abc.c * ONE_THIRD);
30        let beta = (abc.b * SQRT_3_OVER_3) - (abc.c * SQRT_3_OVER_3);
31
32        Self { alpha, beta }
33    }
34}
35
36impl<T: Num> From<AlphaBeta<T>> for Abc<T> {
37    fn from(alpha_beta: AlphaBeta<T>) -> Self {
38        let a = alpha_beta.alpha;
39        let b = -(alpha_beta.alpha * ONE_HALF) + alpha_beta.beta * SQRT_3_OVER_2;
40        let c = -alpha_beta.alpha * ONE_HALF - alpha_beta.beta * SQRT_3_OVER_2;
41        Self { a, b, c }
42    }
43}
44
45impl<T: Num> From<Abc<T>> for AlphaBeta0<T> {
46    fn from(abc: Abc<T>) -> Self {
47        let alpha = (abc.a * TWO_THIRDS) - (abc.b * ONE_THIRD) - (abc.c * ONE_THIRD);
48        let beta = (abc.b * SQRT_3_OVER_3) - (abc.c * SQRT_3_OVER_3);
49        let zero = (abc.a + abc.b + abc.c) * ONE_THIRD;
50
51        Self { alpha, beta, zero }
52    }
53}
54
55impl<T: Num> From<AlphaBeta<T>> for AlphaBeta0<T> {
56    fn from(alpha_beta: AlphaBeta<T>) -> Self {
57        Self {
58            alpha: alpha_beta.alpha,
59            beta: alpha_beta.beta,
60            zero: 0.0.into(),
61        }
62    }
63}
64
65impl<T> From<AlphaBeta0<T>> for AlphaBeta<T> {
66    fn from(alpha_beta_0: AlphaBeta0<T>) -> Self {
67        Self {
68            alpha: alpha_beta_0.alpha,
69            beta: alpha_beta_0.beta,
70        }
71    }
72}
73
74impl<T: Num> From<AlphaBeta0<T>> for Abc<T> {
75    fn from(alpha_beta_0: AlphaBeta0<T>) -> Self {
76        let a = alpha_beta_0.alpha + alpha_beta_0.zero;
77        let b =
78            -alpha_beta_0.alpha * ONE_HALF + alpha_beta_0.beta * SQRT_3_OVER_2 + alpha_beta_0.zero;
79        let c =
80            -alpha_beta_0.alpha * ONE_HALF - alpha_beta_0.beta * SQRT_3_OVER_2 + alpha_beta_0.zero;
81        Self { a, b, c }
82    }
83}
84
85impl<T: Num> AlphaBeta<T> {
86    pub fn to_dq0(&self, cos: Cos, sin: Sin) -> Dq0<T> {
87        let d = (self.alpha * sin) - (self.beta * cos);
88        let q = (self.alpha * cos) + (self.beta * sin);
89
90        Dq0 {
91            d,
92            q,
93            zero: 0.0.into(),
94        }
95    }
96
97    pub fn to_dq(&self, cos: Cos, sin: Sin) -> Dq<T> {
98        let d = (self.alpha * sin) - (self.beta * cos);
99        let q = (self.alpha * cos) + (self.beta * sin);
100
101        Dq { d, q }
102    }
103}
104
105impl<T: Num> AlphaBeta0<T> {
106    pub fn to_dq0(&self, cos: Cos, sin: Sin) -> Dq0<T> {
107        let d = (self.alpha * sin) - (self.beta * cos);
108        let q = (self.alpha * cos) + (self.beta * sin);
109
110        Dq0 {
111            d,
112            q,
113            zero: self.zero,
114        }
115    }
116
117    pub fn to_dq(&self, cos: Cos, sin: Sin) -> Dq<T> {
118        let d = (self.alpha * sin) - (self.beta * cos);
119        let q = (self.alpha * cos) + (self.beta * sin);
120
121        Dq { d, q }
122    }
123}
124
125impl<T: Num> Abc<T> {
126    pub fn to_dq(&self, cos: Cos, sin: Sin) -> Dq<T> {
127        /* sin and cos with 120 degree offsets */
128        let (cos_m, sin_m) = shift_left_120(cos, sin);
129        let (cos_p, sin_p) = shift_right_120(cos, sin);
130
131        let d = ((self.a * sin) + (self.b * sin_m) + (self.c * sin_p)) * TWO_THIRDS;
132        let q = ((self.a * cos) + (self.b * cos_m) + (self.c * cos_p)) * TWO_THIRDS;
133
134        Dq { d, q }
135    }
136
137    pub fn to_dq0(&self, cos: Cos, sin: Sin) -> Dq0<T> {
138        /* sin and cos with 120 degree offsets */
139        let (cos_m, sin_m) = shift_left_120(cos, sin);
140        let (cos_p, sin_p) = shift_right_120(cos, sin);
141
142        let d = ((self.a * sin) + (self.b * sin_m) + (self.c * sin_p)) * TWO_THIRDS;
143        let q = ((self.a * cos) + (self.b * cos_m) + (self.c * cos_p)) * TWO_THIRDS;
144        let zero = (self.a + self.b + self.c) * ONE_THIRD;
145        Dq0 { d, q, zero }
146    }
147}
148
149impl<T: Num> From<Abc<T>> for f32
150where
151    f32: From<T>,
152{
153    fn from(abc: Abc<T>) -> Self {
154        ((abc.a + abc.b + abc.c) * ONE_THIRD).into()
155    }
156}
157
158impl<T: Num> From<Dq<T>> for Dq0<T> {
159    fn from(dq: Dq<T>) -> Self {
160        Self {
161            d: dq.d,
162            q: dq.q,
163            zero: 0.0.into(),
164        }
165    }
166}
167
168impl<T> From<Dq0<T>> for Dq<T> {
169    fn from(dq0: Dq0<T>) -> Self {
170        Self { d: dq0.d, q: dq0.q }
171    }
172}
173
174impl<T: Num> Dq<T> {
175    pub fn to_abc(&self, cos: Cos, sin: Sin) -> Abc<T> {
176        /* sin and cos with 120 degree offsets */
177        let (cos_m, sin_m) = shift_left_120(cos, sin);
178        let (cos_p, sin_p) = shift_right_120(cos, sin);
179
180        let a = (self.d * sin) + (self.q * cos);
181        let b = (self.d * sin_m) + (self.q * cos_m);
182        let c = (self.d * sin_p) + (self.q * cos_p);
183
184        Abc { a, b, c }
185    }
186
187    pub fn to_alpha_beta(&self, cos: Cos, sin: Sin) -> AlphaBeta<T> {
188        let alpha = (self.d * sin) + (self.q * cos);
189        let beta = (self.q * sin) - (self.d * cos);
190
191        AlphaBeta { alpha, beta }
192    }
193
194    pub fn to_alpha_beta_0(&self, cos: Cos, sin: Sin) -> AlphaBeta0<T> {
195        let alpha = (self.d * sin) + (self.q * cos);
196        let beta = (self.q * sin) - (self.d * cos);
197
198        AlphaBeta0 {
199            alpha,
200            beta,
201            zero: 0.0.into(),
202        }
203    }
204}
205
206impl<T: Num> Dq0<T> {
207    pub fn to_abc(&self, cos: Cos, sin: Sin) -> Abc<T> {
208        Dq {
209            d: self.d,
210            q: self.q,
211        }
212        .to_abc(cos, sin)
213            + self.zero
214    }
215
216    pub fn to_alpha_beta(&self, cos: Cos, sin: Sin) -> AlphaBeta<T> {
217        let alpha = (self.d * sin) + (self.q * cos);
218        let beta = (self.q * sin) - (self.d * cos);
219
220        AlphaBeta { alpha, beta }
221    }
222
223    pub fn to_alpha_beta_0(&self, cos: Cos, sin: Sin) -> AlphaBeta0<T> {
224        let alpha = (self.d * sin) + (self.q * cos);
225        let beta = (self.q * sin) - (self.d * cos);
226
227        AlphaBeta0 {
228            alpha,
229            beta,
230            zero: self.zero,
231        }
232    }
233}
234
235#[cfg(test)]
236mod tests {
237
238    use super::*;
239    use crate::trig::cos_sin;
240    use crate::trig::Theta;
241    use approx::assert_abs_diff_eq;
242
243    #[test]
244    fn abc_to_alpha_beta_0() {
245        let abc = Abc {
246            a: 1.0,
247            b: 2.0,
248            c: 3.0,
249        };
250        let alpha_beta_0 = AlphaBeta0::from(abc);
251
252        // verified against results from https://pypi.org/project/ClarkePark/
253        assert_abs_diff_eq!(alpha_beta_0.alpha, -1.0, epsilon = 0.0001);
254        assert_abs_diff_eq!(alpha_beta_0.beta, -0.577350, epsilon = 0.0001);
255        assert_abs_diff_eq!(alpha_beta_0.zero, 2.0, epsilon = 0.0001);
256    }
257
258    #[test]
259    fn abc_to_dq0() {
260        let abc = Abc {
261            a: 1.0,
262            b: 2.0,
263            c: 3.0,
264        };
265
266        let (cos, sin) = cos_sin(Theta::from_degrees(90.0));
267        let dq0 = abc.to_dq0(cos, sin);
268
269        // verified against results from https://pypi.org/project/ClarkePark/
270        assert_abs_diff_eq!(dq0.d, -1.0, epsilon = 0.0001);
271        assert_abs_diff_eq!(dq0.q, -0.577350, epsilon = 0.0001);
272        assert_abs_diff_eq!(dq0.zero, 2.0, epsilon = 0.0001);
273    }
274
275    #[test]
276    fn alpha_beta_0_to_dq0() {
277        let alpha_beta_0 = AlphaBeta0 {
278            alpha: 1.0,
279            beta: 2.0,
280            zero: 3.0,
281        };
282        let (cos, sin) = cos_sin(Theta::from_degrees(45.0));
283        let dq0 = alpha_beta_0.to_dq0(cos, sin);
284
285        // verified against results from https://pypi.org/project/ClarkePark/
286        assert_abs_diff_eq!(dq0.d, -0.7071067, epsilon = 0.0001);
287        assert_abs_diff_eq!(dq0.q, 2.1213203435, epsilon = 0.0001);
288        assert_abs_diff_eq!(dq0.zero, 3.0, epsilon = 0.0001);
289    }
290
291    #[test]
292    fn dq0_to_abc() {
293        let dq0: Dq0<f32> = Dq0 {
294            d: 1.0,
295            q: 2.0,
296            zero: 3.0,
297        };
298        let (cos, sin) = cos_sin(Theta::from_degrees(45.0));
299        let abc = dq0.to_abc(cos, sin);
300
301        // verified against results from https://pypi.org/project/ClarkePark/
302        assert_abs_diff_eq!(abc.a, 5.12132034, epsilon = 0.0001);
303        assert_abs_diff_eq!(abc.b, 2.551712263, epsilon = 0.0001);
304        assert_abs_diff_eq!(abc.c, 1.326967392, epsilon = 0.0001);
305    }
306
307    #[test]
308    fn dq0_to_alpha_beta_0() {
309        let dq0 = Dq0 {
310            d: -0.7071067,
311            q: 2.1213203435,
312            zero: 3.0,
313        };
314        let (cos, sin) = cos_sin(Theta::from_degrees(45.0));
315        let alpha_beta_0 = dq0.to_alpha_beta_0(cos, sin);
316
317        // verified against results from https://pypi.org/project/ClarkePark/
318        assert_abs_diff_eq!(alpha_beta_0.alpha, 1.0, epsilon = 0.0001);
319        assert_abs_diff_eq!(alpha_beta_0.beta, 2.0, epsilon = 0.0001);
320        assert_abs_diff_eq!(alpha_beta_0.zero, 3.0, epsilon = 0.0001);
321    }
322
323    #[test]
324    fn alpha_beta_to_abc() {
325        let alpha_beta = AlphaBeta {
326            alpha: 1.0,
327            beta: 2.0,
328        };
329        let abc = Abc::from(alpha_beta);
330
331        // verified against results from https://pypi.org/project/ClarkePark/
332        assert_abs_diff_eq!(abc.a, 1.0, epsilon = 0.0001);
333        assert_abs_diff_eq!(abc.b, 1.2320508, epsilon = 0.0001);
334        assert_abs_diff_eq!(abc.c, -2.232050, epsilon = 0.0001);
335    }
336
337    #[test]
338    fn alpha_beta_to_alpha_beta_0() {
339        let alpha_beta = AlphaBeta {
340            alpha: 1.0,
341            beta: 2.0,
342        };
343        let alpha_beta0 = AlphaBeta0::<f32>::from(alpha_beta);
344
345        assert_abs_diff_eq!(alpha_beta0.alpha, alpha_beta.alpha, epsilon = 0.0001);
346        assert_abs_diff_eq!(alpha_beta0.beta, alpha_beta.beta, epsilon = 0.0001);
347        assert_abs_diff_eq!(alpha_beta0.zero, 0.0, epsilon = 0.0001);
348    }
349
350    #[test]
351    fn alpha_beta_to_dq0() {
352        let alpha_beta = AlphaBeta {
353            alpha: 1.0,
354            beta: 2.0,
355        };
356        let (cos, sin) = cos_sin(Theta::from_degrees(45.0));
357        let dq0 = alpha_beta.to_dq0(cos, sin);
358
359        // verified against results from https://pypi.org/project/ClarkePark/
360        assert_abs_diff_eq!(dq0.d, -0.7071067, epsilon = 0.0001);
361        assert_abs_diff_eq!(dq0.q, 2.1213203435, epsilon = 0.0001);
362        assert_abs_diff_eq!(dq0.zero, 0.0, epsilon = 0.0001);
363    }
364
365    #[test]
366    fn dq_to_abc() {
367        let dq = Dq { d: 1.0, q: 2.0 };
368        let (cos, sin) = cos_sin(Theta::from_degrees(45.0));
369        let abc = dq.to_abc(cos, sin);
370
371        // verified against results from https://pypi.org/project/ClarkePark/
372        assert_abs_diff_eq!(abc.a, 2.1213203435, epsilon = 0.0001);
373        assert_abs_diff_eq!(abc.b, -0.448287736, epsilon = 0.0001);
374        assert_abs_diff_eq!(abc.c, -1.673032607, epsilon = 0.0001);
375    }
376
377    #[test]
378    fn dq_to_alpha_beta_0() {
379        let dq = Dq {
380            d: -0.7071067,
381            q: 2.1213203435,
382        };
383        let (cos, sin) = cos_sin(Theta::from_degrees(45.0));
384        let alpha_beta_0 = dq.to_alpha_beta_0(cos, sin);
385
386        // verified against results from https://pypi.org/project/ClarkePark/
387        assert_abs_diff_eq!(alpha_beta_0.alpha, 1.0, epsilon = 0.0001);
388        assert_abs_diff_eq!(alpha_beta_0.beta, 2.0, epsilon = 0.0001);
389        assert_abs_diff_eq!(alpha_beta_0.zero, 0.0, epsilon = 0.0001);
390    }
391
392    #[test]
393    fn dq_to_dq0() {
394        let dq = Dq { d: 1.0, q: 2.0 };
395        let dq0 = Dq0::from(dq);
396        assert_abs_diff_eq!(dq0.d, dq.d, epsilon = 0.0001);
397        assert_abs_diff_eq!(dq0.q, dq.q, epsilon = 0.0001);
398        assert_abs_diff_eq!(dq0.zero, 0.0, epsilon = 0.0001);
399    }
400
401    #[test]
402    fn abc_to_alpha_beta() {
403        let abc = Abc {
404            a: 1.0,
405            b: 2.0,
406            c: 3.0,
407        };
408        let alpha_beta = AlphaBeta::from(abc);
409
410        // verified against results from https://pypi.org/project/ClarkePark/
411        assert_abs_diff_eq!(alpha_beta.alpha, -1.0, epsilon = 0.0001);
412        assert_abs_diff_eq!(alpha_beta.beta, -0.577350, epsilon = 0.0001);
413    }
414
415    #[test]
416    fn abc_to_dq() {
417        let abc = Abc {
418            a: 1.0,
419            b: 2.0,
420            c: 3.0,
421        };
422
423        let (cos, sin) = cos_sin(Theta::from_degrees(90.0));
424        let dq = abc.to_dq(cos, sin);
425
426        // verified against results from https://pypi.org/project/ClarkePark/
427        assert_abs_diff_eq!(dq.d, -1.0, epsilon = 0.0001);
428        assert_abs_diff_eq!(dq.q, -0.577350, epsilon = 0.0001);
429    }
430
431    #[test]
432    fn alpha_beta_0_to_alpha_beta() {
433        let alpha_beta_0 = AlphaBeta0 {
434            alpha: 1.0,
435            beta: 2.0,
436            zero: 3.0,
437        };
438        let alpha_beta = AlphaBeta::from(alpha_beta_0);
439
440        assert_abs_diff_eq!(alpha_beta.alpha, alpha_beta_0.alpha, epsilon = 0.0001);
441        assert_abs_diff_eq!(alpha_beta.beta, alpha_beta_0.beta, epsilon = 0.0001);
442    }
443
444    #[test]
445    fn alpha_beta_0_to_dq() {
446        let alpha_beta_0 = AlphaBeta0 {
447            alpha: 1.0,
448            beta: 2.0,
449            zero: 3.0,
450        };
451        let (cos, sin) = cos_sin(Theta::from_degrees(45.0));
452        let dq = alpha_beta_0.to_dq(cos, sin);
453
454        // verified against results from https://pypi.org/project/ClarkePark/
455        assert_abs_diff_eq!(dq.d, -0.7071067, epsilon = 0.0001);
456        assert_abs_diff_eq!(dq.q, 2.1213203435, epsilon = 0.0001);
457    }
458
459    #[test]
460    fn dq0_to_alpha_beta() {
461        let dq0 = Dq0 {
462            d: -0.7071067,
463            q: 2.1213203435,
464            zero: 3.0,
465        };
466        let (cos, sin) = cos_sin(Theta::from_degrees(45.0));
467        let alpha_beta = dq0.to_alpha_beta(cos, sin);
468
469        // verified against results from https://pypi.org/project/ClarkePark/
470        assert_abs_diff_eq!(alpha_beta.alpha, 1.0, epsilon = 0.0001);
471        assert_abs_diff_eq!(alpha_beta.beta, 2.0, epsilon = 0.0001);
472    }
473
474    #[test]
475    fn dq0_to_dq() {
476        let dq0 = Dq0 {
477            d: 1.0,
478            q: 2.0,
479            zero: 3.0,
480        };
481        let dq = Dq::from(dq0);
482        assert_abs_diff_eq!(dq.d, dq0.d, epsilon = 0.0001);
483        assert_abs_diff_eq!(dq.q, dq0.q, epsilon = 0.0001);
484    }
485}