Prototype SQUID practicum software.md 10.10 KiB
jupyter:
jupytext:
formats: ipynb,md
text_representation:
extension: .md
format_name: markdown
format_version: '1.2'
jupytext_version: 1.3.0
kernelspec:
display_name: Python 3
language: python
name: python3
Import Libraries
import sys
import redpitaya_scpi as scpi
import numpy as np
import time
import matplotlib.pyplot as plt
import IPython
import ipywidgets as widgets
from bokeh.plotting import figure, show
from bokeh.io import output_notebook, push_notebook
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Toggle, Range1d
from datetime import datetime
Configure instruments and libraries
dec_exp = np.array([0,3,6,10,13,16])
dec = 2**dec_exp
dec
# Red Pitaya Stuff
address = "rp-f06897.local"
rp = scpi.scpi(address)
dec_exp = np.array([0,3,6,10,13,16])
dec = 2**dec_exp
t_total = np.array([131.072e-6, 1.049e-3, 8.389e-3, 134.218e-3,1.074, 8.590])
# For the gui updates during the live view loops
ipython = IPython.get_ipython()
# For Bokeh
output_notebook()
Function for reading IV data
MrSQUID hardware conversion factors:
- Y-axis: 10000 V/V (1V on Red Pitaya = 100 uV across SQUID)
- X-axis: 10000 V/A (1V on Red Pitaya = 100 uA through SQUID)
Max ranges:
- Current monitor: +/- 2.2V = +/- 220 uA?
- Voltage readout: +/- 8.8V = +/- 880 uV?
To do:
- Use timestamps to figure out where we are losing time
- See if switching to binary read, for example, would make this function run faster
Notes:
With timebase = -3
, the trace is 134 microseconds long or so. But triggering and acquiring the trace with the code below takes 1.8 seconds to bring the 134 microsecond trace into python! This is a very slow update rate.
def get_IV_t(timebase=-3):
tn = [time.time()]
# Configure timebase and input range settings
rp.tx_txt("ACQ:DEC %d" % dec[timebase])
#print("setting dec to %d " % dec[timebase])
#print("t_total is %f" % t_total[timebase])
rp.tx_txt('ACQ:SOUR1:GAIN HV')
rp.tx_txt('ACQ:SOUR2:GAIN HV')
# Start the acquisition
rp.tx_txt('ACQ:START')
time.sleep(t_total[timebase]+0.1) # to avoid getting old data in the buffer
rp.tx_txt('ACQ:TRIG NOW')
tn.append(time.time())
# Wait until it's done
while 1:
rp.tx_txt('ACQ:TRIG:STAT?')
if rp.rx_txt() == 'TD':
break
tn.append(time.time())
# Download the traces
rp.tx_txt('ACQ:SOUR1:DATA?')
buff_string = rp.rx_txt()
buff_string = buff_string.strip('{}\n\r').replace(" ", "").split(',')
I = np.array(list(map(float, buff_string))) / 1e4
tn.append(time.time())
rp.tx_txt('ACQ:SOUR2:DATA?')
buff_string = rp.rx_txt()
buff_string = buff_string.strip('{}\n\r').replace(" ", "").split(',')
V = np.array(list(map(float, buff_string))) / 1e4
tn.append(time.time())
# Create the appropriate time array
t = np.linspace(0,t_total[timebase],len(I))
if False:
print("Total time: %f" % (tn[-1]-tn[0]))
print("Splits:")
for i in range(len(tn)-1):
print("%f" % (tn[i+1]-tn[i]))
# Return the traces
return I,V,t
IV vs Time Live Display
# Shit, spent about 2 hours on this! We need to create the ranges first and pass them to the
# figure initialisation if we want to be able to change them...
I = []
V = []
t = []
def update_display():
global I,V,t
I,V,t = get_IV_t()
I *= 1e6
V *= 1e6
I_avg = np.average(I)
I_ptp = np.max(I) - np.min(I)
p1.title.text = "Current Average = %.2f uA Peak-to-Peak %.2f uA" % (I_avg, I_ptp)
V_avg = np.average(V)
V_ptp = np.max(V) - np.min(V)
p2.title.text = "Voltage Average = %.2f uV Peak-to-Peak %.2f uV" % (V_avg, V_ptp)
R_ptp.value = "%.2f" % (V_ptp / I_ptp)
R_avg.value = "%.2f" % (V_avg / I_avg)
source1.data = dict(x=t, y=I)
source2.data = dict(x=t, y=V)
if scale_mode.value == "Autoscale":
p1.y_range.start = np.min(I)-I_ptp*0.1
p1.y_range.end = np.max(I)+I_ptp*0.1
p2.y_range.start = np.min(V)-V_ptp*0.1
p2.y_range.end = np.max(V)+V_ptp*0.1
elif scale_mode.value == "Full range":
p1.y_range.start = -p1_full_scale
p1.y_range.end = p1_full_scale
p2.y_range.start = -p2_full_scale
p2.y_range.end = p2_full_scale
range1 = Range1d()
range2 = Range1d()
source1 = ColumnDataSource()
source2 = ColumnDataSource()
p1 = figure(title="Current", plot_height=300, plot_width=900,toolbar_location=None, y_range = range1)
p1.yaxis.axis_label = 'Curret (uA)'
p2 = figure(title="Voltage", plot_height=300, plot_width=900,toolbar_location=None, y_range = range2)
p2.xaxis.axis_label = 'Time (s)'
p2.yaxis.axis_label = 'Voltage (uV)'
p1.line('x', 'y', source=source1)
p2.line('x', 'y', source=source2)
p1_full_scale = 240 #uA
p2_full_scale = 800 # uV
style = {'description_width': 'initial'}
stop_button = widgets.ToggleButton(description='Stop')
pause_button = widgets.ToggleButton(description='Pause')
def save_data(w):
fmt = f"mrsquid_{mrsquid_mode.value}_%Y-%m-%d-%H_%M_%S.dat"
outname = datetime.now().strftime(fmt)
np.savetxt(outname, np.array([t,I,V]).T)
filename.value = outname
save_button = widgets.Button(description='Save data')
save_button.on_click(save_data)
filename = widgets.HTML(description="Last filename: ",style=style)
filename.value = "(none)"
scale_mode = widgets.RadioButtons(options=['Autoscale', 'Full range', 'Freeze range'])
mrsquid_mode = widgets.RadioButtons(options=['V-I', 'V-Phi'])
R_ptp = widgets.Text(description="V/I ptp (ohms)",style=style)
R_avg = widgets.Text(description="V/I avg (ohms)",style=style)
R_ptp.layout.width = '150px'
R_avg.layout.width = '150px'
update_display()
target = show(column(p1,p2), notebook_handle=True)
rows = []
rows.append(widgets.HBox([stop_button,pause_button,scale_mode, mrsquid_mode]))
rows.append(widgets.HBox([save_button, filename]))
rows.append(widgets.HBox([R_ptp, R_avg]))
display(widgets.VBox(rows))
while True:
ipython.kernel.do_one_iteration()
if not pause_button.value:
update_display()
if stop_button.value:
break
push_notebook(handle=target)
print("Live view done")
IV Live Display
def update_display():
ipython.kernel.do_one_iteration()
I,V,t = get_IV_t()
ipython.kernel.do_one_iteration()
I *= 1e6
V *= 1e6
source1.data = dict(x=I, y=V)
# Update widgets
I_avg = np.average(I)
I_ptp = np.max(I) - np.min(I)
V_avg = np.average(V)
V_ptp = np.max(V) - np.min(V)
widgets = (I_avg_w, V_avg_w, R_avg_w, I_ptp_w, V_ptp_w, R_ptp_w)
vals = (I_avg, V_avg, V_avg / I_avg, I_ptp, V_ptp, V_ptp / I_ptp)
for w,v in zip(widgets,vals):
w.value = "%.2f" % v
# Update scaling
if scale_mode.value == "Autoscale":
p1.x_range.start = np.min(I)-I_ptp*0.1
p1.x_range.end = np.max(I)+I_ptp*0.1
p1.y_range.start = np.min(V)-V_ptp*0.1
p1.y_range.end = np.max(V)+V_ptp*0.1
elif scale_mode.value == "Full range":
p1.x_range.start = -x_full_scale
p1.x_range.end = x_full_scale
p1.y_range.start = -y_full_scale
p1.y_range.end = y_full_scale
x = np.array([np.min(I), np.max(I)])
R = line_slope.value
y = x*R
source2.data = dict(x=x, y=y)
if plot_line_button.value:
slope_line.visible = True
else:
slope_line.visible = False
range1 = Range1d()
range2 = Range1d()
source1 = ColumnDataSource()
source2 = ColumnDataSource()
p1 = figure(title="IV", plot_height=500, plot_width=600,toolbar_location=None, x_range = range1, y_range = range2)
p1.yaxis.axis_label = 'Measured Voltage (uV)'
p1.xaxis.axis_label = 'Applied Current (uA)'
p1.line('x', 'y', source=source1)
slope_line = p1.line('x','y',source=source2, color='red')
slope_line.visible = False
x_full_scale = 240 #uA
y_full_scale = 800 # uV
stop_button = widgets.ToggleButton(description='Stop')
pause_button = widgets.ToggleButton(description='Pause')
scale_mode = widgets.RadioButtons(options=['Autoscale', 'Full range', 'Freeze range'])
mrsquid_mode = widgets.RadioButtons(options=['V-I', 'V-Phi'])
style = {'description_width': 'initial'}
I_avg_w = widgets.Text(description="I_avg (uA)",style=style)
V_avg_w = widgets.Text(description="V_avg (uV)",style=style)
I_ptp_w = widgets.Text(description="I_ptp (uA)",style=style)
V_ptp_w = widgets.Text(description="V_ptp (uV)",style=style)
R_ptp_w = widgets.Text(description="V/I ptp (ohms)",style=style)
R_avg_w = widgets.Text(description="V/I avg (ohms)",style=style)
plot_line_button = widgets.ToggleButton(description="Plot line")
line_slope = widgets.FloatText(description="R_line (ohms)")
line_slope.value = 50
copy_slope_button = widgets.Button(description="Copy PTP slope")
def copy_slope(w):
line_slope.value = float(R_ptp_w.value)
copy_slope_button.on_click(copy_slope)
for w in (I_avg_w, V_avg_w, I_ptp_w, V_ptp_w, R_ptp_w, R_avg_w):
w.layout.width = '150px'
def save_data(w):
fmt = f"mrsquid_{mrsquid_mode.value}_%Y-%m-%d-%H_%M_%S.dat"
outname = datetime.now().strftime(fmt)
np.savetxt(outname, np.array([t,I,V]).T)
filename.value = outname
save_button = widgets.Button(description='Save data')
save_button.on_click(save_data)
filename = widgets.HTML(description="Last filename: ",style=style)
filename.value = "(none)"
update_display()
target = show(p1, notebook_handle=True)
rows = []
rows.append(widgets.HBox([I_avg_w, V_avg_w, R_avg_w]))
rows.append(widgets.HBox([I_ptp_w, V_ptp_w, R_ptp_w]))
rows.append(widgets.HBox([plot_line_button, line_slope, copy_slope_button]))
rows.append(widgets.HBox([save_button, filename]))
rows.append(widgets.HBox([stop_button,pause_button,scale_mode,mrsquid_mode]))
display(widgets.VBox(rows))
while True:
ipython.kernel.do_one_iteration()
if not pause_button.value:
update_display()
if stop_button.value:
break
push_notebook(handle=target)
print("Live view stopped")