Overview:
A Real-Time sampling oscilloscope is designed to capture time-domain phenomena but the combination of an extremely accurate timebase and Fast Fourier Transforms can make them very useful at measuring frequency domain phenomena.
Phase Noise is traditionally measured using a frequency domain instrument such as a Spectrum Analyzer or specialised Signal Source Analyzer. While these frequency domain instruments typically have high dynamic range there are limitations to their measurement capability in terms of the offset frequency they are capable of measuring to or the fact they are unable to measure the phase noise of a non-clock signal.
First a bit of background on Phase Modulation PM.
Although phase noise refers to the uncorrelated portion of the phase modulation it is informative to take the simple example of sinusoidal phase modulation to build our intuition first.
f_c = 1e9 # The carrier frequency 1GHz
sa_rate = 80e9 # Sample rate 80GSa/s
mem_depth = 2**21 # Number of samples to take ~ 2 million (a power of 2 makes fft's simpler)
t = linspace(0,mem_depth/sa_rate,mem_depth) # Create the time sample array
def plot_wfm_spectrum(t,dt,f_c,beta,f_m):
"""Since we'll create a few plots of similar waveforms let's not duplicate our work"""
v = sin(2*pi*f_c*t + beta*cos(2*pi*f_m*t))
window = hanning(mem_depth)
v_windowed=v*window # Apply a Hanning window to improve the spectrum
v_spec = fft.fft(v_windowed)
freqs = fft.fftfreq(mem_depth, 1/sa_rate)
dfreqs = freqs[1]
f_freq_idx = round(f/dfreqs)
span = 10*f_mod
f_min_idx = int(f_freq_idx - span/(2*dfreqs))
f_max_idx = int(f_freq_idx + span/(2*dfreqs))
psd_v_spec = 20*log10(2*sqrt(2)*abs(v_spec)/(len(v)*0.2236))
plot(freqs[f_min_idx:f_max_idx],psd_v_spec[f_min_idx:f_max_idx])
f_c_idx = round(f_c/dfreqs)
annotate("{:.2f}".format(psd_v_spec[f_c_idx]),(freqs[f_c_idx],psd_v_spec[f_c_idx]))
f_m_idx = round((f_c+f_m)/dfreqs)
annotate("{:.2f}".format(psd_v_spec[f_m_idx]),(freqs[f_m_idx],psd_v_spec[f_m_idx]))
grid(1)
# First let's look at the spectrum of the carrier with no modulation
beta = 0 # Phase Modulation, PM
f_m = 1e6
plot_wfm_spectrum(t,dt,f_c,beta,f_m)
grid(1)
# Let's add some sinusoidal Phase Modulation @ 1MHz
f_m = 1e6
figure(figsize = (16,5))
subplot(1,3,1)
beta = 0.0001
title(r"$\beta$ = %.4f" %beta)
plot_wfm_spectrum(t,dt,f_c,beta,f_m)
subplot(1,3,2)
beta = 0.001
title(r"$\beta$ = %.4f" %beta)
plot_wfm_spectrum(t,dt,f_c,beta,f_m)
subplot(1,3,3)
beta = 0.01
title(r"$\beta$ = %.4f" %beta)
plot_wfm_spectrum(t,dt,f_c,beta,f_m)
Note that at first the modulation sideband at +/- 1MHz increases linearly (+20dB for a 10x step) with increase in the modulation amplitude. Eventually other sidebands appear at +/- N MHz.
# Let's compare a measurement at large modulation amplitude with a measurement from a scope
f_m = 1e6
beta = 0.1
figure(figsize = (16,5))
subplot(1,2,1)
title(r"$\beta$ = %.4f" %beta)
plot_wfm_spectrum(t,dt,f_c,beta,f_m)
#from visa import *
#scope = instrument("TCPIP0::130.30.240.189::inst0::INSTR")
#scope.write(":wav:sour func2")
#wfm_ascii = scope.ask(":wav:data?")
#wfm = wfm_ascii[:-1].split(',')
#wfm_xor = float(scope.ask(":wav:xor?"))
#wfm_dx = float(scope.ask(":wav:xinc?"))
#wfm_freqs = linspace(wfm_xor, len(wfm)*wfm_dx, len(wfm))
#subplot(1,2,2)
#title("PSD from Scope FFT - PM Amplitude = %.4f" %pm_amp)
#plot(wfm_freqs, wfm)
#grid(1)
Note that the first modulation sideband is down ~26dB from the carrier.
It can be shown that our signal:
$$sin(\omega_c t + \beta cos(\omega_\phi t))$$beta_marker = 0.1
from scipy.special import j0,j1
beta=linspace(0,10,10000)
dbeta = beta[1]
y0=j0(beta)
y1=j1(beta)
figure(figsize=(14,6))
subplot(1,2,1)
plot(beta,y0,label="$J_0$")
plot(beta,y1,label="$J_1$")
legend()
xlabel(r"$\beta$")
grid(1)
subplot(1,2,2)
loglog(beta,y0,label="$J_0$")
loglog(beta,y1,label="$J_1$")
beta_marker_idx = beta_marker/dbeta
annotate("{:.2f}".format(y0[beta_marker_idx]),(beta_marker,y0[beta_marker_idx]))
annotate("{:.2f}".format(y1[beta_marker_idx]),(beta_marker,y1[beta_marker_idx]))
print y1[beta_marker]
legend()
ylim(ymin=1e-3)
xlabel(r"$\beta$")
grid(which='both')
0.0
What does this tell us?
What we are looking at in both of these plots is the power spectral density PSD of the signal, $S(f)$.
When a Spectrum Analyzer makes a Phase Noise measurement it starts with this $S(f)$ data.
If there is no Amplitude Modulation of the signal we can call say $S(f) = S_\phi(f)$, the Frequency Spectral Density.
To get to from $S_\phi(f)$ to $L(f_\phi)$ we need to:
This is the NIST definition of Phase Noise.
Another approach is to first de-modulate the phase from the signal and then calculate the Power Spectral Density of the phase, $S_\phi(f_\phi)$
An advantage of this approach is that it is more immune to amplitude modulation on the signal.
The Signal Source Analyzer and dedicated Phase Noise test systems use this approach with a hardware phase demodulater.
However a real-time oscilloscope can also demodulate phase using a software clock recovery algorithm to find the phase deviation, ph(t).
If we then take the Fourier Transform of this we get the PSD of the phase modulation, $S_\phi(f_\phi)$
Translation of Bessel Functions
$$y(t) = sin(\omega_c t + \beta cos(\omega_\phi t))$$From:
$$sin(A + B) = sin(A)cos(B) + cos(a)sin(B)$$
$A = \omega_c t$; $B = \beta cos(\omega_\phi t)$
$y(t) = sin(\omega_c t)cos(\beta cos(\omega_\phi t)) + cos(\omega_c t)sin(\beta cos(\omega_\phi t))$
From:
$sin(A)cos(B) = 1/2(sin(A-B)+sin(A+B))$
$y(t) = 1/2(sin(\omega_c t - \beta cos(\omega_\phi t)) + sin(\omega_c t + \beta cos(\omega_\phi t))) + 1/2(sin(\beta cos(\omega_\phi t)-\omega_c t) + sin(\beta cos(\omega_\phi t)+\omega_c t))$