Example #1
0
def lanczos_interpolation(data, old_start, old_dt, new_start, new_dt, new_npts,
                          a, window="lanczos", *args, **kwargs):
    r"""
    Function performing Lanczos resampling, see
    https://en.wikipedia.org/wiki/Lanczos_resampling for details. Essentially a
    finite support version of sinc resampling (the ideal reconstruction
    filter). For large values of ``a`` it converges towards sinc resampling. If
    used for downsampling, make sure to apply an appropriate anti-aliasing
    lowpass filter first.

    .. note::

        In most cases you do not want to call this method directly but invoke
        it via either the :meth:`obspy.core.stream.Stream.interpolate` or
        :meth:`obspy.core.trace.Trace.interpolate` method. These offer a nicer
        API that naturally integrates with the rest of ObsPy. Use
        ``method="lanczos"`` to use this interpolation method. In that case the
        only additional parameters of interest are ``a`` and ``window``.

    :type data: array_like
    :param data: Array to interpolate.
    :type old_start: float
    :param old_start: The start of the array as a number.
    :type old_start: float
    :param old_dt: The time delta of the current array.
    :type new_start: float
    :param new_start: The start of the interpolated array. Must be greater
        or equal to the current start of the array.
    :type new_dt: float
    :param new_dt: The desired new time delta.
    :type new_npts: int
    :param new_npts: The new number of samples.
    :type a: int
    :param a: The width of the window in samples on either side. Runtime
        scales linearly with the value of ``a`` but the interpolation also gets
        better.
    :type window: str
    :param window: The window used to taper the sinc function. One of
        ``"lanczos"``, ``"hanning"``, ``"blackman"``. The window determines
        the trade-off between "sharpness" and the amplitude of the wiggles in
        the pass and stop band. Please use the
        :func:`~obspy.signal.interpolation.plot_lanczos_windows` function to
        judge these for any given application.

    Values of ``a`` >= 20 show good results even for data that has
    energy close to the Nyquist frequency. If your data is extremely
    oversampled you can get away with much smaller ``a``'s.

    To get an idea of the response of the filter and the effect of the
    different windows, please use the
    :func:`~obspy.signal.interpolation.plot_lanczos_windows` function.

    Also be aware of any boundary effects. All values outside the data
    range are assumed to be zero which matters when calculating interpolated
    values at the boundaries. At each side the area with potential boundary
    effects is ``a`` * ``old_dt``. If you want to avoid any boundary effects
    you will have to remove these values.

    **Mathematical Details:**

    The :math:`\operatorname{sinc}` function is defined as

    .. math::

        \operatorname{sinc}(t) = \frac{\sin(\pi t)}{\pi t}.

    The Lanczos kernel is then given by a multiplication of the
    :math:`\operatorname{sinc}` function with an additional window function
    resulting in a finite support kernel.

    .. math::

        \begin{align}
            L(t) =
            \begin{cases}
                \operatorname{sinc}(t)\, \cdot \operatorname{sinc}(t/a)
                    & \text{if } t \in [-a, a]
                    \text{ and } \texttt{window} = \texttt{lanczos}\\
                \operatorname{sinc}(t)\, \cdot \frac{1}{2}
                (1 + \cos(\pi\, t/a))
                    & \text{if } t \in [-a, a]
                    \text{ and } \texttt{window} = \texttt{hanning}\\
                \operatorname{sinc}(t)\, \cdot \left( \frac{21}{50} +
                \frac{1}{2}
                \cos(\pi\, t/a) + \frac{2}{25} \cos (2\pi\, t/a) \right)
                    & \text{if } t \in [-a, a]
                    \text{ and } \texttt{window} = \texttt{blackman}\\
                0                     & \text{else}
            \end{cases}
        \end{align}


    Finally interpolation is performed by convolving the discrete signal
    :math:`s_i` with that kernel and evaluating it at the new time samples
    :math:`t_j`:

    .. math::

        \begin{align}
            S(t_j) =
                \sum_{i = \left \lfloor{t_j / \Delta t}\right \rfloor -a + 1}
                    ^{\left \lfloor{t_j / \Delta t}\right \rfloor + a}
            s_i L(t_j/\Delta t - i),
        \end{align}

    where :math:`\lfloor \cdot \rfloor` denotes the floor function. For more
    details and justification please see [Burger2009]_ and [vanDriel2015]_.
    """
    _validate_parameters(data, old_start, old_dt, new_start, new_dt, new_npts)

    # dt and offset in terms of the original sampling interval.
    dt_factor = float(new_dt) / old_dt
    offset = (new_start - old_start) / float(old_dt)

    if offset < 0:
        raise ValueError("Cannot extrapolate. Make sure to only interpolate "
                         "within the time range of the original signal.")

    if a < 1:
        raise ValueError("a must be at least 1.")

    return_data = np.zeros(new_npts, dtype=np.float64)

    clibsignal.lanczos_resample(
        np.require(data, dtype=np.float64), return_data, dt_factor, offset,
        len(data), len(return_data), int(a), 0)
    return return_data
Example #2
0
def lanczos_interpolation(data,
                          old_start,
                          old_dt,
                          new_start,
                          new_dt,
                          new_npts,
                          a,
                          window="lanczos",
                          *args,
                          **kwargs):
    r"""
    Function performing Lanczos resampling, see
    https://en.wikipedia.org/wiki/Lanczos_resampling for details. Essentially a
    finite support version of sinc resampling (the ideal reconstruction
    filter). For large values of ``a`` it converges towards sinc resampling. If
    used for downsampling, make sure to apply an appropriate anti-aliasing
    lowpass filter first.

    .. note::

        In most cases you do not want to call this method directly but invoke
        it via either the :meth:`obspy.core.stream.Stream.interpolate` or
        :meth:`obspy.core.trace.Trace.interpolate` method. These offer a nicer
        API that naturally integrates with the rest of ObsPy. Use
        ``method="lanczos"`` to use this interpolation method. In that case the
        only additional parameters of interest are ``a`` and ``window``.

    :type data: array_like
    :param data: Array to interpolate.
    :type old_start: float
    :param old_start: The start of the array as a number.
    :type old_start: float
    :param old_dt: The time delta of the current array.
    :type new_start: float
    :param new_start: The start of the interpolated array. Must be greater
        or equal to the current start of the array.
    :type new_dt: float
    :param new_dt: The desired new time delta.
    :type new_npts: int
    :param new_npts: The new number of samples.
    :type a: int
    :param a: The width of the window in samples on either side. Runtime
        scales linearly with the value of ``a`` but the interpolation also gets
        better.
    :type window: str
    :param window: The window used to taper the sinc function. One of
        ``"lanczos"``, ``"hanning"``, ``"blackman"``. The window determines
        the trade-off between "sharpness" and the amplitude of the wiggles in
        the pass and stop band. Please use the
        :func:`~obspy.signal.interpolation.plot_lanczos_windows` function to
        judge these for any given application.

    Values of ``a`` >= 20 show good results even for data that has
    energy close to the Nyquist frequency. If your data is extremely
    oversampled you can get away with much smaller ``a``'s.

    To get an idea of the response of the filter and the effect of the
    different windows, please use the
    :func:`~obspy.signal.interpolation.plot_lanczos_windows` function.

    Also be aware of any boundary effects. All values outside the data
    range are assumed to be zero which matters when calculating interpolated
    values at the boundaries. At each side the area with potential boundary
    effects is ``a`` * ``old_dt``. If you want to avoid any boundary effects
    you will have to remove these values.

    **Mathematical Details:**

    The :math:`\operatorname{sinc}` function is defined as

    .. math::

        \operatorname{sinc}(t) = \frac{\sin(\pi t)}{\pi t}.

    The Lanczos kernel is then given by a multiplication of the
    :math:`\operatorname{sinc}` function with an additional window function
    resulting in a finite support kernel.

    .. math::

        \begin{align}
            L(t) =
            \begin{cases}
                \operatorname{sinc}(t)\, \cdot \operatorname{sinc}(t/a)
                    & \text{if } t \in [-a, a]
                    \text{ and } \texttt{window} = \texttt{lanczos}\\
                \operatorname{sinc}(t)\, \cdot \frac{1}{2}
                (1 + \cos(\pi\, t/a))
                    & \text{if } t \in [-a, a]
                    \text{ and } \texttt{window} = \texttt{hanning}\\
                \operatorname{sinc}(t)\, \cdot \left( \frac{21}{50} +
                \frac{1}{2}
                \cos(\pi\, t/a) + \frac{2}{25} \cos (2\pi\, t/a) \right)
                    & \text{if } t \in [-a, a]
                    \text{ and } \texttt{window} = \texttt{blackman}\\
                0                     & \text{else}
            \end{cases}
        \end{align}


    Finally interpolation is performed by convolving the discrete signal
    :math:`s_i` with that kernel and evaluating it at the new time samples
    :math:`t_j`:

    .. math::

        \begin{align}
            S(t_j) =
                \sum_{i = \left \lfloor{t_j / \Delta t}\right \rfloor -a + 1}
                    ^{\left \lfloor{t_j / \Delta t}\right \rfloor + a}
            s_i L(t_j/\Delta t - i),
        \end{align}

    where :math:`\lfloor \cdot \rfloor` denotes the floor function. For more
    details and justification please see [Burger2009]_ and [vanDriel2015]_.
    """
    _validate_parameters(data, old_start, old_dt, new_start, new_dt, new_npts)

    # dt and offset in terms of the original sampling interval.
    dt_factor = float(new_dt) / old_dt
    offset = (new_start - old_start) / float(old_dt)

    if offset < 0:
        raise ValueError("Cannot extrapolate. Make sure to only interpolate "
                         "within the time range of the original signal.")

    if a < 1:
        raise ValueError("a must be at least 1.")

    return_data = np.zeros(new_npts, dtype=np.float64)

    clibsignal.lanczos_resample(np.require(data, dtype=np.float64),
                                return_data, dt_factor, offset, len(data),
                                len(return_data), int(a), 0)
    return return_data