A few things: 1. Got it working again; 2. Renamed files to make more sense; 3. Replaced template tmp.tnt with an emptied out file that previously took data, now data is collected correctly (bug, I'm not sure where this need comes from but this is, as far as I know, a permanent workaround); 4. Added automatic COM interface restart on errors compiling; 5. Implemented variable acquisition times.
This commit is contained in:
@@ -11,8 +11,8 @@ frappy-based module for generating and running pulse sequences on TNMR (Tecmag).
|
||||
"""
|
||||
#On-the-fly!
|
||||
|
||||
import TNMRExt.TNMR_DG_Extension as te
|
||||
import TNMRExt.SequenceGeneration as seq_gen
|
||||
import frappy_psi.tnmr.tnmr_interface as te
|
||||
import frappy_psi.tnmr.sequence_generation as seq_gen
|
||||
|
||||
import frappy.core as fc
|
||||
import frappy
|
||||
@@ -31,7 +31,7 @@ class ProgrammedSequence(fc.Readable):
|
||||
Instance Attributes
|
||||
-------------------
|
||||
(parameter) title: a title which will be embedded to the sequence files. Use this for identification.
|
||||
(parameter) sequence_data: a dictionary describing the currently-built sequence
|
||||
(parameter) sequence_data: an array of structs: keys are { 'pulse_width': (width of pulse in us), 'pulse_height': (amplitude of pulse in a.u.), 'relaxation_time': (relaxation time in us), 'phase_cycle': (a str denoting a phase cycle, e.g., '0 1 2 3') }
|
||||
(parameter) value: an array of complexes representing the TNMR data return (technically inherited from Readable)
|
||||
(parameter) acquisition_time: float (usecs) which describes the length of acquisition
|
||||
(parameter) ringdown_time: float (usecs) which describes the length of ringdown
|
||||
@@ -64,7 +64,10 @@ class ProgrammedSequence(fc.Readable):
|
||||
"""
|
||||
|
||||
# inherited
|
||||
value = fc.Parameter('data_return', fc.ArrayOf(fc.FloatRange(), maxlen=4096), default=[])
|
||||
value = fc.Parameter('data_return', 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': [] })
|
||||
status = fc.Parameter(datatype=frappy.datatypes.StatusType(fc.Readable, "DISABLED", 'PREPARED', 'BUSY'))
|
||||
pollinterval = fc.Parameter(default=1)
|
||||
|
||||
@@ -73,19 +76,19 @@ class ProgrammedSequence(fc.Readable):
|
||||
sequence_data = fc.Parameter('sequence_config', fc.ArrayOf(fc.StructOf(pulse_width=fc.FloatRange(unit='u'),
|
||||
pulse_height=fc.FloatRange(),
|
||||
relaxation_time=fc.FloatRange(unit='u'),
|
||||
phase_cycle=fc.StringType())))
|
||||
phase_cycle=fc.StringType())), default=[], readonly=False)
|
||||
|
||||
# sequence edit
|
||||
pulse_width = fc.Parameter('pulse_width', fc.FloatRange(unit='u'), readonly=False, group='pulse_editor')
|
||||
pulse_height = fc.Parameter('pulse_height', fc.FloatRange(), readonly=False, group='pulse_editor')
|
||||
relaxation_time = fc.Parameter('relaxation_time', fc.FloatRange(unit='u', min=0.1), readonly=False, group='pulse_editor')
|
||||
phase_cycle = fc.Parameter('phase_cycle', fc.StringType(), readonly=False, group='pulse_editor', default='')
|
||||
#pulse_width = fc.Parameter('pulse_width', fc.FloatRange(unit='u'), readonly=False, group='pulse_editor', default=5)
|
||||
#pulse_height = fc.Parameter('pulse_height', fc.FloatRange(), readonly=False, group='pulse_editor', default=40)
|
||||
#relaxation_time = fc.Parameter('relaxation_time', fc.FloatRange(unit='u', min=0.1), readonly=False, group='pulse_editor', default=50)
|
||||
#phase_cycle = fc.Parameter('phase_cycle', fc.StringType(), readonly=False, group='pulse_editor', default='')
|
||||
|
||||
# final details
|
||||
acquisition_time = fc.Parameter('acquisition_time', fc.FloatRange(unit='u'), readonly=True, group='sequence_editor', default=204.8) # this is a limit set by the dwell limit and number of acquisition points (1024, TODO: Make this adjustable)
|
||||
ringdown_time = fc.Parameter('ringdown_time', fc.FloatRange(unit='u'), readonly=False, group='sequence_editor')
|
||||
pre_acquisition_time = fc.Parameter('pre_acquisition_time', fc.FloatRange(unit='u'), readonly=False, group='sequence_editor')
|
||||
post_acquisition_time = fc.Parameter('post_acquisition_time', fc.FloatRange(unit='m'), readonly=False, group='sequence_editor')
|
||||
acquisition_time = fc.Parameter('acquisition_time', fc.FloatRange(unit='u'), readonly=False, group='sequence_editor', default=204.8) # this is a limit set by the dwell limit and number of acquisition points (1024, TODO: Make this adjustable)
|
||||
ringdown_time = fc.Parameter('ringdown_time', fc.FloatRange(unit='u'), readonly=False, group='sequence_editor', default=1)
|
||||
pre_acquisition_time = fc.Parameter('pre_acquisition_time', fc.FloatRange(unit='u'), readonly=False, group='sequence_editor', default=1)
|
||||
post_acquisition_time = fc.Parameter('post_acquisition_time', fc.FloatRange(unit='m'), readonly=False, group='sequence_editor', default=500)
|
||||
acq_phase_cycle = fc.Parameter('acq_phase_cycle', fc.StringType(), readonly=False, group='sequence_editor', default='')
|
||||
|
||||
inited = False
|
||||
@@ -101,111 +104,79 @@ class ProgrammedSequence(fc.Readable):
|
||||
pass
|
||||
|
||||
### COMMANDS
|
||||
@fc.Command(description="Add Pulse", group='pulse_editor', argument={ 'type': 'struct' }, members={ 'a': { 'type': 'string' }})
|
||||
def add_pulse(self):
|
||||
if(self.status == ('PREPARED', 'compiled')):
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
data = list(self.sequence_data) # should be a tuple when it comes out of ArrayOf __call__, so make it mutable
|
||||
data += [ { 'pulse_width': self.pulse_width, 'pulse_height': self.pulse_height, 'relaxation_time': self.relaxation_time, 'phase_cycle': self.phase_cycle } ]
|
||||
self.sequence_data = data
|
||||
#@fc.Command(description="Add Pulse", group='pulse_editor')
|
||||
#def add_pulse(self):
|
||||
# if(self.status == ('PREPARED', 'compiled')):
|
||||
# self.status = ('IDLE', 'ok - uncompiled')
|
||||
# data = list(self.sequence_data) # should be a tuple when it comes out of ArrayOf __call__, so make it mutable
|
||||
# data += [ { 'pulse_width': self.pulse_width, 'pulse_height': self.pulse_height, 'relaxation_time': self.relaxation_time, 'phase_cycle': self.phase_cycle } ]
|
||||
# self.sequence_data = data
|
||||
|
||||
@fc.Command(description="Pop Pulse", group='pulse_editor')
|
||||
def pop_pulse(self):
|
||||
if(self.status == ('PREPARED', 'compiled')):
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
data = list(self.sequence_data) # should be a tuple when it comes out of ArrayOf __call__, so make it mutable
|
||||
data = data[:-1] # chop off the tail
|
||||
self.sequence_data = data
|
||||
#@fc.Command(description="Pop Pulse", group='pulse_editor')
|
||||
#def pop_pulse(self):
|
||||
# if(self.status == ('PREPARED', 'compiled')):
|
||||
# self.status = ('IDLE', 'ok - uncompiled')
|
||||
# data = list(self.sequence_data) # should be a tuple when it comes out of ArrayOf __call__, so make it mutable
|
||||
# data = data[:-1] # chop off the tail
|
||||
# self.sequence_data = data
|
||||
|
||||
@fc.Command(description="Compile", group='sequence_editor')
|
||||
def compile_sequence(self):
|
||||
threading.Thread(target=lambda s=self: s.__compile_sequence()).start()
|
||||
#@fc.Command(description="Compile", group='sequence_editor')
|
||||
#def compile_sequence(self):
|
||||
# threading.Thread(target=lambda s=self: s.__compile_sequence()).start()
|
||||
|
||||
@fc.Command(description="Run")
|
||||
def run(self):
|
||||
threading.Thread(target=lambda s=self: s.__zero_go()).start()
|
||||
#@fc.Command(description="Run")
|
||||
#def run(self):
|
||||
# threading.Thread(target=lambda s=self: s.__zero_go()).start()
|
||||
|
||||
@fc.Command(description="Compile & Run")
|
||||
def compile_and_run(self):
|
||||
threading.Thread(target=lambda s=self: s.__compile_and_run()).start()
|
||||
@fc.Command(description="Compile & Run", argument={'type': 'bool'})
|
||||
def compile_and_run(self, thread=True):
|
||||
if(thread):
|
||||
threading.Thread(target=lambda s=self: s.__compile_and_run()).start()
|
||||
else:
|
||||
self.__compile_and_run()
|
||||
|
||||
### READ/WRITE
|
||||
def read_value(self):
|
||||
return self.value # TODO: this is only reals
|
||||
|
||||
def read_title(self):
|
||||
return self.title
|
||||
|
||||
def write_title(self, t):
|
||||
self.title = t
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
return self.read_title()
|
||||
|
||||
#def write_pulse_width(self, t):
|
||||
# self.pulse_width = t
|
||||
# return self.read_pulse_width()
|
||||
|
||||
def read_sequence_data(self):
|
||||
return self.sequence_data
|
||||
#def write_pulse_height(self, t):
|
||||
# self.pulse_height = t
|
||||
# return self.read_pulse_height()
|
||||
|
||||
def read_pulse_width(self):
|
||||
return self.pulse_width
|
||||
#def write_relaxation_time(self, t):
|
||||
# self.relaxation_time = t
|
||||
# return self.read_relaxation_time()
|
||||
|
||||
def write_pulse_width(self, t):
|
||||
self.pulse_width = t
|
||||
return self.read_pulse_width()
|
||||
|
||||
def read_pulse_height(self):
|
||||
return self.pulse_height
|
||||
|
||||
def write_pulse_height(self, t):
|
||||
self.pulse_height = t
|
||||
return self.read_pulse_height()
|
||||
|
||||
def read_relaxation_time(self):
|
||||
return self.relaxation_time
|
||||
|
||||
def write_relaxation_time(self, t):
|
||||
self.relaxation_time = t
|
||||
return self.read_relaxation_time()
|
||||
|
||||
def read_phase_cycle(self):
|
||||
return self.phase_cycle
|
||||
|
||||
def write_phase_cycle(self, t):
|
||||
self.phase_cycle = t
|
||||
return self.read_phase_cycle()
|
||||
|
||||
def read_acquisition_time(self):
|
||||
return self.acquisition_time
|
||||
#def write_phase_cycle(self, t):
|
||||
# self.phase_cycle = t
|
||||
# return self.read_phase_cycle()
|
||||
|
||||
def write_acquisition_time(self, t):
|
||||
self.acquisition_time = t
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
return self.read_acquisition_time()
|
||||
|
||||
def read_ringdown_time(self):
|
||||
return self.ringdown_time
|
||||
|
||||
def write_ringdown_time(self, t):
|
||||
self.ringdown_time = t
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
return self.read_ringdown_time()
|
||||
|
||||
def read_pre_acquisition_time(self):
|
||||
return self.pre_acquisition_time
|
||||
|
||||
def write_pre_acquisition_time(self, t):
|
||||
self.pre_acquisition_time = t
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
return self.read_pre_acquisition_time()
|
||||
|
||||
def read_post_acquisition_time(self):
|
||||
return self.post_acquisition_time
|
||||
|
||||
def write_post_acquisition_time(self, t):
|
||||
self.post_acquisition_time = t
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
return self.read_post_acquisition_time()
|
||||
|
||||
def read_acq_phase_cycle(self):
|
||||
return self.acq_phase_cycle
|
||||
|
||||
def write_acq_phase_cycle(self, t):
|
||||
self.acq_phase_cycle = t
|
||||
@@ -214,7 +185,7 @@ class ProgrammedSequence(fc.Readable):
|
||||
|
||||
### PRIVATE (Utility)
|
||||
def __compile_sequence(self):
|
||||
if(self.status != ('PREPARED', 'compiled')) and (self.status[0] != 'BUSY'):
|
||||
if(self.status[0] != 'BUSY'):
|
||||
self.status = ('BUSY', 'compiling')
|
||||
# first, create the sequence
|
||||
seq = seq_gen.get_initial_block()
|
||||
@@ -248,13 +219,30 @@ class ProgrammedSequence(fc.Readable):
|
||||
if(self.status[0] != 'BUSY'):
|
||||
self.status = ('BUSY', 'acquiring')
|
||||
self.tnmr().ZeroGo(lock=True, interval=0.5)
|
||||
self.value = self.tnmr().get_data()[0] # TODO: this is only reals...
|
||||
print(self.value)
|
||||
newvals = {}
|
||||
newvals['reals'] = self.tnmr().get_data()[0]
|
||||
newvals['imags'] = self.tnmr().get_data()[1]
|
||||
newvals['t'] = self.tnmr().get_data_times()
|
||||
self.value = newvals
|
||||
self.status = ('PREPARED', 'compiled')
|
||||
|
||||
def __compile_and_run(self):
|
||||
self.__compile_sequence()
|
||||
self.__zero_go()
|
||||
def __compile_and_run(self, thread=True, recurse=True):
|
||||
pythoncom.CoInitialize()
|
||||
try:
|
||||
self.__compile_sequence()
|
||||
self.__zero_go()
|
||||
except AttributeError as e:
|
||||
print(f'Attribute error on compile and run.{" Resetting the COM interface and retrying..." if recurse else " Resetting did not fix this problem!"}')
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
self.tnmr().reset_NTNMR_instance()
|
||||
self.__compile_and_run(thread, recurse=False)
|
||||
except Exception as e:
|
||||
print('Failed to compile and run!')
|
||||
print(str(e))
|
||||
print(repr(e))
|
||||
self.status = ('IDLE', 'ok - uncompiled')
|
||||
if(thread):
|
||||
pythoncom.CoUninitialize()
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user