### Note:  the following code can provide a useful starting point for the described generic calculations
###        by cutting-and-pasting into larger Python scripts and then modifying as necessary

## calculate integrated jitter based on phase noise S_out_tot
i_start = argwhere(f >= fstart)[0]
i_end = argwhere(f >= fend)[0]

integ_pn = 0.0
for i in xrange((i_start+1),i_end+1):
integ_pn = integ_pn + (S_out_tot[i] + S_out_tot[i-1])*(f[i] - f[i-1])
integ_jitter = sqrt(integ_pn)/(2*pi*fvco)


##### Calculate Open Loop Magnitude and Phase, Unity Gain Crossover, and Phase Margin based on open loop transfer A
ind_vals = np.arange( len(A) )[abs(A) <= 1]
f_crossover = f[ind_vals[0]]
angle_crossover = 180/pi*np.angle(A[ind_vals[0]])
# print("crossover frequency = %5.1f kHz, phase margin = %5.1f degrees" % (f_crossover*1e-3, 180+angle_crossover))

fig = figure(2,figsize=(14,8))
fig.clf()
rcParams['lines.linewidth'] = 2

subplot(211)
semilogx(f,20*log10(abs(A)),'m')
title('Magnitude of Open Loop Response (Unity Gain Crossover = %5.1f kHz)' % (f_crossover*1e-3));
ylabel('dB')
grid(True,which='both')

subplot(212)
semilogx(f,180/pi*np.unwrap(np.angle(A)),'m')
title('Phase of Open Loop Response (Phase Margin = %5.1f Degrees)' % (180+angle_crossover));
ylabel('Degrees')
xlabel('Frequency (Hz)')
grid(True,which='both')
show()

###### Grab CppSim data and plot phase noise
t_start=1e-3
t_end=20e-3
data = CppSimData('test.tr0')
vin_raw = data.evalsig('vin')
t_raw = data.evalsig('TIME')
i_start = argwhere(t_raw >= t_start)[0]
i_end = argwhere(t_raw <= t_end)[-1]
t = t_raw[i_start:i_end]
vin = vin_raw[i_start:i_end]
t_deriv = lfilter([1,-1],[1],t)
Ts = mean(t_deriv[1:])
# print("1/Ts = %5.3e" % (1/Ts))

f_sim,Pxx_db = calc_pll_phasenoise(vin,Ts)
semilogx(f_sim,Pxx_db,'b');

##### logspace usage
f = logspace(log10(1e3),log10(20e6),100e3)


#### inclusion of flicker noise
noise_buf_vco = -160 ## dBc/Hz
fcorner_buf_vco = 1e6
S_n_buf_vco = 10**(noise_buf_vco/10)*abs(1 + fcorner_buf_vco/f)
