summaryrefslogtreecommitdiff
path: root/src/sinusoid.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sinusoid.rs')
-rw-r--r--src/sinusoid.rs100
1 files changed, 89 insertions, 11 deletions
diff --git a/src/sinusoid.rs b/src/sinusoid.rs
index fe8b3ad..4bbfcc3 100644
--- a/src/sinusoid.rs
+++ b/src/sinusoid.rs
@@ -1,5 +1,6 @@
use std::cmp::{PartialOrd};
-use ::num_traits::{Trig, ArithmeticOps, FractionOps};
+use ::num_traits::{Trig, Pow, ArithmeticOps, FractionOps};
+use ::complex::Complex;
/// A data structure representing a sinusoid. AKA the sin or cos functions.
///
@@ -7,6 +8,7 @@ use ::num_traits::{Trig, ArithmeticOps, FractionOps};
///
/// The number type is generic, but realistically it's only useful for
/// floats.
+#[derive(Debug, Clone, PartialEq)]
pub struct Sinusoid<T> {
amplitude: T,
frequency: T,
@@ -31,12 +33,86 @@ impl<T> Sinusoid<T> where T: FractionOps + Copy {
/// use std::f32;
///
/// let sinusoid = Sinusoid::new(1.0 as f32, 0.5, 0.0);
- /// assert!((sinusoid.period()-2.0) < f32::EPSILON);
+ /// assert!((sinusoid.period()-2.0).abs() < f32::EPSILON);
/// ```
pub fn period(&self) -> T {
self.frequency.recip()
}
}
+
+#[derive(Debug, PartialEq)]
+pub enum AddSinusoidError {
+ DifferentFrequency
+}
+
+impl<T> Sinusoid<T> where T: Trig + Pow + ArithmeticOps + Copy {
+ /// Converts the sinusoid to phaser form
+ ///
+ /// This discards the frequency. Take care to only compare phasers
+ /// that had the same frequency before their conversion.
+ ///
+ /// ```
+ /// use worthe_signals::sinusoid::Sinusoid;
+ /// use worthe_signals::complex::Complex;
+ /// use std::f32;
+
+ /// let pure_real = Sinusoid::new(1.0 as f32, 0.5, 0.0);
+ /// let pure_real_phasor = pure_real.to_phaser();
+ /// assert!((pure_real_phasor.real - 1.0).abs() < f32::EPSILON);
+ /// assert!((pure_real_phasor.imag - 0.0).abs() < f32::EPSILON);
+ ///
+ /// let pure_imag = Sinusoid::new(1.0 as f32, 2.0, -f32::consts::FRAC_PI_2);
+ /// let pure_imag_phasor = pure_imag.to_phaser();
+ /// assert!((pure_imag_phasor.real - 0.0).abs() < f32::EPSILON);
+ /// assert!((pure_imag_phasor.imag + 1.0).abs() < f32::EPSILON);
+ /// ```
+ pub fn to_phaser(self) -> Complex<T> {
+ Complex::from_polar(self.amplitude, self.phase)
+ }
+}
+
+impl<T> Sinusoid<T> where T: Trig + Pow + ArithmeticOps + PartialEq + Copy {
+ /// Adds two sinusoids together into one sinusoid.
+ ///
+ /// # Errors
+ ///
+ /// This can only be done if the two sinusoids have the same
+ /// frequency.
+ ///
+ /// ```
+ /// use worthe_signals::sinusoid::{Sinusoid, AddSinusoidError};
+ ///
+ /// let sin1 = Sinusoid::new(1.0 as f32, 0.5, 0.0);
+ /// let sin2 = Sinusoid::new(1.0 as f32, 2.0, 0.0);
+ /// assert_eq!(Err(AddSinusoidError::DifferentFrequency), sin1.add(sin2));
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use worthe_signals::sinusoid::{Sinusoid, AddSinusoidError};
+ /// use std::f32;
+ ///
+ /// let sin1 = Sinusoid::new(-3.0 as f32, 1.0, 0.0);
+ /// let sin2 = Sinusoid::new(4.0 as f32, 1.0, -f32::consts::FRAC_PI_2);
+ /// assert_eq!(Ok(Sinusoid::new(5.0, 1.0, -2.21429743558818100603413092035707408014009529080286529335)), sin1.add(sin2));
+ /// ```
+ pub fn add(self, other: Sinusoid<T>) -> Result<Self, AddSinusoidError> {
+ if self.frequency != other.frequency {
+ Err(AddSinusoidError::DifferentFrequency)
+ }
+ else {
+ let frequency = self.frequency;
+ let self_phaser = self.to_phaser();
+ let other_phaser = other.to_phaser();
+ let combined = self_phaser + other_phaser;
+ let (amplitude, phase) = combined.to_polar();
+ Ok(Sinusoid::new(amplitude, frequency, phase))
+ }
+
+ }
+}
+
impl<T> Sinusoid<T> where T: FractionOps + ArithmeticOps + Trig + Copy {
/// Frequency can be considered in terms of the signal's number of
/// repetitions per second (referred to just as the frequency), or
@@ -60,11 +136,11 @@ impl<T> Sinusoid<T> where T: FractionOps + ArithmeticOps + Trig + Copy {
/// use std::f32;
///
/// let sinusoid = Sinusoid::new(1.0 as f32, 1.0, -f32::consts::FRAC_PI_2); //AKA sin
- /// assert!((sinusoid.sample(0.0)-0.0) < f32::EPSILON);
- /// assert!((sinusoid.sample(0.25)-1.0) < f32::EPSILON);
- /// assert!((sinusoid.sample(0.5)-0.0) < f32::EPSILON);
- /// assert!((sinusoid.sample(0.75)+1.0) < f32::EPSILON);
- /// assert!((sinusoid.sample(1.0)-0.0) < f32::EPSILON);
+ /// assert!((sinusoid.sample(0.0)-0.0).abs() < f32::EPSILON);
+ /// assert!((sinusoid.sample(0.25)-1.0).abs() < f32::EPSILON);
+ /// assert!((sinusoid.sample(0.5)-0.0).abs() < f32::EPSILON);
+ /// assert!((sinusoid.sample(0.75)+1.0).abs() < f32::EPSILON);
+ /// assert!((sinusoid.sample(1.0)-0.0).abs() < f32::EPSILON);
/// ```
pub fn sample(&self, t: T) -> T {
(self.radial_frequency()*(t%self.period()) + self.phase).cos() * self.amplitude
@@ -83,10 +159,10 @@ impl<T> Sinusoid<T> where T: FractionOps + ArithmeticOps + From<u16> + Trig + Co
/// let samples = sinusoid.sample_range(0.0, 100.0, 4.0);
/// assert_eq!(samples.len(), 400);
/// for i in (0..100).map(|i| i*4) {
- /// assert!((samples[i+0]-0.0) < f32::EPSILON, "Sample {} was {}", i+0, samples[i+0]);
- /// assert!((samples[i+1]-1.0) < f32::EPSILON, "Sample {} was {}", i+1, samples[i+1]);
- /// assert!((samples[i+2]-0.0) < f32::EPSILON, "Sample {} was {}", i+2, samples[i+2]);
- /// assert!((samples[i+3]+1.0) < f32::EPSILON, "Sample {} was {}", i+3, samples[i+3]);
+ /// assert!((samples[i+0]-0.0).abs() < f32::EPSILON, "Sample {} was {}", i+0, samples[i+0]);
+ /// assert!((samples[i+1]-1.0).abs() < f32::EPSILON, "Sample {} was {}", i+1, samples[i+1]);
+ /// assert!((samples[i+2]-0.0).abs() < f32::EPSILON, "Sample {} was {}", i+2, samples[i+2]);
+ /// assert!((samples[i+3]+1.0).abs() < f32::EPSILON, "Sample {} was {}", i+3, samples[i+3]);
/// }
/// ```
pub fn sample_range(&self, start: T, end: T, sample_rate: T) -> Vec<T> {
@@ -105,9 +181,11 @@ impl<T> Sinusoid<T> where T: FractionOps + ArithmeticOps + From<u16> + Trig + Co
}
}
+/*
#[cfg(test)]
mod tests {
use super::*;
use std::f32;
}
+*/