From b3d243d83151c340a495f86e1c4c30d45811b7b8 Mon Sep 17 00:00:00 2001 From: "Davis V. Garrad" Date: Tue, 5 Aug 2025 10:33:59 +0200 Subject: [PATCH] safety --- frappy_psi/capacitance_readings/TSSOP16.py | 20 ++--- frappy_psi/tnmr/OTFModule.py | 91 ++++++++++++++++----- frappy_psi/tnmr/sequence_generation.py | 3 + frappy_psi/tnmr/templates/tmp - Copy.tnt | Bin 0 -> 16057 bytes frappy_psi/tnmr/templates/tmper.tnt | Bin 30145 -> 30146 bytes frappy_psi/tnmr/tnmr_interface.py | 15 +++- 6 files changed, 94 insertions(+), 35 deletions(-) create mode 100644 frappy_psi/tnmr/templates/tmp - Copy.tnt diff --git a/frappy_psi/capacitance_readings/TSSOP16.py b/frappy_psi/capacitance_readings/TSSOP16.py index 2810e9d4..91d1011a 100644 --- a/frappy_psi/capacitance_readings/TSSOP16.py +++ b/frappy_psi/capacitance_readings/TSSOP16.py @@ -5,35 +5,35 @@ import traceback from frappy.core import Readable, Parameter, FloatRange, HasIO, StringIO, Property, IntRange, IDLE, BUSY, WARN, ERROR, Drivable, BoolType, Attached class TSSOP16_IO(StringIO): - end_of_line = '\r' - wait_before = 0.0 #3.0 - timeout=3.0 + end_of_line = '\r\n' + wait_before = 3.0 + timeout=1 identification = [ ('*IDN?', r'0x48,ACM1219,.*') ] class TSSOP16(HasIO, Readable): '''only configured for channel 1''' ioClass = TSSOP16_IO - value = Parameter('value', FloatRange(unit='pF'), readonly=True) - pollinterval = Parameter(default=0.1) + value = Parameter('value', FloatRange(unit='pF'), readonly=True, default=-1) + pollinterval = Parameter(default=1) def custom_read_off_cvt(self, recursive_layer=0): try: l = self.communicate('readCVT') vals = l.split(',') vals = [ float(v) for v in vals ] - #print(vals) + print(vals) return vals[0] except: - return self.read_off_cvt() + return self.custom_read_off_cvt(recursive_layer=recursive_layer+1) traceback.print_exc() self.io.closeConnection() self.io.connectStart() - time.sleep(3.0) - if(recursive_layer > 10): + #time.sleep(3.0) + if(recursive_layer > 2): return -1 else: - return self.read_off_cvt(recursive_layer+1) + return self.custom_read_off_cvt(recursive_layer+1) def read_value(self): res = self.custom_read_off_cvt() diff --git a/frappy_psi/tnmr/OTFModule.py b/frappy_psi/tnmr/OTFModule.py index 057a4202..3212af69 100644 --- a/frappy_psi/tnmr/OTFModule.py +++ b/frappy_psi/tnmr/OTFModule.py @@ -27,7 +27,7 @@ import time import os import traceback -class ProgrammedSequence(fc.Readable): +class ProgrammedSequence(fc.Drivable): # Drivable only for kill() funcitonality """An NMR device being driven by an instance of TNMR. Requires that an instance of TNMR is opened before creation. Use @@ -49,7 +49,7 @@ class ProgrammedSequence(fc.Readable): pre_acquisition_time: float (usecs) which describes the length of time to wait after ringdown finishes (1u is okay) post_acquisition_time: float (ms) which describes the length of time to wait after finishing acquisition acq_phase_cycle: str, the phase cycle to run on acquisition (eg., '0 1 1 2', '0 1 2 3', '1 1 2 2 0 0 3 3 1 2 3 4', ...) - num_scans: int (ct), the number of 1D scans to take per sequence + num_acqs: int (ct), the number of 1D scans to take per sequence obs_freq: float (MHz), the NMR frequency Commands @@ -72,7 +72,12 @@ class ProgrammedSequence(fc.Readable): imags=fc.ArrayOf(fc.FloatRange(), maxlen=4096), # imag values t =fc.ArrayOf(fc.FloatRange(), maxlen=4096)), # times (starting from zero) default={ 'reals': [], 'imags': [], 't': [] }) - status = fc.Parameter(datatype=frappy.datatypes.StatusType(fc.Readable, "DISABLED", 'PREPARED', 'BUSY'), default=('IDLE', 'ok - uncompiled')) + target = fc.Parameter('dummy', fc.StructOf(reals=fc.ArrayOf(fc.FloatRange(), maxlen=4096), # real values + imags=fc.ArrayOf(fc.FloatRange(), maxlen=4096), # imag values + t =fc.ArrayOf(fc.FloatRange(), maxlen=4096)), # times (starting from zero) + default={ 'reals': [], 'imags': [], 't': [] }, readonly=True, visibility='w--') + + status = fc.Parameter(datatype=frappy.datatypes.StatusType(fc.Drivable, "DISABLED", 'PREPARED', 'BUSY'), default=('IDLE', 'ok - uncompiled')) pollinterval = fc.Parameter(default=1) # basic @@ -81,10 +86,12 @@ class ProgrammedSequence(fc.Readable): comments = fc.Parameter('comments', fc.StringType(), default='', readonly=False) nucleus = fc.Parameter('nucleus', fc.StringType(), default='', readonly=False) + sequence_length = fc.Parameter('sequence_length', fc.IntRange(), default=0, readonly=True) sequence_data = fc.Parameter('sequence_config', fc.ArrayOf(fc.StructOf(pulse_width=fc.FloatRange(unit='usecs'), pulse_height=fc.FloatRange(unit='%'), delay_time=fc.FloatRange(unit='usecs'), - phase_cycle=fc.StringType())), default=[], readonly=False) + phase_cycle=fc.StringType()), minlen=0), default=[{'pulse_width':0,'pulse_height':0,'delay_time':0,'phase_cycle':''}]*100, readonly=False) + num_acqs_actual = fc.Parameter('num_acqs', fc.IntRange(), readonly=True, default=0) # final details acquisition_time = fc.Parameter('acquisition_time', fc.FloatRange(unit='usecs'), readonly=False, group='sequence_editor', default=204.8) # this is a limit set by the dwell limit and number of acquisition points @@ -92,11 +99,12 @@ class ProgrammedSequence(fc.Readable): pre_acquisition_time = fc.Parameter('pre_acquisition_time', fc.FloatRange(unit='usecs'), readonly=False, group='sequence_editor', default=1) post_acquisition_time = fc.Parameter('post_acquisition_time', fc.FloatRange(unit='msecs'), readonly=False, group='sequence_editor', default=500) acq_phase_cycle = fc.Parameter('acq_phase_cycle', fc.StringType(), readonly=False, group='sequence_editor', default='') - num_scans = fc.Parameter('num_scans', fc.IntRange(), readonly=False, group='sequence_editor', default=16) + num_acqs = fc.Parameter('num_acqs', fc.IntRange(), readonly=False, group='sequence_editor', default=16) obs_freq = fc.Parameter('obs_freq', fc.FloatRange(unit='MHz'), readonly=False, group='sequence_editor', default=213.16) compiled_parameters = {} # so that we can store the values of parameters only when compiling, effectively giving us an instance of each parameter loaded into TNMR, as well as "targets" (those above) inited = False + starting = False approx_sequence_length = 0 ### SETUP @@ -148,6 +156,20 @@ class ProgrammedSequence(fc.Readable): ### READ/WRITE + def read_status(self): + if not(self.inited): + self.status = ('ERROR', 'TNMR disconnected!') + else: + if(self.starting): + self.status = ('BUSY', 'starting') + else: + if(self.tnmr().acquisition_running()): + self.status = ('BUSY', 'acquiring') + elif(self.status[1] == 'acquiring'): + # we've just finished acquiring, in frappy's perspective + self.status = ('PREPARED', 'compiled') + return self.status + def write_title(self, t): self.title = t self.status = ('IDLE', 'ok - uncompiled') @@ -193,14 +215,14 @@ class ProgrammedSequence(fc.Readable): self.status = ('IDLE', 'ok - uncompiled') return self.read_acq_phase_cycle() - def read_num_scans(self): - return self.tnmr().get_nmrparameter('Scans 1D') + def read_num_acqs(self): + return int(self.tnmr().get_nmrparameter('Scans 1D')) - def write_num_scans(self, t): + def write_num_acqs(self, t): if(self.status[0] != 'BUSY'): self.tnmr().set_nmrparameter('Scans 1D', t) self.status = ('IDLE', 'ok - uncompiled') - return self.read_num_scans() + return self.read_num_acqs() def read_obs_freq(self): return self.tnmr().get_nmrparameter('Observe Freq.') @@ -210,6 +232,36 @@ class ProgrammedSequence(fc.Readable): self.tnmr().set_nmrparameter('Observe Freq.', t) self.status = ('IDLE', 'ok - uncompiled') return self.read_obs_freq() + + def write_sequence_data(self, t): + self.sequence_length = len(t) + seq = [] + seq += t + print(seq) + seq += [{'pulse_width':0,'pulse_height':0,'delay_time':0,'phase_cycle':''}] * (100-self.sequence_length) # because nicos will only send the smallest size it has ever sent... + self.sequence_data = seq + + return self.read_sequence_data() + + def read_value(self): + newvals = {} + #try: + d = self.tnmr().get_data() + newvals['reals'] = d[0] + newvals['imags'] = d[1] + newvals['t'] = [ self.compiled_parameters['acquisition_time'] * i/len(d[0]) for i in range(0, len(d[0])) ] + #except: + # newvals['reals'] = [] + # newvals['imags'] = [] + # newvals['t'] = [] + return newvals + + def read_num_acqs_actual(self): + try: + n = self.tnmr().get_nmrparameter('Actual Scans 1D') + return int(n) + except: + return 0 ### PRIVATE (Utility) def __compile_sequence(self): @@ -229,7 +281,8 @@ class ProgrammedSequence(fc.Readable): seq = seq_gen.get_initial_block() i = 0 self.approx_sequence_length = 0 - for s in self.sequence_data: + for si in range(self.sequence_length): + s = self.sequence_data[si] seq = seq_gen.combine_blocks(seq, seq_gen.get_single_pulse_block(f'pulse_{i}', str(s['pulse_width']) + 'u', str(s['pulse_height']), str(s['delay_time']) + 'u', @@ -252,10 +305,9 @@ class ProgrammedSequence(fc.Readable): filename = filepath + '/sequences/' + filename.replace('.','') seq_gen.save_sequence(filename, seq) seq_gen.save_sequence_cfg(filename, seq) - print(filename) dashboard_params = { 'Observe Freq.': self.read_obs_freq(), - 'Scans 1D': self.read_num_scans(), + 'Scans 1D': self.read_num_acqs(), } self.compiled_parameters['ringdown_time'] = self.ringdown_time @@ -263,7 +315,7 @@ class ProgrammedSequence(fc.Readable): self.compiled_parameters['acquisition_time'] = self.acquisition_time self.compiled_parameters['post_acquisition_time'] = self.post_acquisition_time self.compiled_parameters['acq_phase_cycle'] = self.acq_phase_cycle - self.compiled_parameters['num_scans'] = self.read_num_scans() + self.compiled_parameters['num_acqs'] = self.read_num_acqs() self.compiled_parameters['obs_freq'] = self.read_obs_freq() self.compiled_parameters['title'] = self.read_title() self.compiled_parameters['comments'] = self.read_comments() @@ -290,15 +342,8 @@ class ProgrammedSequence(fc.Readable): def __zero_go(self): '''Tells TNMR to acquire data. Only call after __compile_sequence().''' - if(self.status[0] != 'BUSY'): - self.status = ('BUSY', 'acquiring') - self.tnmr().ZeroGo(lock=True, interval=0.5, check_time=max(self.approx_sequence_length*5, 5)) - newvals = {} - newvals['reals'] = self.tnmr().get_data()[0] - newvals['imags'] = self.tnmr().get_data()[1] - newvals['t'] = [ self.compiled_parameters['acquisition_time'] * i/1024 for i in range(0, 1024) ] - self.value = newvals - self.status = ('PREPARED', 'compiled') + if(self.status[0] != 'BUSY' or self.starting): + self.tnmr().ZeroGo(lock=False, check_time=max(int(self.approx_sequence_length*1.5), 5)) def __compile_and_run(self, thread=True): '''Compiles and runs the currently-loaded sequence @@ -307,9 +352,11 @@ class ProgrammedSequence(fc.Readable): ---------- thread: bool, determines if this should open a child thread and detach the process ''' + self.starting = True self.__compile_sequence() time.sleep(1.0) self.__zero_go() + self.starting = False diff --git a/frappy_psi/tnmr/sequence_generation.py b/frappy_psi/tnmr/sequence_generation.py index 928eb673..b0b366b3 100644 --- a/frappy_psi/tnmr/sequence_generation.py +++ b/frappy_psi/tnmr/sequence_generation.py @@ -41,6 +41,9 @@ def get_single_pulse_block(name, pulse_width, pulse_height, delay_time, phase_cy delay_time = float(delay_time.strip()[:-1]) / 1e6 else: delay_time = float(delay_time.strip()) # assume in us + + delay_time_rounded = int(delay_time*10) / 10 # nearest 10ns + delay_time = delay_time_rounded ph = name + '_phase' rl = name + '_delay' diff --git a/frappy_psi/tnmr/templates/tmp - Copy.tnt b/frappy_psi/tnmr/templates/tmp - Copy.tnt new file mode 100644 index 0000000000000000000000000000000000000000..631a30c2b527ab283d99c95a97f30426fc0a11d4 GIT binary patch literal 16057 zcmeHO&2QsG6!+2(DAKZkDlI}tjI@YTn#M`GTjf$aA59BMnkLR}+e?*gTw-mSgv9;Y zQ;*!>%71`_1P5-M5pY0z#tmiJ~niR~m#V>fCMGm-P&ym@ct=ij`UIF_vy zG&vlJtZI2RdrN{c%MR4}fi|vlAA243H?0Fi&lmOQPZz(*K9>~KrQh02(#nvpaqUAw zMwxEW6B*Sy#QxcL3bhWAOR4w%`u+a9Yonwi4l2y=`!#BpUQme~{jhV^Wwya@%Ilzc za!awJ&>$`+@4;8Z&hDPCxc^Jg@v2hC1J5@aiLNZ)Uyc(wo{(dSxa=Zf!)@KV|>FGGt##yPqMxtpP%AVD1?cn~CIp8pMmDfR?x2lT@pQZkO#AXDyDwrNN zBTyL^zei$pLxahRGo@2KWLq3v$m8O55~1FF#pC$6vPnvBntF$fi@#hRv7H1kMtNq% zxDsayqNb_Y-+tJZbE*9ZBkic;Fem9#z0n|=aioWcdhlu3QghX=Nz!_w%Fj&5o5?`2 zoY}@zB8TNwlH4r7C4p3FnugiXYqK`UXy?y5z70BVhpsJ={togG?32vFSD|{OCwdBR zo>bEACJo~Lh!Z=&f|Bc4$2R#G@nN=}_Y`dNQ&@j~A~A+CD(5qZa7S|@MOy@?VO`!Y z)=OQ}u7lZEp`H4Qgdjw_;Dr`9V?diZGqsNKiL>T+(7=e_mx~#sE>;@CFI+VtV)dxq zu&LLEbq?4?mN$w{tY}-3F-+q7pPJGV|3(5hUxSHSshGwc=#{fSiFxt1QQBe+{#l5Pe?N{2h zs!2DlUd0bqj0uF7_#Jghad77|jwi%t8g+gYGwR1Utd`kUQEnErEXrJA9j?S7S@Q)d zp^q4kLA%!*kmz@#iv2I$w1DxqEl2jx@6e8L7>NH{8R2nL(QD+?fcwj_=+a}uBw8U~A}4yMYqXjPLSo@~I3^R4JT@9NB0rGj$b);Cv$o!W zJ9kaERR=#8;5P6UtKH}EIjMB@4!9MT)qC(y&Nwt+R4eq6UTf;zTJ=0cK00o~4bOOp ac4xg1yAtU~*gDFW6acyujmRr$$?B3lNBsjHor7h zXJj!p(lebr(X^W(FE<4HsSU7b41nEIM%k$K-n@0*n_Yzb~07;aHkpT3n)FV4|a7WME{f RU|?=#Y;I*>FnM07G63tSO3(lR delta 217 zcmX@~n(^Rk#tjZSQo*T(rKx$zsqu#9riMlq#%87#h9(B429{=PfqNWg*7bh4tM