1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
 * File: duty_ratio.rs
 * Project: common
 * Created Date: 14/10/2023
 * Author: Shun Suzuki
 * -----
 * Last Modified: 14/10/2023
 * Modified By: Shun Suzuki (suzuki@hapis.k.u-tokyo.ac.jp)
 * -----
 * Copyright (c) 2023 Shun Suzuki. All rights reserved.
 *
 */

use crate::{
    defined::{float, PI},
    derive::prelude::AUTDInternalError,
};

use super::Amplitude;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DutyRatio {
    value: float,
}

impl DutyRatio {
    pub const MAX: DutyRatio = DutyRatio { value: 0.5 };
    pub const MIN: DutyRatio = DutyRatio { value: 0.0 };

    pub fn new(value: float) -> Result<Self, AUTDInternalError> {
        if !(0.0..=0.5).contains(&value) {
            Err(AUTDInternalError::DutyRatioOutOfRange(value))
        } else {
            Ok(Self { value })
        }
    }

    pub fn new_clamped(value: float) -> Self {
        Self {
            value: value.clamp(0.0, 0.5),
        }
    }

    pub const fn value(&self) -> float {
        self.value
    }
}

impl From<Amplitude> for DutyRatio {
    fn from(f: Amplitude) -> Self {
        Self {
            value: f.value().asin() / PI,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn duty_ratio() {
        let d = DutyRatio::new(0.0);
        assert!(d.is_ok());
        assert_eq!(d.unwrap().value(), 0.0);

        let d = DutyRatio::new(0.5);
        assert!(d.is_ok());
        assert_eq!(d.unwrap().value(), 0.5);

        let d = DutyRatio::new(0.6);
        assert!(d.is_err());

        let d = DutyRatio::new(-0.1);
        assert!(d.is_err());
    }

    #[test]
    fn duty_ratio_clamped() {
        let d = DutyRatio::new_clamped(0.0);
        assert_eq!(d.value(), 0.0);

        let d = DutyRatio::new_clamped(0.5);
        assert_eq!(d.value(), 0.5);

        let d = DutyRatio::new_clamped(0.6);
        assert_eq!(d.value(), 0.5);

        let d = DutyRatio::new_clamped(-0.1);
        assert_eq!(d.value(), 0.0);
    }

    #[test]
    fn duty_ratio_from_amplitude() {
        let duty = DutyRatio::from(Amplitude::MIN);
        assert_eq!(duty, DutyRatio::MIN);

        let duty = DutyRatio::from(Amplitude::MAX);
        assert_eq!(duty, DutyRatio::MAX);

        let amp = Amplitude::new_clamped(0.3090169943749474);
        let duty = DutyRatio::from(amp);
        assert_approx_eq::assert_approx_eq!(duty.value(), 0.1);
    }

    #[test]
    fn duty_ratio_clone() {
        let duty = DutyRatio::new_clamped(0.2);
        let duty2 = Clone::clone(&duty);
        assert_eq!(duty, duty2);
    }

    #[test]
    fn duty_ratio_debug() {
        let duty = DutyRatio::new_clamped(0.2);
        assert_eq!(format!("{:?}", duty), "DutyRatio { value: 0.2 }");
    }
}