End of chirp in phase 0

I would like the chirp to end in phase zero. Chirp time or end frequency may vary slightly.

Now I'm checking the maplot output. I observe the same thing at the output of the sound card.

In this solution, the chirp does not always end in phase zero.

help find a solution.

Thanks for the help. Andrew

from pylab import *
import numpy as np
from scipy.signal import chirp

f0 = 7000
f1 = 17000
samplerate = 192000
T = 0.0013
T = np.ceil(T*f1)/f1 # new T
t = np.arange(0, int(T*samplerate)) / samplerate
w = chirp(t, f0=f0, f1=f1, t1=T,phi=270, method='linear')

fig, ax = subplots(figsize=(6,1))
ax.set_title("Chirp ")
ax.plot(w)
show()


2 Answers

Derivations here - you can pick any tmin, tmax, and fmin, fmax for some number of samples or sampling rate N.

We adjust the code one line toward the end to rescale phi to end as an integer multiple of $$2\pi$$ to yield zero phase; this has the effect of nudging fmin and fmax, slightly or greatly depending on all other parameters - see here.

An alternative variant that forces only the end of the chirp to be zero phase will exactly preserve fmin and fmax, by subtracting. Forcing all, zero phase at tmin and tmax without changing fmin and fmax is impossible.

Taking your parameters, with cosine and sine:

Code

import numpy as np
import matplotlib.pyplot as plt

def _lchirp(N, tmin=0, tmax=1, fmin=0, fmax=None):
fmax = fmax if fmax is not None else N / 2
t = np.linspace(tmin, tmax, N, endpoint=True)

a = (fmin - fmax) / (tmin - tmax)
b = (fmin*tmax - fmax*tmin) / (tmax - tmin)

phi = (a/2)*(t**2 - tmin**2) + b*(t - tmin)
phi *= (2*np.pi)
return phi

def lchirp(N, tmin=0, tmax=1, fmin=0, fmax=None, zero_phase_tmin=True, cos=True):
phi = _lchirp(N, tmin, tmax, fmin, fmax)
if zero_phase_tmin:
phi *= ( (phi[-1] - phi[-1] % (2*np.pi)) / phi[-1] )
else:
phi -= (phi[-1] % (2*np.pi))
fn = np.cos if cos else np.sin
return fn(phi)

#%%######################################################################
f0 = 7000
f1 = 17000
samplerate = 192000
T = .0013

N = int(samplerate * T)
tmin = 0
tmax = T

t = np.linspace(tmin, tmax, N, endpoint=True)
for zero_phase_min in (True, False):
for cos in (True, False):
x = lchirp(N=int(samplerate * T), tmin=tmin, tmax=tmax, fmin=f0, fmax=f1,
zero_phase_tmin=zero_phase_min, cos=cos)
plt.plot(t, x)
plt.title("cos={}, zero_phase_tmin={}".format(cos, zero_phase_min),
weight='bold', fontsize=17, loc='left')
plt.show()

• many thanks. Andrew. Commented May 21, 2021 at 7:42
• added changes to my project. github.com/karu2003/sweep_gen Commented May 25, 2021 at 15:05

For a linear chirp you can do this by slightly "nudging" the upper frequency. I don't think the build in Python function supports so yo have to do this yourself manually.

Let's assume we have a chirp with N samples and the normalized start and end frequencies $$\omega_0$$ and $$\omega_0$$

The frequency as a function of time is then

$$\omega[n] = \omega_0 + (\omega_1-\omega_0)\frac{n}{N}$$

To get the phase we need to integrate

$$\phi[n] = \sum_{k=0}^n\omega[k] = \omega_0\cdot n + (\omega_1-\omega_0)\frac{n^2}{2N}$$

In order to have a seamless chirp you want the phase at $$n=N$$ to be an integer multiple of $$2\pi$$. We get

$$\phi[N] = \frac{N}{2}(\omega_1+\omega_0)$$

So you need to find the nearest integer multiple of $$2\pi$$ and then recalculate the upper frequency again.

$$\phi_{clean} = 2\pi\cdot round(\frac{\phi[N]}{2\pi})$$

$$\omega_{clean} = \frac{2 \phi_{clean}}{N}-\omega_0$$

and your sweep becomes finally

$$x[n] = sin \left ( \omega_0\cdot n + (\omega_{clean}-\omega_0)\frac{n^2}{2N} \right)$$

• Presumably the samples in the periodic chirp of period $N$ are numbered from $0$ to $N-1$? So that the phase is $0$ at $n=0$ and again at $n=N$ and so the next period of the periodic chirp starts again at $n=N$ with frequency $\omega_0$? Commented May 20, 2021 at 14:28
• Yes that's generally the assumption Commented May 20, 2021 at 14:35
• Thank you, i changed the code. the result is very bad. the new frequency and phase have not changed. github.com/karu2003/chirp_phase_zero :( :( Commented May 21, 2021 at 6:39