ac_power/trig/newtypes/
sin.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::trig::cos_sin;
17use crate::trig::Cos;
18use crate::trig::Theta;
19use core::convert::From;
20use core::ops::{Mul, Neg};
21use derive_more::{From, Into};
22
23/// Sin of an angle
24#[derive(Debug, Copy, Clone, PartialEq, From, Into)]
25pub struct Sin(f32);
26
27impl Sin {
28    /// Calculates sin from theta
29    ///
30    /// # Examples
31    ///
32    /// ```
33    /// use ac_power::trig::{Theta, Sin};
34    ///
35    /// let theta = Theta::from_degrees(180.0);
36    /// let sin = Sin::from_theta(theta);
37    /// ```
38    pub fn from_theta(theta: Theta) -> Self {
39        let (_, sin) = cos_sin(theta);
40        sin
41    }
42
43    /// Calculates sin from degrees
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use ac_power::trig::Sin;
49    ///
50    /// let sin = Sin::from_degrees(45.0);
51    /// ```
52    pub fn from_degrees(degrees: f32) -> Self {
53        let theta = Theta::from_degrees(degrees);
54        Self::from_theta(theta)
55    }
56
57    /// Calculates sin from radians
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// use ac_power::trig::Sin;
63    ///
64    /// let sin = Sin::from_radians(core::f32::consts::PI/4.0);
65    /// ```
66    pub fn from_radians(radians: f32) -> Self {
67        let theta = Theta::from_radians(radians);
68        Self::from_theta(theta)
69    }
70}
71
72impl Neg for Sin {
73    fn neg(self) -> Self {
74        Self(-self.0)
75    }
76    type Output = Self;
77}
78
79impl From<i32> for Sin {
80    fn from(item: i32) -> Self {
81        Self((item as f32) / 2147483648.)
82    }
83}
84
85impl Mul<Sin> for Sin {
86    fn mul(self, other: Sin) -> f32 {
87        self.0 * other.0
88    }
89    type Output = f32;
90}
91
92impl Mul<Cos> for Sin {
93    fn mul(self, rhs: Cos) -> f32 {
94        self.0 * rhs
95    }
96    type Output = f32;
97}
98
99#[cfg(test)]
100mod tests {
101
102    use super::*;
103    use approx::assert_abs_diff_eq;
104
105    use std::f32::consts::PI;
106
107    fn linspace(x0: f32, x1: f32, length: usize) -> Vec<f32> {
108        let dx = (x1 - x0) / ((length - 1) as f32);
109        let mut xs: Vec<f32> = vec![x0];
110        for index in 1..length {
111            xs.push(xs[index - 1] + dx);
112        }
113        xs
114    }
115
116    fn degrees_to_radians(degrees: f32) -> f32 {
117        2.0 * PI * (degrees / 360.0)
118    }
119
120    fn check_from_degrees(degrees: f32) {
121        let sin = Sin::from_degrees(degrees);
122        let radians = degrees_to_radians(degrees);
123        assert_abs_diff_eq!(f32::from(sin), radians.sin(), epsilon = 0.0001);
124    }
125
126    fn check_from_radians(radians: f32) {
127        let sin = Sin::from_radians(radians);
128        assert_abs_diff_eq!(f32::from(sin), radians.sin(), epsilon = 0.0001);
129    }
130
131    #[test]
132    fn constructors() {
133        let angles = linspace(-720.0, 720.0, 500);
134        for angle in angles.iter() {
135            check_from_degrees(*angle);
136        }
137
138        let angles = linspace(-4.0 * PI, 4.0 * PI, 100);
139        for angle in angles.iter() {
140            check_from_radians(*angle);
141        }
142    }
143
144    #[test]
145    fn conversions() {
146        let x = 0.707;
147        let sin = Sin::from(x);
148        assert_abs_diff_eq!(x, f32::from(sin), epsilon = 0.0001);
149    }
150
151    #[test]
152    fn arithmetic() {
153        let radians = 0.26;
154        let sin = Sin::from_radians(radians);
155        let x = 1.0;
156        assert_abs_diff_eq!(x * sin, x * radians.sin(), epsilon = 0.0001);
157        assert_abs_diff_eq!(sin * x, x * radians.sin(), epsilon = 0.0001);
158        assert_abs_diff_eq!(sin * sin, radians.sin() * radians.sin(), epsilon = 0.0001);
159    }
160}