version as of 2025-01-09 from ultrasound PC

This commit is contained in:
Ultrasound PC 2025-01-09 09:59:33 +01:00
parent efca358c72
commit a1fa9f1cbb
12 changed files with 757 additions and 306 deletions

126
cfg/PEUS.cfg Normal file
View File

@ -0,0 +1,126 @@
[NODE]
id = ultrasound.psi.ch
description = ultrasound settings
[INTERFACE]
uri = tcp://5000
[f]
class = secop_psi.ultrasound.Frequency
description = ultrasound frequency and acquisition loop
uri = serial:///dev/ttyS1
pars = pars
pollinterval = 0.1
# this is the start time:
time = 900
size = 5000
freq = 1.17568e+06
basefreq = 4.14902e+07
control = True
amp = 5.0
nr = 1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #500
sr = 32768 #16384
plot = True
maxstep = 100000
bw = 10E6 #butter worth filter bandwidth
# y scale for plot:
maxy = 0.7
# module to transmit curves:
curves = curves
[curves]
class = secop_psi.ultrasound.Curves
description = t, I, Q and pulse arrays for plot
[roi0]
class = secop_psi.ultrasound.Roi
description = I/Q of region in the control loop.
# this is the center of roi:
time = 2450
size = 300
main = f
[roi1]
class = secop_psi.ultrasound.Roi
description = I/Q of region 1
time = 5950
size = 300
main = f
[roi2]
class = secop_psi.ultrasound.Roi
description = I/Q of region 2
# enable = False
time = 9475
size = 300
main = f
[roi3]
class = secop_psi.ultrasound.Roi
description = I/Q of region 3
#enable = False
time = 12900
size = 300
main = f
[roi4]
class = secop_psi.ultrasound.Roi
description = I/Q of region 4
enable = True
time = 16100
size = 300
main = f
[roi5]
class = secop_psi.ultrasound.Roi
description = I/Q of region 5
enable = False
time = 4000
size = 30
main = f
[roi6]
class = secop_psi.ultrasound.Roi
description = I/Q of region 6
enable = False
time = 4000
size = 200
main = f
[roi7]
class = secop_psi.ultrasound.Roi
description = I/Q of region 7
enable = False
time = 4000
size = 200
main = f
[roi8]
class = secop_psi.ultrasound.Roi
description = I/Q of region 8
enable = False
time = 4000
size = 200
main = f
[roi9]
class = secop_psi.ultrasound.Roi
description = I/Q of region 9
enable = False
time = 4000
size = 200
main = f
[delay]
class = secop_psi.dg645.Delay
description = delay line with 2 channels
#uri = dil4-ts.psi.ch:3008
uri = serial:///dev/ttyS2
on1 = 1e-9
on2 = 1E-9
off1 = 400e-9
off2 = 600e-9
[pars]
class = secop_psi.ultrasound.Pars
description = SEA parameters

63
cfg/RUS.cfg Normal file
View File

@ -0,0 +1,63 @@
[NODE]
id = ultrasound.psi.ch
description = resonant ultra sound setup
[INTERFACE]
uri = tcp://5000
[f]
class = secop_psi.ultrasound.Frequency
description = ultrasound frequency and acquisition loop
uri = serial:///dev/ttyS1
pars = pars
pollinterval = 0.1
# this is the start time:
time = 900
size = 5000
freq = 1.e+03
basefreq = 1.E+3
control = False
rusmode = False
amp = 2.5
nr = 1 #500 #300 #100 #50 #30 #10 #5 #3 #1 #1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #500
sr = 1E8 #16384
plot = True
maxstep = 100000
bw = 10E6 #butter worth filter bandwidth
# y scale for plot:
maxy = 0.7
# module to transmit curves:
curves = curves
[curves]
class = secop_psi.ultrasound.Curves
description = t, I, Q and pulse arrays for plot
[roi0]
class = secop_psi.ultrasound.Roi
description = I/Q of region in the control loop.
# this is the center of roi:
time = 300
size = 5000
main = f
[roi1]
class = secop_psi.ultrasound.Roi
description = I/Q of region 1
time = 100
size = 300
main = f
[delay]
class = secop_psi.dg645.Delay
description = delay line with 2 channels
#uri = dil4-ts.psi.ch:3008
uri = serial:///dev/ttyS2
on1 = 1e-9
on2 = 1E-9
off1 = 400e-9
off2 = 600e-9
[pars]
class = secop_psi.ultrasound.Pars
description = SEA parameters

17
cfg/dg.cfg Normal file
View File

@ -0,0 +1,17 @@
[node ultrasound.psi.ch]
description = ultrasound settings
[interface tcp]
type = tcp
bindto = 0.0.0.0
bindport = 5000
[module delay]
class = secop_psi.dg645.Delay
description = delay line with 2 channels
#uri = dil4-ts.psi.ch:3008
uri = serial:///dev/ttyS2
on1 = 10e-9
on2 = 1E-9
off1 = 250e-9
off2 = 550e-9

126
cfg/ultrasound.cfg Normal file
View File

@ -0,0 +1,126 @@
[NODE]
id = ultrasound.psi.ch
description = ultrasound settings
[INTERFACE]
uri = tcp://5000
[f]
class = secop_psi.ultrasound.Frequency
description = ultrasound frequency and acquisition loop
uri = serial:///dev/ttyS1
pars = pars
pollinterval = 0.1
# this is the start time:
time = 900
size = 5000
freq = 1.17568e+06
basefreq = 4.14902e+07
control = True
amp = 5.0
nr = 1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #1000 #500 #300 #100 #50 #30 #10 #5 #3 #1 #500
sr = 32768 #16384
plot = True
maxstep = 100000
bw = 10E6 #butter worth filter bandwidth
# y scale for plot:
maxy = 0.7
# module to transmit curves:
curves = curves
[curves]
class = secop_psi.ultrasound.Curves
description = t, I, Q and pulse arrays for plot
[roi0]
class = secop_psi.ultrasound.Roi
description = I/Q of region in the control loop.
# this is the center of roi:
time = 2450
size = 300
main = f
[roi1]
class = secop_psi.ultrasound.Roi
description = I/Q of region 1
time = 5950
size = 300
main = f
[roi2]
class = secop_psi.ultrasound.Roi
description = I/Q of region 2
# enable = False
time = 9475
size = 300
main = f
[roi3]
class = secop_psi.ultrasound.Roi
description = I/Q of region 3
#enable = False
time = 12900
size = 300
main = f
[roi4]
class = secop_psi.ultrasound.Roi
description = I/Q of region 4
enable = True
time = 16100
size = 300
main = f
[roi5]
class = secop_psi.ultrasound.Roi
description = I/Q of region 5
enable = False
time = 4000
size = 30
main = f
[roi6]
class = secop_psi.ultrasound.Roi
description = I/Q of region 6
enable = False
time = 4000
size = 200
main = f
[roi7]
class = secop_psi.ultrasound.Roi
description = I/Q of region 7
enable = False
time = 4000
size = 200
main = f
[roi8]
class = secop_psi.ultrasound.Roi
description = I/Q of region 8
enable = False
time = 4000
size = 200
main = f
[roi9]
class = secop_psi.ultrasound.Roi
description = I/Q of region 9
enable = False
time = 4000
size = 200
main = f
[delay]
class = secop_psi.dg645.Delay
description = delay line with 2 channels
#uri = dil4-ts.psi.ch:3008
uri = serial:///dev/ttyS2
on1 = 1e-9
on2 = 1E-9
off1 = 400e-9
off2 = 600e-9
[pars]
class = secop_psi.ultrasound.Pars
description = SEA parameters

127
ftune.py Normal file
View File

@ -0,0 +1,127 @@
import PySimpleGUI as sg
import time
import sys
sys.path.append(r'/Users/bartkowiak/Programs/frappy/frappy')
from secop.client import SecopClient
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
from numpy.random import rand
import numpy as np
def draw_figure(canvas, figure):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg
def upd_flim(inp):
return int(inp)
def connection_handler(sec,status,IP,port, cstate):
if cstate == -1 and status == False:
try :
cstate = 1
#sock.connect((IP,int(port)))
sec = SecopClient(IP+':'+port)
sec.connect()
sec.setParameter('f','control',False)
status = False
except :
cstate = -1
status = True
elif status == True and cstate == 1 :
status = False
sec.disconnect()
#sock.close()
cstate = -1
return cstate, status,sec
def new_xy(sec, cstate):
xn = 0
yn = 0
if cstate == 1:
try:
xn = sec.getParameter('roi0','i')[0]
yn = sec.getParameter('roi0','q')[0]
except:
pass
return xn, yn
def send_new_f(sec, cstate, freq):
if cstate == 1:
freq = sec.setParameter('f','basefreq',freq)
return freq
# THIS IS THE MAIN LOOP
def main(): # define the main loop
freq = '10000000'
f_start = 39000000
f_end = 42000000
new_f = f_start
down = True
down2 = True
cstate = -1
tstart = time.time()
x = np.array([])
y = np.array([])
IP = 'pc13252'
port = '5000'
sec = SecopClient(IP+':'+port)
layout = [ [sg.Text('Frequency Tuner)')],
[sg.Text('IP\t\t:',font= ("Helvetica",22)),sg.InputText(key='_IP_',default_text=IP,font= ("Helvetica",22),size=(15,1))],
[sg.Text('PORT\t\t:',font= ("Helvetica",22)),sg.InputText(key='_PORT_',default_text=port,font= ("Helvetica",22),size=(15,1)),sg.T(' ' * 41), sg.Button('connect',key='connect',font= ("Helvetica",22))],
[sg.Text('Start Frequency\t:',font= ("Helvetica",22)),sg.InputText(key='_f_start_', default_text='39000000',font= ("Helvetica",22),size=(15,1))],
[sg.Text('Stop Frequency\t:',font= ("Helvetica",22)),sg.InputText(key='_f_end_',default_text='45000000',font= ("Helvetica",22),size=(15,1))],
[sg.Slider(range=(f_start, f_end),key='_SLtune_', orientation='h',font= ("Helvetica",22), size=(35, 20), default_value=f_start),sg.T(' ' * 2),sg.Button('sweep',key='sweep',font= ("Helvetica",22))],
[sg.Canvas(size=(640, 480), key='-CANVAS-')],
[sg.Button('Exit',font= ("Helvetica",22)),sg.T(' ' * 150),sg.Button('Clear Plot',key='clp',font= ("Helvetica",22))] ] # a couple of buttons
window = sg.Window('Frequency tuner', layout, finalize=True)
canvas_elem = window['-CANVAS-']
canvas = canvas_elem.TKCanvas
# draw the intitial scatter plot
fig, ax = plt.subplots()
ax.grid(True)
fig_agg = draw_figure(canvas, fig)
while True:
# Event Loop
event, values = window.Read(timeout=0.5)
if event in (None, 'Exit'): # checks if user wants to exit
if cstate == 1:
connection_handler(sec,True,values['_IP_'],values['_PORT_'],cstate)
break
if event == 'connect':
down = not down
window['connect'].Update(('disconnect','connect')[down])
cstate, down, sec = connection_handler(sec,down,values['_IP_'],values['_PORT_'],cstate)
if event == 'sweep':
down2 = not down2
window['sweep'].Update(('sweeping','sweep')[down2])
#if event in ('_SL8_','_SL6_','_SL4_','_SL2_'): # the two lines of code needed to get button and run command
#freq = update_freq(inp=[values['_SL8_'],values['_SL6_'],values['_SL4_'],values['_SL2_']])
if down2 == False:
if time.time() - tstart > 1.:
tstart = time.time()
new_f = int(int(values['_SLtune_']) + (int(values['_f_end_'])-int(values['_f_start_']))/200)
send_new_f(sec,cstate,new_f)
window['_SLtune_'].Update(value=new_f)
if event == 'clp':
x = np.array([])
y = np.array([])
window['_SLtune_'].Update(range=(int(values['_f_start_']),int(values['_f_end_'])))
xn,yn = new_xy(sec,cstate)
ax.cla()
ax.grid(True)
x = np.append(x,xn)
y = np.append(y,yn)
ax.plot(x, y)
fig_agg.draw()
window.Close()
if __name__ == '__main__':
main()

View File

@ -44,3 +44,4 @@ ERROR = Drivable.Status.ERROR
WARN = Drivable.Status.WARN
BUSY = Drivable.Status.BUSY
IDLE = Drivable.Status.IDLE
DISABLED = Drivable.Status.DISABLED

View File

@ -760,7 +760,7 @@ class ArrayOf(DataType):
def __call__(self, value):
"""validate an external representation to an internal one"""
if isinstance(value, (tuple, list)):
try:
# check number of elements
if self.minlen is not None and len(value) < self.minlen:
raise BadValueError(
@ -768,11 +768,12 @@ class ArrayOf(DataType):
self.minlen)
if self.maxlen is not None and len(value) > self.maxlen:
raise BadValueError(
'Array too big, holds at most %d elements!' % self.minlen)
# apply subtype valiation to all elements and return as list
'Array too big, holds at most %d elements!' % self.maxlen)
# apply subtype valdiation to all elements and return as list
return tuple(self.members(elem) for elem in value)
raise BadValueError(
'Can not convert %s to ArrayOf DataType!' % repr(value))
except TypeError:
raise BadValueError('%s can not be converted to ArrayOf DataType!'
% type(value).__name__) from None
def export_value(self, value):
"""returns a python object fit for serialisation"""
@ -842,6 +843,7 @@ class TupleOf(DataType):
return tuple(sub(elem)
for sub, elem in zip(self.members, value))
except Exception as exc:
print(value)
raise BadValueError('Can not validate:', str(exc)) from None
def export_value(self, value):

View File

@ -47,7 +47,7 @@ class SilentError(CommunicationFailedError):
class HasIO(Module):
"""Mixin for modules using a communicator"""
io = Attached()
io = Attached(mandatory=False)
uri = Property('uri for automatic creation of the attached communication module',
StringType(), default='')

View File

@ -500,10 +500,11 @@ class Module(HasAccessibles):
# TODO: remove readerror 'property' and replace value with exception
pobj = self.parameters[pname]
timestamp = timestamp or time.time()
changed = pobj.value != value
try:
value = pobj.datatype(value)
changed = pobj.value != value
# store the value even in case of error
pobj.value = pobj.datatype(value)
pobj.value = value
except Exception as e:
if isinstance(e, DiscouragedConversion):
if DiscouragedConversion.log_message:

View File

@ -149,6 +149,9 @@ class Parameter(Accessible):
default None: write if given in config''', NoneOr(BoolType()),
export=False, default=None, settable=False)
# used in NICOS only ...
nicos_category = Property(
'''NICOS parameter category''', StringType(), export=True, default='')
# used on the instance copy only
value = None

View File

@ -6,15 +6,17 @@ Created on Tue Nov 26 15:42:43 2019
"""
import sys
import atexit
import signal
import time
import numpy as np
import ctypes as ct
import time
from numpy import sqrt, arctan2, sin, cos
import scipy.signal
#from pylab import *
from scipy import signal
#ADQAPI = ct.cdll.LoadLibrary("ADQAPI.dll")
ADQAPI = ct.cdll.LoadLibrary("libadq.so.0")
@ -37,7 +39,7 @@ ADQ_CHANNELS_MASK = 0x3
def butter_lowpass(cutoff, sr, order=5):
nyq = 0.5 * sr
normal_cutoff = cutoff / nyq
b, a = signal.butter(order, normal_cutoff, btype = 'low', analog = False)
b, a = scipy.signal.butter(order, normal_cutoff, btype = 'low', analog = False)
return b, a
@ -125,6 +127,8 @@ class Adq(object):
# print('SetTriggerThresholdVoltage failed.')
print("CHANNEL:"+str(ct.c_int(ADQAPI.ADQ_GetLvlTrigChannel(self.adq_cu, self.adq_num))))
self.setup_target_buffers()
atexit.register(self.deletecu)
signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
def setup_target_buffers(self):
# Setup target buffers for data
@ -139,9 +143,10 @@ class Adq(object):
ADQAPI.ADQ_MultiRecordClose(self.adq_cu, self.adq_num);
# Delete ADQControlunit
ADQAPI.DeleteADQControlUnit(self.adq_cu)
print('ADQ closed')
def start(self):
"""start datat acquisition"""
"""start data acquisition"""
# samples_per_records = samples_per_record/number_of_records
# Change number of pulses to be acquired acording to how many records are taken
# Start acquisition
@ -177,52 +182,7 @@ class Adq(object):
def acquire(self):
self.start()
return self.getdata()
'''
def average(self, data):
#Average over records
return [data[ch].sum(axis=0) / self.number_of_records for ch in range(2)]
def iq(self, channel, f_LO):
newx = np.linspace(0, self.samples_per_record /2, self.samples_per_record)
s0 = channel /((2**16)/2)*0.5*np.exp(1j*2*np.pi*f_LO/(1e3)*newx)
I0 = s0.real
Q0 = s0.imag
return I0, Q0
def fitting(self, data, f_LO, ti, tf):
# As long as data[0] is the pulse
si = 2*ti #Those are for fitting the pulse
sf = 2*tf
phase = np.zeros(self.number_of_records)
amplitude = np.zeros(self.number_of_records)
offset = np.zeros(self.number_of_records)
for i in range(self.number_of_records):
phase[i], amplitude[i] = sineW(data[0][i][si:sf],f_LO*1e-9,ti,tf)
offset[i] = np.average(data[0][i][si:sf])
return phase, amplitude, offset
def waveIQ(self, channel,ti,f_LO):
#channel is not the sample data
t = np.linspace(0, self.samples_per_record /2, self.samples_per_record + 1)[:-1]
si = 2*ti # Again that is where the wave pulse starts
cwi = np.zeros((self.number_of_records,self.samples_per_record))
cwq = np.zeros((self.number_of_records,self.samples_per_record))
iq = np.zeros((self.number_of_records,self.samples_per_record))
q = np.zeros((self.number_of_records,self.samples_per_record))
for i in range(self.number_of_records):
cwi[i] = np.zeros(self.samples_per_record)
cwq[i] = np.zeros(self.samples_per_record)
cwi[i] = amplitude[i]*sin(t[si:]*f_LO*1e-9*2*np.pi+phase[i]*np.pi/180)+bias[i]
cwq[i] = amplitude[i]*sin(t[si:]*f_LO*1e-9*(2*np.pi+(phase[i]+90)*np.pi/180))+bias[i]
iq[i] = channel[i]*cwi[i]
q[i] = channel[i]*cwq[i]
return iq,q
'''
def sinW(self,sig,freq,ti,tf):
# sig: signal array
# freq
@ -262,9 +222,9 @@ class Adq(object):
def filtro(self, iorq, cutoff):
b, a = butter_lowpass(cutoff, self.samp_freq*1e9)
#ifi = np.array(signal.filtfilt(b,a,iorq[0]))
#qf = np.array(signal.filtfilt(b,a,iorq[1]))
iqf = [signal.filtfilt(b,a,iorq[i]) for i in np.arange(len(iorq))]
#ifi = np.array(scipy.signal.filtfilt(b,a,iorq[0]))
#qf = np.array(scipy.signal.filtfilt(b,a,iorq[1]))
iqf = [scipy.signal.filtfilt(b,a,iorq[i]) for i in np.arange(len(iorq))]
return iqf
@ -276,12 +236,15 @@ class Adq(object):
def gates_and_curves(self, data, freq, pulse, roi):
"""return iq values of rois and prepare plottable curves for iq"""
self.ndecimate = int(round(2E9/freq))
times = []
times.append(('aviq', time.time()))
iq = self.averageiq(data,freq*1e-9,*pulse)
times.append(('filtro', time.time()))
iqf = self.filtro(iq,self.bw_cutoff)
m = len(iqf[0]) // self.ndecimate
ll = m * self.ndecimate
iqf = [iqfx[0:ll] for iqfx in iqf]
times.append(('iqdec', time.time()))
iqd = np.average(np.resize(iqf, (2, m, self.ndecimate)), axis=2)
t_axis = np.arange(m) * self.ndecimate / self.samp_freq

View File

@ -30,7 +30,8 @@ import numpy as np
import secop_psi.iqplot as iqplot
from secop_psi.adq_mr import Adq
from secop.core import Attached, BoolType, Done, FloatRange, HasIO, \
IntRange, Module, Parameter, Readable, StringIO, StringType
IntRange, Module, Parameter, Readable, StringIO, StringType, \
IDLE, DISABLED, TupleOf, ArrayOf
from secop.properties import Property
@ -54,7 +55,6 @@ class Roi(Readable):
time = Parameter('start time', FloatRange(unit='nsec'), readonly=False)
size = Parameter('interval (symmetric around time)', FloatRange(unit='nsec'), readonly=False)
enable = Parameter('calculate this roi', BoolType(), readonly=False, default=True)
#status = Parameter(export=False)
pollinterval = Parameter(export=False)
interval = (0,0)
@ -67,6 +67,9 @@ class Roi(Readable):
def calc_interval(self):
self.interval = (self.time - 0.5 * self.size, self.time + 0.5 * self.size)
def read_status(self):
return (IDLE, '') if self.enable else (DISABLED, 'disabled')
def write_time(self, value):
self.time = value
self.calc_interval()
@ -84,7 +87,7 @@ class Pars(Module):
timestamp = Parameter('unix timestamp', StringType(), default='0', readonly=False)
temperature = Parameter('T', FloatRange(unit='K'), default=0, readonly=False)
mf = Parameter('field', FloatRange(unit='T'), default=0, readonly=False)
sr = Parameter('rotaion angle', FloatRange(unit='deg'), default=0, readonly=False)
sr = Parameter('rotation angle', FloatRange(unit='deg'), default=0, readonly=False)
class FreqStringIO(StringIO):
@ -93,16 +96,19 @@ class FreqStringIO(StringIO):
class Frequency(HasIO, Readable):
pars = Attached()
sr = Property('samples per record', datatype=IntRange(), default=16384)
curves = Attached(mandatory=False)
# sr = Property('samples per record', datatype=IntRange(), default=16384)
maxy = Property('plot y scale', datatype=FloatRange(), default=0.5)
value = Parameter('frequency@I,q', datatype=FloatRange(unit='Hz'), default=0)
basefreq = Parameter('base frequency', FloatRange(unit='Hz'), readonly=False)
nr = Parameter('number of records', datatype=IntRange(1,10000), default=500)
sr = Parameter('samples per record', datatype=IntRange(1,1E9), default=16384)
freq = Parameter('target frequency', FloatRange(unit='Hz'), readonly=False)
bw = Parameter('bandwidth lowpassfilter', datatype=FloatRange(unit='Hz'),default=10E6)
amp = Parameter('amplitude', FloatRange(unit='dBm'), readonly=False)
control = Parameter('control loop on?', BoolType(), readonly=False, default=True)
rusmode = Parameter('RUS mode on?', BoolType(), readonly=False, default=False)
time = Parameter('pulse start time', FloatRange(unit='nsec'),
readonly=False)
size = Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
@ -130,6 +136,7 @@ class Frequency(HasIO, Readable):
self.adq = Adq(self.nr, self.sr, self.bw)
self.roilist = []
self.write_nr(self.nr)
self.write_sr(self.sr)
self.skipctrl = 0
self.plotter = iqplot.Plot(self.maxy)
self.calc_interval()
@ -151,6 +158,9 @@ class Frequency(HasIO, Readable):
# self.pollinterval = value * 0.0001
return value
def write_sr(self, value):
return value
def register_roi(self, roi):
self.roilist.append(roi)
@ -176,7 +186,11 @@ class Frequency(HasIO, Readable):
"""main poll loop body"""
if self.lastfreq is None:
self.lastfreq = self.set_freq()
if self.rusmode:
self.sr = int(12e9/self.lastfreq) #picking up 12 period at the ith frequency in the time scale
# self.adq.samples_per_record = self.sr
self.adq.start()
if self.starttime is None:
self.starttime = time.time()
times = []
@ -200,6 +214,8 @@ class Frequency(HasIO, Readable):
gates = self.adq.gates_and_curves(data, freq, self.interval,
[r.interval for r in roilist])
if self.curves: # if attached Curves module is defined, update it
self.curves.value = self.adq.curves
if self.save:
times.append(('save',time.time()))
tdata, idata, qdata, pdata = self.adq.curves
@ -259,3 +275,9 @@ class Frequency(HasIO, Readable):
self.freq = sorted((self.freq - self.maxstep, newfreq, self.freq + self.maxstep))[1]
#print(times)
return Done
class Curves(Readable):
value = Parameter("t, i, q, pulse curves",
TupleOf(*[ArrayOf(FloatRange(), 0, 16283) for _ in range(4)]), default=[[]] * 4)