Skip to content

Commit 91441d4

Browse files
committed
fix!: add explicit interpolation method for more functions
1 parent f97d593 commit 91441d4

File tree

7 files changed

+185
-110
lines changed

7 files changed

+185
-110
lines changed

argus-core/src/signals/cmp_ops.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
use std::cmp::Ordering;
22

3-
use super::interpolation::Linear;
43
use super::traits::SignalPartialOrd;
54
use super::{InterpolationMethod, Signal};
65

7-
impl<T> SignalPartialOrd for Signal<T>
6+
impl<T> SignalPartialOrd<T> for Signal<T>
87
where
98
T: PartialOrd + Clone,
10-
Linear: InterpolationMethod<T>,
119
{
12-
fn signal_cmp<F>(&self, other: &Self, op: F) -> Option<Signal<bool>>
10+
fn signal_cmp<F, I>(&self, other: &Self, op: F) -> Option<Signal<bool>>
1311
where
1412
F: Fn(Ordering) -> bool,
13+
I: InterpolationMethod<T>,
1514
{
1615
// This has to be manually implemented and cannot use the apply2 functions.
1716
// This is because if we have two signals that cross each other, then there is
1817
// an intermediate point where the two signals are equal. This point must be
1918
// added to the signal appropriately.
2019
// the union of the sample points in self and other
21-
let sync_points = self.sync_with_intersection::<Linear>(other)?;
20+
let sync_points = self.sync_with_intersection::<I>(other)?;
2221
let sig: Option<Signal<bool>> = sync_points
2322
.into_iter()
2423
.map(|t| {
25-
let lhs = self.interpolate_at::<Linear>(t).unwrap();
26-
let rhs = other.interpolate_at::<Linear>(t).unwrap();
24+
let lhs = self.interpolate_at::<I>(t).unwrap();
25+
let rhs = other.interpolate_at::<I>(t).unwrap();
2726
let cmp = lhs.partial_cmp(&rhs);
2827
cmp.map(|v| (t, op(v)))
2928
})
@@ -35,16 +34,18 @@ where
3534
impl<T> Signal<T>
3635
where
3736
T: PartialOrd + Clone,
38-
Linear: InterpolationMethod<T>,
3937
{
4038
/// Compute the time-wise min of two signals
41-
pub fn min(&self, other: &Self) -> Self {
42-
let time_points = self.sync_with_intersection::<Linear>(other).unwrap();
39+
pub fn min<I>(&self, other: &Self) -> Self
40+
where
41+
I: InterpolationMethod<T>,
42+
{
43+
let time_points = self.sync_with_intersection::<I>(other).unwrap();
4344
time_points
4445
.into_iter()
4546
.map(|t| {
46-
let lhs = self.interpolate_at::<Linear>(t).unwrap();
47-
let rhs = other.interpolate_at::<Linear>(t).unwrap();
47+
let lhs = self.interpolate_at::<I>(t).unwrap();
48+
let rhs = other.interpolate_at::<I>(t).unwrap();
4849
if lhs < rhs {
4950
(t, lhs)
5051
} else {
@@ -55,13 +56,16 @@ where
5556
}
5657

5758
/// Compute the time-wise max of two signals
58-
pub fn max(&self, other: &Self) -> Self {
59-
let time_points = self.sync_with_intersection::<Linear>(other).unwrap();
59+
pub fn max<I>(&self, other: &Self) -> Self
60+
where
61+
I: InterpolationMethod<T>,
62+
{
63+
let time_points = self.sync_with_intersection::<I>(other).unwrap();
6064
time_points
6165
.into_iter()
6266
.map(|t| {
63-
let lhs = self.interpolate_at::<Linear>(t).unwrap();
64-
let rhs = other.interpolate_at::<Linear>(t).unwrap();
67+
let lhs = self.interpolate_at::<I>(t).unwrap();
68+
let rhs = other.interpolate_at::<I>(t).unwrap();
6569
if lhs > rhs {
6670
(t, lhs)
6771
} else {

argus-core/src/signals/num_ops.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ impl<T> Signal<T> {
4343
pub fn sub<U, I>(&self, other: &Signal<T>) -> Signal<U>
4444
where
4545
for<'t> &'t T: core::ops::Sub<Output = U>,
46-
T: Clone,
46+
T: Clone + PartialOrd,
4747
I: InterpolationMethod<T>,
4848
{
49-
self.binary_op::<_, _, I>(other, |u, v| u - v)
49+
self.binary_op_with_intersection::<_, _, I>(other, |u, v| u - v)
5050
}
5151

5252
/// Perform sample-wise division of the two signals.
@@ -86,7 +86,7 @@ impl<T> Signal<T> {
8686
T: Clone + PartialOrd,
8787
I: InterpolationMethod<T>,
8888
{
89-
self.binary_op::<_, _, I>(other, |u, v| if u < v { v - u } else { u - v })
89+
self.binary_op_with_intersection::<_, _, I>(other, |u, v| if u < v { v - u } else { u - v })
9090
}
9191
}
9292

argus-core/src/signals/shift_ops.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,17 @@ use core::time::Duration;
33

44
use itertools::Itertools;
55

6-
use super::interpolation::Linear;
76
use super::{InterpolationMethod, Signal};
87

98
impl<T> Signal<T>
109
where
1110
T: Copy,
12-
Linear: InterpolationMethod<T>,
1311
{
1412
/// Shift all samples in the signal by `delta` amount to the left.
1513
///
1614
/// This essentially filters out all samples with time points greater than `delta`,
1715
/// and subtracts `delta` from the rest of the time points.
18-
pub fn shift_left(&self, delta: Duration) -> Self {
16+
pub fn shift_left<I: InterpolationMethod<T>>(&self, delta: Duration) -> Self {
1917
match self {
2018
Signal::Sampled { values, time_points } => {
2119
// We want to skip any time points < delta, and subtract the rest.
@@ -34,7 +32,7 @@ where
3432
if idx > 0 && first_t != &delta {
3533
// The shifted signal will not start at 0, and we have a previous
3634
// index to interpolate from.
37-
let v = self.interpolate_at::<Linear>(delta).unwrap();
35+
let v = self.interpolate_at::<I>(delta).unwrap();
3836
new_samples.push((Duration::ZERO, v));
3937
}
4038
// Shift the rest of the samples

argus-core/src/signals/traits.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,27 @@ macro_rules! impl_signal_cmp {
4242
($cmp:ident) => {
4343
paste! {
4444
/// Compute the time-wise comparison of two signals
45-
fn [<signal_ $cmp>](&self, other: &Rhs) -> Option<Signal<bool>> {
46-
self.signal_cmp(other, |ord| ord.[<is_ $cmp>]())
45+
fn [<signal_ $cmp>]<I>(&self, other: &Rhs) -> Option<Signal<bool>>
46+
where
47+
I: InterpolationMethod<T>
48+
{
49+
self.signal_cmp::<_, I>(other, |ord| ord.[<is_ $cmp>]())
4750
}
4851
}
4952
};
5053
}
5154

5255
/// A time-wise partial ordering defined for signals
53-
pub trait SignalPartialOrd<Rhs = Self> {
56+
pub trait SignalPartialOrd<T, Rhs = Self> {
5457
/// Compare two signals within each of their domains (using [`PartialOrd`]) and
5558
/// apply the given function `op` to the ordering to create a signal.
5659
///
5760
/// This function returns `None` if the comparison isn't possible, namely, when
5861
/// either of the signals are empty.
59-
fn signal_cmp<F>(&self, other: &Rhs, op: F) -> Option<Signal<bool>>
62+
fn signal_cmp<F, I>(&self, other: &Rhs, op: F) -> Option<Signal<bool>>
6063
where
61-
F: Fn(Ordering) -> bool;
64+
F: Fn(Ordering) -> bool,
65+
I: InterpolationMethod<T>;
6266

6367
impl_signal_cmp!(lt);
6468
impl_signal_cmp!(le);

argus-core/src/signals/utils.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,35 @@ impl<T> Signal<T> {
6565
}
6666
}
6767
}
68+
69+
pub(crate) fn binary_op_with_intersection<U, F, Interp>(&self, other: &Signal<T>, op: F) -> Signal<U>
70+
where
71+
T: Clone + PartialOrd,
72+
F: Fn(&T, &T) -> U,
73+
Interp: InterpolationMethod<T>,
74+
{
75+
use Signal::*;
76+
match (self, other) {
77+
// If either of the signals are empty, we return an empty signal.
78+
(Empty, _) | (_, Empty) => Signal::Empty,
79+
(Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(op(v1, v2)),
80+
(lhs, rhs) => {
81+
// We determine the range of the signal (as the output signal can only be
82+
// defined in the domain where both signals are defined).
83+
let time_points = lhs.sync_with_intersection::<Interp>(rhs).unwrap();
84+
// Now, at each of the merged time points, we sample each signal and operate on
85+
// them
86+
time_points
87+
.into_iter()
88+
.map(|t| {
89+
let v1 = lhs.interpolate_at::<Interp>(t).unwrap();
90+
let v2 = rhs.interpolate_at::<Interp>(t).unwrap();
91+
(t, op(&v1, &v2))
92+
})
93+
.collect()
94+
}
95+
}
96+
}
6897
}
6998

7099
fn partial_min<T>(a: T, b: T) -> Option<T>

0 commit comments

Comments
 (0)