Skip to content
Snippets Groups Projects
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")