ac_power/pq/
pq.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::newtypes::Power;
17use crate::trig::Cos;
18use idsp;
19
20// function to normalize p and q, which are floats, to fixed-point i32 while preverving ratio
21fn normalize(x: f32, y: f32) -> (i32, i32) {
22    let norm = 2147483648. * f32::max(x, y).recip();
23    let xn = (norm * x) as i32;
24    let yn = (norm * y) as i32;
25    (xn, yn)
26}
27
28/// Instantaneous real (p) and reactive (q) powers
29#[derive(Debug, Copy, Clone, PartialEq)]
30pub struct Pq {
31    pub p: Power,
32    pub q: Power,
33}
34
35impl Pq {
36    /// Calculates power factor of a Pq value
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use ac_power::Pq;
42    /// use approx::assert_abs_diff_eq;
43    ///
44    /// let pq = Pq {
45    ///     p: 1.0.into(),
46    ///     q: 1.0.into(),
47    /// };
48    ///
49    /// let pf = pq.power_factor();
50    /// assert_abs_diff_eq!(f32::from(pf), 0.707, epsilon = 0.0001);
51    /// ```
52    pub fn power_factor(&self) -> Cos {
53        // convert p and q into fixed-point format for efficient trig
54        let (x, y) = normalize(self.p.into(), self.q.into());
55
56        // calculate the fixed-point power factor (PF = cos(arctan(Q/P)))
57        let (pf, _) = idsp::cossin(idsp::atan2(y, x));
58
59        pf.into()
60    }
61}
62
63#[cfg(test)]
64mod tests {
65
66    use super::*;
67    use approx::assert_abs_diff_eq;
68
69    #[test]
70    fn power_factor() {
71        let pq = Pq {
72            p: 1.0.into(),
73            q: 0.0.into(),
74        };
75        let pf = pq.power_factor();
76        assert_abs_diff_eq!(f32::from(pf), 1.0, epsilon = 0.0001);
77
78        let pq = Pq {
79            p: 0.0.into(),
80            q: 1.0.into(),
81        };
82        let pf = pq.power_factor();
83        assert_abs_diff_eq!(f32::from(pf), 0.0, epsilon = 0.0001);
84
85        let pq = Pq {
86            p: 1.0.into(),
87            q: 1.0.into(),
88        };
89        let pf = pq.power_factor();
90        assert_abs_diff_eq!(f32::from(pf), 0.707, epsilon = 0.0001);
91    }
92}