Reputation: 185
A left shift of t0 in a signal can be obtained by convolving it with δ(t+t0). I want to obtain this in python using discrete signals by only using the np.convolve
operator in mode='full'
. Note that I can't use np.roll
or assignment from an index like [shift:]
. It has to be obtained purely by convolution. Here's an example:
signal = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
shift = 3
filter = ??
shifted_signal = np.convolve(signal, filter, mode='full')
print(shifted_signal)
[4, 5, 6, 7, 8, 9, 0, 0, 0, ...
The number of zeros at the right side, or to say the length of shifted_signal
is not important. But I want the signal to start from the correct position. What should the filter
be such that I get the desired output? I need it this way because I am trying to obtain the left shift from an FFT analysis. A left shift of t0 in the time domain corresponds to a multiplication of ei2πft0 in the frequency domain. I can obtain t0 by comparing the shifted signal with the original in the frequency domain. But to check if my algorithm works, I just can't think of a filter that performs a left shift in the time domain.
I tried filter = [0, 0, 0, 1]
and filter = [1, 0, 0, 0]
but they adds zeros to the left and right of the signal. The signal itself doesn't start from the desired index. I have no idea what other filter I can use to obtain the desired result.
Upvotes: 0
Views: 356
Reputation: 185
After realizing that it may not be possible to perform a left shift via convolution with a Dirac delta function using only np.convolve(, mode='full')
since there is no concept of negative indices in arrays in python, I manually defined a time
axis to perform discrete convolution based on its elements rather than the indices of arrays. The execution is crude and runs very slowly for large arrays but it serves as a proof-of-concept. The code below shows an example.
Special mention to @dankal444 who also had the same suggestion in the edit to his answer.
# performing a left shift via convolution in python
import numpy as np
import matplotlib.pyplot as plt
# custom define a convolution function
def convolve(signal, kernel, time):
# initialize a convolution array
conv = np.zeros_like(time)
# flip the kernel about time=0
flipped_kernel = np.flip(kernel)
inverted_time = np.flip(time)*-1
# now loop through time
for i, t in enumerate(time):
# shift the flipped kernel
shifted_inverted_time = inverted_time + t
# get the times when the flipped kernel overlaps with the signal
time_overlap_indices = np.where((time >= shifted_inverted_time.min()) & (time <= shifted_inverted_time.max()))[0]
sit_overlap_indices = np.where((shifted_inverted_time >= time.min()) & (shifted_inverted_time <= time.max()))[0]
# make sure that both have the same times
if len(time_overlap_indices) > len(sit_overlap_indices):
if time[time_overlap_indices[0]] != shifted_inverted_time[sit_overlap_indices[0]] or \
time[time_overlap_indices[-1]] != shifted_inverted_time[sit_overlap_indices[-1]]:
time_overlap_indices = time_overlap_indices[1:] if (time[time_overlap_indices[0]] !=
shifted_inverted_time[sit_overlap_indices[0]]) \
else time_overlap_indices[:-1]
elif len(time_overlap_indices) < len(sit_overlap_indices):
if shifted_inverted_time[sit_overlap_indices[0]] != time[time_overlap_indices[0]] or \
shifted_inverted_time[sit_overlap_indices[-1]] != time[time_overlap_indices[-1]]:
sit_overlap_indices = sit_overlap_indices[1:] if (shifted_inverted_time[sit_overlap_indices[0]] !=
time[time_overlap_indices[0]]) \
else sit_overlap_indices[:-1]
# get the convolution at index 'i'
conv[i] = np.sum(signal[time_overlap_indices] * flipped_kernel[sit_overlap_indices])
return conv
# Example 1D signal
time = np.linspace(-10, 8, 10000)
signal = np.exp(-(time-5)**2)
# define a left shift Dirac delta function
shift = -9 # in μs
leftShift = np.zeros_like(time)
leftShift[np.where(time >= shift)[0][0]] = 1
# get the convolved signal
convolved_signal = convolve(signal, leftShift, time)
# plot the signal, kernel and the convolved signal
plt.figure()
# plot the signals in the time domain
plt.plot(time, signal, label='signal', color='C0')
plt.axvline(time[np.argmax(signal)], color='C0', linestyle='--',
label='signal peak at t = {} μs'.format(np.round(time[np.argmax(signal)], 2)))
plt.axvline(x=shift, ymin=0, ymax=1, color='C2', linestyle='--',
label='left shift of {} μs'.format(np.round(shift, 2)))
plt.plot(time, convolved_signal, label='convolved signal', color='C1')
plt.axvline(time[np.argmax(convolved_signal)], color='C1', linestyle='--',
label='convolved signal \npeak at t = {} μs'.format(np.round(time[np.argmax(convolved_signal)], 2)))
# append new ticks to the current ticks
plt.xticks(np.append(plt.gca().get_xticks(), np.round([shift, time[np.argmax(signal)], time[np.argmax(convolved_signal)]], 2)))
plt.xlabel('Time (in μs)'), plt.ylabel('Amplitude'), plt.grid(), plt.legend()
plt.title('Left shift of {} μs'.format(shift))
Upvotes: 0
Reputation: 4158
You can't shift signal left using standard convolution (without cutting the signal as mode 'same' does). There is no filter kernel that could accomplish that. If you could do that you could also travel back in time or foresee future.
Convolution can represent Linear System that has an input and some changed delayed (or not, but definitely not hastened) output. If it could "hasten" signals it would mean it could read future - give some information on the output about signal that hasn't yet entered input.
However, we are in digital domain, and can interpret results of the discrete convolution a bit arbitrarily. We can assume any time domain we want for the convolution kernel.
We can say that kernel (e.g. [1, 0, 0]) has time domain of [-1, 0, 1]. For simplicity signal has time domain [0, 1, 2..]. This way result of the convolution will have time domain shifted, to the beginning of kernel: [-1, 0, 1, 2..]. And we accomplished shifting signal left.
We could use any time domain we wanted for the kernel and have different result, but the one that is symmetrical around 0 seems common sense.
Upvotes: 0
Reputation: 29
I got your desired result by switching the mode to same
. Here is the code
import numpy as np
signal = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
shift = 3
filter = [1]
for i in filter:
if len(filter) < 2 * shift + 1:
filter.append(0)
else:
break
shifted_signal = np.convolve(signal, filter, mode='same')
print(shifted_signal)
Output is
[4 5 6 7 8 9 0 0 0]
This could probably be improved by using list comprehension
Upvotes: 0