harmos_storage/signal/spectrum/
taper.rs1use std::f64::consts::PI;
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum Taper {
34 Rectangular,
35 Hamming,
36 Hann,
37 Blackman,
38}
39
40#[derive(Clone, Copy, Debug, PartialEq)]
41pub enum Band {
42 Rectangular { start: f64, end: f64 },
43 RaisedCosine { start: f64, end: f64, transition: f64 },
44 Kaiser { start: f64, end: f64, beta: f64 },
45 Gaussian { start: f64, end: f64, sigma: f64 },
46}
47
48impl Taper {
53 #[must_use]
54 pub fn coefficient(&self, index: usize, length: usize) -> f64 {
55 let length = length as f64;
56 let index = index as f64;
57
58 match self {
59 Self::Rectangular => 1.0,
60 Self::Hamming => 0.54 - 0.46 * (2.0 * PI * index / length).cos(),
61 Self::Hann => 0.5 * (1.0 - (2.0 * PI * index / length).cos()),
62 Self::Blackman => 0.42 - 0.5 * (2.0 * PI * index / length).cos() + 0.08 * (4.0 * PI * index / length).cos(),
63 }
64 }
65
66 #[must_use]
67 pub fn coefficients(&self, length: usize) -> Vec<f64> {
68 (0..length).map(|index| self.coefficient(index, length)).collect()
69 }
70}
71
72impl Band {
73 #[must_use]
74 pub fn coefficient(&self, frequency: f64) -> f64 {
75 let (start, end) = self.bounds();
76 if frequency < start || frequency > end {
77 return 0.0;
78 }
79
80 match self {
81 Self::Rectangular { .. } => 1.0,
82 Self::RaisedCosine { transition, .. } => {
83 if frequency < start + transition {
84 0.5 * (1.0 - (PI * (frequency - start) / transition).cos())
85 } else if frequency > end - transition {
86 0.5 * (1.0 - (PI * (end - frequency) / transition).cos())
87 } else {
88 1.0
89 }
90 }
91 Self::Kaiser { beta, .. } => {
92 let half = (end - start) / 2.0;
93 let offset = (frequency - (start + end) / 2.0) / half;
94 bessel_i0(beta * (1.0 - offset * offset).sqrt()) / bessel_i0(*beta)
95 }
96 Self::Gaussian { sigma, .. } => {
97 let offset = (frequency - (start + end) / 2.0) / sigma;
98 (-0.5 * offset * offset).exp()
99 }
100 }
101 }
102
103 #[must_use]
104 pub fn bounds(&self) -> (f64, f64) {
105 match self {
106 Self::Rectangular { start, end }
107 | Self::RaisedCosine { start, end, .. }
108 | Self::Kaiser { start, end, .. }
109 | Self::Gaussian { start, end, .. } => (*start, *end),
110 }
111 }
112}
113
114fn bessel_i0(argument: f64) -> f64 {
119 let magnitude = argument.abs();
120
121 if magnitude < 3.75 {
122 let square = (argument / 3.75).powi(2);
123 1.0 + square
124 * (3.515_622_9
125 + square * (3.089_942_4 + square * (1.206_749_2 + square * (0.265_973_2 + square * (0.036_076_8 + square * 0.004_581_3)))))
126 } else {
127 let ratio = 3.75 / magnitude;
128 (magnitude.exp() / magnitude.sqrt())
129 * (0.398_942_28
130 + ratio
131 * (0.013_285_92
132 + ratio
133 * (0.002_253_19
134 + ratio
135 * (-0.001_575_65
136 + ratio
137 * (0.009_162_81
138 + ratio
139 * (-0.020_577_06
140 + ratio * (0.026_355_37 + ratio * (-0.016_476_33 + ratio * 0.003_923_77))))))))
141 }
142}
143
144