2. TCLab in the frequency domain

[1]:
import numpy
[2]:
%matplotlib inline
[3]:
tau_p = 150
K_p = 0.33
theta = 15
[4]:
omega = numpy.logspace(-3, -2)
s = omega*1j
[5]:
G = K_p/(tau_p*s + 1)*numpy.exp(-theta*s)
[6]:
import matplotlib.pyplot as plt

Let’s choose 5 logarithmically spaced points around the corner frequency

[7]:
freqs = numpy.logspace(-2.8, -2, 5)
[8]:
def plotfreqs(ax):
    for freq in freqs:
        ax.axvline(freq)
[9]:
def bode(omega, G, gainax=None, phaseax=None, phasecorr=0):
    if gainax is None:
        fig, (gainax, phaseax) = plt.subplots(2, 1, sharex=True)
    gainax.loglog(omega, numpy.abs(G))
    angle = numpy.angle(G)
    phaseax.semilogx(omega, numpy.unwrap(angle) + phasecorr)
    return gainax, phaseax
[10]:
gainax, phaseax = bode(omega, G)
plotfreqs(gainax)
../_images/tclab_Frequency_domain_11_0.png

2.1. Direct frequency domain tests

[11]:
Qbar = 50
deltaQ = A = 10

How long does one sine wave take to repeat?

\[P = 2\pi/\omega\]
[12]:
P = 2*numpy.pi/freqs

We’ll do a couple of repeats.

[13]:
nperiods = 3
[14]:
switch_times = numpy.concatenate([[0], numpy.cumsum(P*nperiods)])
[15]:
switch_times
[15]:
array([    0.        , 11893.26574888, 19397.409123  , 24132.20349893,
       27119.65678502, 29004.61237718])
[16]:
from tclab import runexperiment, labtime
[17]:
def send_sine_wave(t, lab):
    print(f'\rTime: {t} Last sleep: {labtime.lastsleep} ', end='')
    step = numpy.max(numpy.nonzero(switch_times <= t))
    lab.Q1(Qbar + A*numpy.sin(freqs[step]*(t - switch_times[step])))
[18]:
totaltime = sum(nperiods*P)
totaltime
[18]:
29004.612377178186

Experiment will take this many hours

[19]:
totaltime/60/60
[19]:
8.056836771438386

Looks like we’ll be running it overnight.

[57]:
%%time
experiment = runexperiment(send_sine_wave, connected=True,
                           plot=False, twindow=1000,
                           time=int(totaltime),
                           speedup=1,
                           dbfile='sinetest.db')
TCLab version 0.4.6dev
NHduino connected on port /dev/cu.wchusbserial1410 at 115200 baud.
TCLab Firmware 1.3.0 Arduino Uno.
Time: 4872.0 Last sleep: 0.8694100379943848 TCLab disconnected successfully.
CPU times: user 15.5 s, sys: 28 s, total: 43.6 s
Wall time: 1h 21min 16s
[37]:
import tclab
[38]:
h = tclab.Historian((('Q1', lambda: [0, 0, 0, 0]),
                     ('Q2', None),
                     ('T1', None),
                     ('T2', None)), dbfile='sinetest.db')
[39]:
# h = experiment.historian
[40]:
h.get_sessions()
[40]:
[(2, '2018-03-06 18:45:27', 13710),
 (12, '2018-03-07 14:55:43', 2001),
 (15, '2018-03-07 18:43:39', 7526),
 (25, '2018-03-08 05:34:09', 5523),
 (27, '2018-03-08 07:10:23', 4873),
 (28, '2018-03-08 12:59:29', 55),
 (29, '2018-03-08 13:00:31', 116),
 (30, '2018-03-08 13:02:35', 1001),
 (31, '2018-03-08 13:25:17', 2001),
 (32, '2018-03-08 14:30:19', 0),
 (33, '2018-03-08 14:30:32', 891),
 (34, '2018-03-08 14:46:08', 536),
 (35, '2018-03-08 14:55:18', 132),
 (36, '2018-03-08 15:02:28', 2001),
 (37, '2018-03-09 04:37:56', 0),
 (38, '2018-03-09 04:39:17', 0),
 (39, '2018-03-09 04:45:49', 0),
 (40, '2018-03-09 04:46:44', 0),
 (41, '2018-03-09 08:22:48', 0),
 (42, '2018-03-09 08:23:20', 0)]
[41]:
import pandas
[42]:
%matplotlib inline
[43]:
# h = experiment.historian
[44]:
sine_sessions = [2, 15, 25, 27]
[45]:
def sinefit(session, freq, Tbar=40, inphase=0, gain=0.4, phase=0):
    h.load_session(session)
    t = numpy.array(h.t)
    Q1 = numpy.array(h.logdict['Q1'])
    Q1_sine = A*numpy.sin(t*freq + inphase)
    T1 = numpy.array(h.logdict['T1'])
    T1_sine = A*gain*numpy.sin(t*freq + inphase + phase)
    plt.plot(t, Q1 - Qbar, color='blue', alpha=0.4)
    plt.plot(t, Q1_sine, color='blue')
    plt.plot(t, T1 - Tbar, color='red', alpha=0.4)
    plt.plot(t, T1_sine, color='red')
    plt.ylim(-A, A)
    print(f'Gain={gain}, Phase={phase-inphase}')
[46]:
sinefit(2, freqs[0], 40, 0, 0.3, 0, )
Gain=0.3, Phase=0
../_images/tclab_Frequency_domain_36_1.png
[47]:
from ipywidgets import interact
[48]:
interact(sinefit,
         session=sine_sessions,
         freq=freqs,
         Tbar=(30., 50.),
         inphase=(0, 2*numpy.pi),
         gain=(0., 1., 0.01),
         phase=(-2*numpy.pi, 0),)
[48]:
<function __main__.sinefit>
[124]:
gains = [0.3, 0.32, 0.25, 0.24, 0.19]
phases = [-0.28, -0.38, -0.58, -0.88, -1.28]
[158]:
gainax, phaseax = bode(omega, G)
gainax.scatter(freqs, gains, color='red')
phaseax.scatter(freqs, phases, color='red')
[158]:
<matplotlib.collections.PathCollection at 0x11a430ba8>
../_images/tclab_Frequency_domain_40_1.png

2.2. FFT based bode diagram

[20]:
next_values = {'time': 0, 'value': +A}
def random_noise(t, lab):
    if t > next_values['time']:
        next_values['time'] += numpy.random.uniform(50, 300)
        next_values['value'] = -A if next_values['value'] == +A else +A
    lab.Q1(Qbar + next_values['value'])
[21]:
next_values
[21]:
{'time': 0, 'value': 10}
[22]:
%matplotlib notebook
[23]:
%%time
experiment = runexperiment(random_noise, connected=True,
                           plot=True, twindow=2000,
                           time=8*60*60,
                           speedup=1,
                           dbfile='sinetest.db',
                          )
TCLab version 0.4.6dev
NHduino connected on port /dev/cu.wchusbserial1410 at 115200 baud.
TCLab Firmware 1.3.0 Arduino Uno.
TCLab disconnected successfully.
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<timed exec> in <module>()

~/Documents/Development/TCLab/tclab/experiment.py in runexperiment(function, connected, plot, twindow, time, dbfile, speedup, synced)
     99     """
    100     with Experiment(connected, plot, twindow, time, dbfile, speedup, synced) as experiment:
--> 101         for t in experiment.clock():
    102             function(t, experiment.lab)
    103     return experiment

~/Documents/Development/TCLab/tclab/experiment.py in clock(self)
     78         else:
     79             times = range(self.time)
---> 80         for t in times:
     81             yield t
     82             if self.plot:

~/Documents/Development/TCLab/tclab/labtime.py in clock(period, step, tol, adaptive)
     98                            'Step size was {} s, but {:.2f} s elapsed '
     99                            '({:.2f} too long). Consider increasing step.')
--> 100                 raise RuntimeError(message.format(step, elapsed, elapsed-step))
    101         labtime.sleep(step - (labtime.time() - start) % step)
    102         now = labtime.time() - start

RuntimeError: Labtime clock lost synchronization with real time. Step size was 1 s, but 3.22 s elapsed (2.22 too long). Consider increasing step.
[181]:
h = experiment.historian
[30]:
h.load_session(36)
[31]:
%matplotlib inline
[32]:
plt.plot(h.t, h.logdict['T1'])
[32]:
[<matplotlib.lines.Line2D at 0x118ee7a20>]
../_images/tclab_Frequency_domain_49_1.png
[33]:
import numpy
[72]:
startcut = 900
[73]:
Q1 = numpy.array(h.logdict['Q1'][startcut:])
T1 = numpy.array(h.logdict['T1'][startcut:])
[74]:
plt.plot(Q1)
plt.plot(T1)
[74]:
[<matplotlib.lines.Line2D at 0x1176f1278>]
../_images/tclab_Frequency_domain_53_1.png
[75]:
u = numpy.fft.rfft(Q1 - Qbar)
y = numpy.fft.rfft(T1 - numpy.mean(T1))
[76]:
omegafft = numpy.fft.rfftfreq(len(Q1), 1)
Gfft = y/u
[160]:
ga, pa = bode(omega, G)
bode(omegafft, Gfft, ga, pa, numpy.pi/4)
plt.xlim(1e-3, 1e-2)
ga.set_ylim(0.01, 1)
pa.set_ylim(-numpy.pi/2, 0)
[160]:
(-1.5707963267948966, 0)
../_images/tclab_Frequency_domain_56_1.png
[ ]:

[ ]: