Suppose I have the following Python function to apply any real-valued circular delay to a signal by multiplying by a phase factor in the frequency domain:

import numpy as np

def rfd_shift(sig, delay):
    ft = np.fft.rfft(sig)
    ft *= np.exp(2j * np.pi * delay * np.arange(len(ft)) / len(sig))
    return np.fft.irfft(ft)

Let's use this to apply a delay of 0.5 samples (twice) to an odd-length signal:

>>> sig = np.arange(5)
>>> rfd_shift(sig, 0.5)
array([-0.23606798,  2.        ,  2.        ,  4.23606798,  2.        ])
>>> rfd_shift(_, 0.5)
array([1.00000000e+00, 2.00000000e+00, 3.00000000e+00, 4.00000000e+00,

The intermediate result may look a bit unintuitive, but the fact that the second application resulted in a shift of exactly 1 sample suggests to me that this is working.

But if I try the same thing with an even-length signal:

>>> sig = np.arange(6)
>>> rfd_shift(sig, 0.5)
array([-0.23205081,  1.76794919,  2.5       ,  3.23205081,  5.23205081,
        2.5       ])
>>> rfd_shift(_, 0.5)
array([0.5, 2.5, 2.5, 4.5, 4.5, 0.5])

The intermediate result looks similarly plausible, but now the 1-sample delay is not recovered after the second application.

I have tracked down this discrepancy to the fact that in the even case the final frequency bin is shared by the positive and negative Nyquist frequency and so must be real-valued. This leads to the phase factor not being applied fully as this bin is projected onto the real axis when the inverse Fourier transform is performed.

To attempt to work around this, I tried the same with NumPy's FFT functions for complex-valued signals, but I was unable to retrieve a real-valued signal for any fractional delay. There were always significant imaginary components.

My questions: Am I doing something wrong? Is this simply not possible? Do I have to resort to FIR filters to achieve fractional delays for arbitrary signals?


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.