From 9cf783aeb2c89d52abde5c44b5b345983f549aea Mon Sep 17 00:00:00 2001
From: Wolfgang Kastaun <wolfgang.kastaun@aei.mpg.de>
Date: Fri, 20 Dec 2024 19:33:29 +0100
Subject: [PATCH] Changed definition of shift sign to the one used in
 dsp.timeshift.

Now a positive shift generally means a shift to the right, i.e. a delay is a negative shift.
---
 lisainstrument/dynamic_delay_dask.py    | 16 ++++++++--------
 lisainstrument/dynamic_delay_dsp.py     | 14 +++++++-------
 lisainstrument/dynamic_delay_numpy.py   | 20 ++++++++++----------
 lisainstrument/fixed_shift_dask.py      |  8 ++++----
 lisainstrument/fixed_shift_dsp.py       |  8 ++++----
 lisainstrument/fixed_shift_numpy.py     | 17 ++++++++---------
 lisainstrument/regular_interpolators.py | 10 ++++------
 7 files changed, 45 insertions(+), 48 deletions(-)

diff --git a/lisainstrument/dynamic_delay_dask.py b/lisainstrument/dynamic_delay_dask.py
index 5b8d662..1304b7f 100644
--- a/lisainstrument/dynamic_delay_dask.py
+++ b/lisainstrument/dynamic_delay_dask.py
@@ -28,8 +28,8 @@ class DynamicShiftDask:
     This allows to interpolate samples in a dask array to locations specified
     by a shift given by another dask array of same size. The shift is specified in
     units of the array index, i.e. there is no separate coordinate array.
-    A positive shift refers to values left of a given sample, negative shifts
-    to values on the right.
+    A positive shift refers to values right of a given sample, negative shifts
+    to values on the left.
 
     The boundary treatment can be specified for each boundary in terms of
     DynShiftBC enums.
@@ -79,8 +79,8 @@ class DynamicShiftDask:
 
         The shift and sample arrays need to have the same size, and each shift provides
         the interpolation location relative to the sample with the same index.
-        Shifts are floating point values. A shift of +1 refers to the sample on the left,
-        -1 the sample on the right, etc. All arrays have to be 1D.
+        Shifts are floating point values. A shift of +1 refers to the sample on the right,
+        -1 the sample on the left, etc. All arrays have to be 1D.
 
         Arguments:
             samples: 1D dask array with data samples
@@ -156,8 +156,8 @@ def make_dynamic_shift_linear_dask(
     """Set up DynamicShiftDask instance with linear interpolation method.
 
     Arguments:
-        min_delay: Assume that any shift < -min_delay
-        max_delay: Assume that any shift > -max_delay
+        min_delay: Assume that any shift > -max_delay
+        max_delay: Assume that any shift < -min_delay
         left_bound: Treatment of left boundary
         right_bound: Treatment of right boundary
 
@@ -180,8 +180,8 @@ def make_dynamic_shift_lagrange_dask(
 
     Arguments:
         length: Number of lagrange polynomials (of order length - 1)
-        min_delay: Assume that any shift < -min_delay
-        max_delay: Assume that any shift > -max_delay
+        min_delay: Assume that any shift > -max_delay
+        max_delay: Assume that any shift < -min_delay
         left_bound: Treatment of left boundary
         right_bound: Treatment of right boundary
 
diff --git a/lisainstrument/dynamic_delay_dsp.py b/lisainstrument/dynamic_delay_dsp.py
index bd0ae14..3c394ef 100644
--- a/lisainstrument/dynamic_delay_dsp.py
+++ b/lisainstrument/dynamic_delay_dsp.py
@@ -67,7 +67,7 @@ class RegularInterpDSP(RegularInterpCore):
     ) -> np.ndarray:
         """Interpolate regularly spaced data to location in index-space"""
         doff = int_offsets - np.arange(locations.shape[0])
-        shift = -locations + doff[0] - doff
+        shift = locations - doff[0] + doff
         shift_offset = doff[0]
 
         return self.apply_shift(samples, shift, shift_offset)
@@ -79,7 +79,7 @@ class RegularInterpDSP(RegularInterpCore):
         shift_offset: int,
     ) -> np.ndarray:
         """Iterpolate to location specified in terms of shifts instead absolute locations"""
-        shift_tot = shift - shift_offset
+        shift_tot = shift + shift_offset
 
         npad_right = 0
 
@@ -90,7 +90,7 @@ class RegularInterpDSP(RegularInterpCore):
             msg = "DSPWrapper: insufficient samples for interpolation"
             raise RuntimeError(msg)
 
-        res = dsp.timeshift(samples, -shift_tot, self._order)
+        res = dsp.timeshift(samples, shift_tot, self._order)
         if npad_right > 0:
             res = res[:-npad_right]
 
@@ -121,8 +121,8 @@ def make_dynamic_shift_dsp_dask(
 
     Arguments:
         order: Lagrange interpolation order (must be odd)
-        min_delay: Assume that any shift < -min_delay
-        max_delay: Assume that any shift > -max_delay
+        min_delay: Assume that any shift > -max_delay
+        max_delay: Assume that any shift < -min_delay
         left_bound: Treatment of left boundary
         right_bound: Treatment of right boundary
 
@@ -146,8 +146,8 @@ def make_dynamic_shift_dsp_numpy(
 
     Arguments:
         order: Lagrange interpolation order (must be odd)
-        min_delay: Assume that any shift < -min_delay
-        max_delay: Assume that any shift > -max_delay
+        min_delay: Assume that any shift > -max_delay
+        max_delay: Assume that any shift < -min_delay
         left_bound: Treatment of left boundary
         right_bound: Treatment of right boundary
 
diff --git a/lisainstrument/dynamic_delay_numpy.py b/lisainstrument/dynamic_delay_numpy.py
index 639a312..bb5f2f6 100644
--- a/lisainstrument/dynamic_delay_numpy.py
+++ b/lisainstrument/dynamic_delay_numpy.py
@@ -38,8 +38,8 @@ class DynShiftCfg:
     """Config class for dynamic shift interpolation
 
     Attributes:
-        min_delay: Assume that any shift < -min_delay
-        max_delay: Assume that any shift > -max_delay
+        min_delay: Assume that any shift > -max_delay
+        max_delay: Assume that any shift < -min_delay
         left_bound: Treatment of left boundary
         right_bound: Treatment of right boundary
     """
@@ -71,8 +71,8 @@ class DynamicShiftNumpy:
     This allows to interpolate samples in a numpy array to locations specified
     by a shift given by another numpy array of same size. The shift is specified in
     units of the array index, i.e. there is no separate coordinate array.
-    A positive shift refers to values left of a given sample, negative shifts
-    to values on the right.
+    A positive shift refers to values right of a given sample, negative shifts
+    to values on the left.
 
     The boundary treatment can be specified for each boundary in terms of
     DynShiftBC enums.
@@ -121,8 +121,8 @@ class DynamicShiftNumpy:
 
         The shift and sample arrays need to have the same size, and each shift provides
         the interpolation location relative to the sample with the same index.
-        Shifts are floating point values.A shift of +1 refers to the sample on the left,
-        -1 the sample on the right, etc. All arrays have to be 1D.
+        Shifts are floating point values. A shift of +1 refers to the sample on the right,
+        -1 the sample on the left, etc. All arrays have to be 1D.
 
 
         Arguments:
@@ -187,8 +187,8 @@ def make_dynamic_shift_lagrange_numpy(
 
     Arguments:
         length: Number of lagrange polynomials (of order length - 1)
-        min_delay: Assume that any shift < -min_delay
-        max_delay: Assume that any shift > -max_delay
+        min_delay: Assume that any shift > -max_delay
+        max_delay: Assume that any shift < -min_delay
         left_bound: Treatment of left boundary
         right_bound: Treatment of right boundary
 
@@ -209,8 +209,8 @@ def make_dynamic_shift_linear_numpy(
     """Set up DynamicShiftNumpy instance with linear interpolation method.
 
     Arguments:
-        min_delay: Assume that any shift < -min_delay
-        max_delay: Assume that any shift > -max_delay
+        min_delay: Assume that any shift > -max_delay
+        max_delay: Assume that any shift < -min_delay
         left_bound: Treatment of left boundary
         right_bound: Treatment of right boundary
 
diff --git a/lisainstrument/fixed_shift_dask.py b/lisainstrument/fixed_shift_dask.py
index 471a713..e6e2100 100644
--- a/lisainstrument/fixed_shift_dask.py
+++ b/lisainstrument/fixed_shift_dask.py
@@ -23,7 +23,7 @@ class FixedShiftDask:  # pylint: disable=too-few-public-methods
     This allows to interpolate samples in a Dask array to locations specified
     by a fixed shift. The shift is specified in units of the array index, i.e.
     there is no separate coordinate array. A positive shift refers to values
-    left of a given sample, negative shifts to values on the right.
+    right of a given sample, negative shifts to values on the left.
 
     The boundary treatment can be specified for each boundary in terms of
     DynShiftBC enums.
@@ -98,7 +98,7 @@ class FixedShiftDask:  # pylint: disable=too-few-public-methods
 
         Denoting the input data as $y_i$ with $i=0 \ldots N-1$, and the interpolated
         input data as $y(t)$, such that $y(i)=y_i$, the output $z_k$ is given by
-        $z_k = y(k-s), k=0 \ ldots N - 1$.
+        $z_k = y(k + s), k=0 \ ldots N - 1$.
 
         Required data outside the provided samples is created if the specified
         boundary condition allows it, or an exception is raised if the BC disallows it.
@@ -113,11 +113,11 @@ class FixedShiftDask:  # pylint: disable=too-few-public-methods
             Dask array with interpolated samples
         """
 
-        loc = -shift
+        loc = float(shift)
         loc_int = int(np.floor(loc))
         loc_frac = loc - loc_int
 
-        interp = self._interp_fac(-loc_frac)
+        interp = self._interp_fac(loc_frac)
 
         margin_left = interp.margin_left - loc_int
         margin_right = interp.margin_right + loc_int
diff --git a/lisainstrument/fixed_shift_dsp.py b/lisainstrument/fixed_shift_dsp.py
index c25597d..39afef8 100644
--- a/lisainstrument/fixed_shift_dsp.py
+++ b/lisainstrument/fixed_shift_dsp.py
@@ -44,8 +44,8 @@ class FixedShiftWrapDSP(FixedShiftCore):
         for each interpolation point. The order of the interpolating polynomials
         is also the order of plynomials that are interpolated with zero error.
 
-        The shift is given as a delay, i.e. positive values refer to locations
-        to the left of the output sample at hand.
+        The shift sign is defined such that positive values refer to locations
+        to the right of the output sample at hand.
 
         Arguments:
             length: Size of the interpolation stencil
@@ -60,7 +60,7 @@ class FixedShiftWrapDSP(FixedShiftCore):
             msg = "FixedShiftWrapDSP: even Lagrange polynomial order not supported by dsp.timeshift"
             raise ValueError(msg)
 
-        loc = -float(shift)
+        loc = float(shift)
         loc_int = int(np.floor(loc))
         # ~ loc_frac = loc - loc_int
 
@@ -97,7 +97,7 @@ class FixedShiftWrapDSP(FixedShiftCore):
             Shifted input excluding margins
         """
         a = make_numpy_array_1d_float(samples)
-        res = dsp.timeshift(a, -self.shift, self._order)
+        res = dsp.timeshift(a, self.shift, self._order)
 
         if self.margin_left > 0:
             res = res[self.margin_left :]
diff --git a/lisainstrument/fixed_shift_numpy.py b/lisainstrument/fixed_shift_numpy.py
index cf8d47d..77eab13 100644
--- a/lisainstrument/fixed_shift_numpy.py
+++ b/lisainstrument/fixed_shift_numpy.py
@@ -64,10 +64,9 @@ class FixedShiftCore(Protocol):
         r"""Apply the fractional shift to a 1D numpy array.
 
         The output is the shifted input with left and right margins removed.
-        The shift $s$ should be bounded $-1 < s < 1$
         Denoting the input data as $y_i$ with $i=0 \ldots N-1$, and the interpolated
         input data as $y(t)$, such that $y(i)=y_i$, the output $z_k$ is given by
-        $z_k = y(k+M_L-s), k=0 \ ldots N - 1 - M_L - M_R$, where $M_L$ and $M_R$
+        $z_k = y(k + M_L + s), k=0 \ ldots N - 1 - M_L - M_R$, where $M_L$ and $M_R$
         are the left and right margin sizes.
 
         Arguments:
@@ -166,15 +165,15 @@ class FixedShiftLagrange(FixedShiftCore):
         for each interpolation point. The order of the interpolating polynomials
         is also the order of plynomials that are interpolated with zero error.
 
-        The shift is given as a delay, i.e. positive values refer to locations
-        to the left of the output sample at hand.
+        The shift sign is defined such that positive values refer to locations
+        to the right of the output sample at hand.
 
         Arguments:
             length: Size of the interpolation stencil
             shift: The constant shift
         """
 
-        loc = -float(shift)
+        loc = float(shift)
         if length % 2 == 0:
             loc_int = int(np.floor(loc))
         else:
@@ -248,7 +247,7 @@ class FixedShiftNumpy:  # pylint: disable=too-few-public-methods
     This allows to interpolate samples in a numpy array to locations specified
     by a fixed shift. The shift is specified in units of the array index, i.e.
     there is no separate coordinate array. A positive shift refers to values
-    left of a given sample, negative shifts to values on the right.
+    right of a given sample, negative shifts to values on the left.
 
     The boundary treatment can be specified for each boundary in terms of
     DynShiftBC enums.
@@ -323,7 +322,7 @@ class FixedShiftNumpy:  # pylint: disable=too-few-public-methods
 
         Denoting the input data as $y_i$ with $i=0 \ldots N-1$, and the interpolated
         input data as $y(t)$, such that $y(i)=y_i$, the output $z_k$ is given by
-        $z_k = y(k-s), k=0 \ ldots N - 1$.
+        $z_k = y(k + s), k=0 \ ldots N - 1$.
 
         Required data outside the provided samples is created if the specified
         boundary condition allows it, or an exception is raised if the BC disallows it.
@@ -339,12 +338,12 @@ class FixedShiftNumpy:  # pylint: disable=too-few-public-methods
         """
 
         # pylint: disable=duplicate-code
-        loc = -shift
+        loc = float(shift)
         loc_int = int(np.floor(loc))
         loc_frac = loc - loc_int
         # pylint: enable=duplicate-code
 
-        interp = self._interp_fac(-loc_frac)
+        interp = self._interp_fac(loc_frac)
 
         margin_left = interp.margin_left - loc_int
         margin_right = interp.margin_right + loc_int
diff --git a/lisainstrument/regular_interpolators.py b/lisainstrument/regular_interpolators.py
index 4f4cc69..cc54d10 100644
--- a/lisainstrument/regular_interpolators.py
+++ b/lisainstrument/regular_interpolators.py
@@ -92,12 +92,12 @@ class RegularInterpCore(Protocol):
 
         The locations are specified via an array s of real-valued shifts. For the element s[i] of
         the shift array with array index i, the absolute location within the index space of the
-        input samples is given by i - s[i] + ofs, where ofs is a constant integer offset. A zero
+        input samples is given by i + s[i] + ofs, where ofs is a constant integer offset. A zero
         shift means the output sample with index i is the input sample with index i+ofs.
         The offset can be positive or negative. Shift values that would require samples not
         in the input are not allowed. The output should be the same as for
 
-        apply(samples, -shift, shift_offset + np.arange(shift.shape[0]))
+        apply(samples, shift, shift_offset + np.arange(shift.shape[0]))
 
         Arguments:
             samples: 1D numpy array with sampled data
@@ -266,10 +266,9 @@ class RegularInterpLagrange(RegularInterpCore):
         Returns:
             Interpolated samples
         """
-        loc = -shift
         offsets = shift_offset + np.arange(shift.shape[0])
         return self.apply(
-            samples, make_numpy_array_1d(loc), make_numpy_array_1d(offsets)
+            samples, make_numpy_array_1d(shift), make_numpy_array_1d(offsets)
         )
 
 
@@ -343,10 +342,9 @@ class RegularInterpLinear(RegularInterpCore):
         Returns:
             Interpolated samples
         """
-        loc = -shift
         offsets = shift_offset + np.arange(shift.shape[0])
         return self.apply(
-            samples, make_numpy_array_1d(loc), make_numpy_array_1d(offsets)
+            samples, make_numpy_array_1d(shift), make_numpy_array_1d(offsets)
         )
 
 
-- 
GitLab