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}