TNMR updates: workaround for the hardware module error. Added more dashboard support. Added class definition auto-generated by pycom (NTNMR.py)

This commit is contained in:
2025-06-30 13:55:21 +02:00
parent 388748c995
commit 10acd4a188
5 changed files with 1687 additions and 17 deletions

1596
frappy_psi/tnmr/NTNMR.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -93,6 +93,7 @@ class ProgrammedSequence(fc.Readable):
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
approx_sequence_length = 0
### SETUP
def tnmr(self):
@@ -207,11 +208,14 @@ class ProgrammedSequence(fc.Readable):
# first, create the sequence
seq = seq_gen.get_initial_block()
i = 0
self.approx_sequence_length = 0
for s in self.sequence_data:
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',
str(s['phase_cycle'])))
self.approx_sequence_length += float(s['delay_time'])*1e-6
self.approx_sequence_length += float(s['pulse_width'])*1e-6
i += 1
seq = seq_gen.combine_blocks(seq, seq_gen.get_final_block(str(self.ringdown_time) + 'u',
str(self.pre_acquisition_time) + 'u',
@@ -219,10 +223,13 @@ class ProgrammedSequence(fc.Readable):
str(self.post_acquisition_time) + 'm',
str(self.acq_phase_cycle)))
self.approx_sequence_length += float(self.acquisition_time)*1e-6
self.approx_sequence_length += float(self.post_acquisition_time)*1e-6
# then, save the thing
filepath = os.getcwd()
filename = self.title + f'_{time.time()}'
filename = filepath + '/' + filename.replace('.','')
filename = filepath + '/sequences/' + filename.replace('.','')
seq_gen.save_sequence(filename, seq)
seq_gen.save_sequence_cfg(filename, seq)
@@ -240,12 +247,10 @@ class ProgrammedSequence(fc.Readable):
# then, load the thing into TNMR
self.tnmr().load_sequence(filename)
time.sleep(1.0) # hardware module issue???
# load some parameters back to TNMR
for key, val in dashboard_params.items():
self.tnmr().set_nmrparameter(key, val)
time.sleep(0.5)
# finally, let ourselves know we're ready
self.status = ('PREPARED', 'compiled')
@@ -256,11 +261,11 @@ class ProgrammedSequence(fc.Readable):
'''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)
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/self.compiled_parameters['num_scans'] for i in range(0, self.compiled_parameters['num_scans']) ]
newvals['t'] = [ self.compiled_parameters['acquisition_time'] * i/1024 for i in range(0, 1024) ]
self.value = newvals
self.status = ('PREPARED', 'compiled')
@@ -272,7 +277,7 @@ class ProgrammedSequence(fc.Readable):
thread: bool, determines if this should open a child thread and detach the process
'''
self.__compile_sequence()
time.sleep(0.5)
time.sleep(1.0)
self.__zero_go()

View File

@@ -0,0 +1,5 @@
Acquisition = Nucleus, Observe Freq., Acq. Points, Points 1D, SW +/-, Filter, Dwell Time, Acq. Time, Last Delay, ::, Scans 1D, Actual Scans 1D, Scan Start 1D, Repeat Times, S.A. Dimension, Dummy Scans, Receiver Gain, ::, Points 2D, Actual Points 2D, Points Start 2D, Points 3D, Actual Points 3D, Points Start 3D, Points 4D, Actual Points 4D, Points Start 4D, ::, SW 2D, SW 3D, SW 4D, Dwell_2D, Dwell_3D, Dwell_4D
Frequency = Observe Freq., Observe Ch., ::, F1 Freq., F2 Freq.
Processing = Shift # Points, LB 1D, GB 1D, DM 1D, SB Shift 1D, SB Width 1D, SB Skew 1D, TZ 1 1D, TZ 2 1D, TZ 3 1D, TZ 4 1D, Traf 1D, Sys. Phase 0 1D, Sys. Phase 1 1D, Phase 0 1D, Phase 1 1D, Echo Center 1D, ::, LB 2D, GB 2D, DM 2D, SB Shift 2D, SB Width 2D, SB Skew 2D, TZ 1 2D, TZ 2 2D, TZ 3 2D, TZ 4 2D, Traf 2D, Sys. Phase 0 2D, Sys. Phase 1 2D, Phase 0 2D, Phase 1 2D, Echo Center 2D, ::, LB 3D, GB 3D, DM 3D, SB Shift 3D, SB Width 3D, SB Skew 3D, TZ 1 3D, TZ 2 3D, TZ 3 3D, TZ 4 3D, Traf 3D, Sys. Phase 0 3D, Sys. Phase 1 3D, Phase 0 3D, Phase 1 3D, Echo Center 3D, ::, LB 4D, GB 4D, DM 4D, SB Shift 4D, SB Width 4D, SB Skew 4D, TZ 1 4D, TZ 2 4D, TZ 3 4D, TZ 4 4D, Traf 4D, Sys. Phase 0 4D, Sys. Phase 1 4D, Phase 0 4D, Phase 1 4D, Echo Center 4D
Misc. = Date, Magnet Field, Absolute Freq., Exp. Start Time, Exp. Finish Time, Exp. Elapsed Time

Binary file not shown.

View File

@@ -15,8 +15,12 @@ TEMPLATE_FILE_PATH = os.path.dirname(os.path.realpath(__file__)) + '/templates/'
import win32com.client
import pythoncom
import frappy_psi.tnmr.NTNMR as NTNMR
import time
import json
import traceback
import threading
class TNMRNotRunnningError(Exception):
def __init__(self, msg=None):
@@ -88,7 +92,7 @@ class TNMR:
if filepath != "":
print(f'Loading file {filepath}')
ntnmr.OpenFile(filepath)
self.ACTIVEFILE = ntnmr.GetActiveDocPath
self.ACTIVEFILE = ntnmr.GetActiveDocPath()
self.ACTIVEPATH = os.path.dirname(self.ACTIVEFILE)
print(f'Active file: {self.ACTIVEFILE} in path {self.ACTIVEPATH}')
@@ -122,17 +126,17 @@ class TNMR:
ntnmr = self.get_instance()
ntnmr.OpenFile(filepath)
if active:
self.ACTIVEFILE = ntnmr.GetActiveDocPath
self.ACTIVEFILE = ntnmr.GetActiveDocPath()
print(f'Active file: {self.ACTIVEFILE} in path {self.ACTIVEPATH}')
def set_activefile(self):
""" Sets TNMR active doc path to ACTIVEFILE
"""
self.ACTIVEFILE = self.get_instance().GetActiveDocPath
self.ACTIVEFILE = self.get_instance().GetActiveDocPath()
self.ACTIVEPATH = os.path.dirname(self.ACTIVEFILE)
print(f'Active file: {self.ACTIVEFILE} in path {self.ACTIVEPATH}')
def ZeroGo(self, lock = True, interval = 0.5):
def ZeroGo(self, lock = True, interval = 0.5, check_time=10):
""" If possible, zeros and starts acquisition
Parameters
@@ -141,19 +145,79 @@ class TNMR:
if true, program waits until acquisition is done
interval: float
how often to check if acquisition done
check_time: float
how many seconds until not recieving new data is considered grounds for another Zero-Go attempt. Recommended to set as at least the length of 2-3 pulse sequences.
"""
# for some reason CheckAcquisition is False while an experiment is
# running but true otherwise
CHECK_MODE = 'thread' # thread OR data
print('Zero-going...')
ntnmr = self.get_instance()
if not(self.acquisition_running()):
ntnmr.Reset # to avoid hardware issues?
ntnmr.ZG
print('Reset')
ntnmr.Reset() # to avoid hardware issues? EDIT: Doesn't seem to do much...
if(CHECK_MODE == 'data'):
print('Artificially setting the zeroth point to NULL for error detection.')
ntnmr.SetDataPoint(1, [0,0])
print('ZG')
try:
def t(s):
print('\nStart ZG lambda')
try:
s.get_instance().ZeroAndGo()
except:
print('\nException in ZG lambda')
pass
print('\nCompletion of ZG lambda')
return
thread = threading.Thread(target=t, args=(self,))
thread.start()
except:
traceback.print_exc()
print('ZG completed')
else:
print('An Acquisition is already running')
print('An acquisition is already running')
if(CHECK_MODE == 'data'):
elapsed = 0
print('Waiting to recieve real data')
while(True):
try:
d = ntnmr.GetData()
if not(d is None):
if(d[0] != 0):
break
except:
traceback.print_exc()
time.sleep(0.1)
elapsed += 0.1
print(f'\rElapsed: {elapsed:.1f}s/{check_time:.1f}s', end='')
if(elapsed > check_time): # broken
print('\nTimeout! No data!')
ntnmr.Abort()
self.ZeroGo(lock=lock, interval=interval, check_time=check_time)
break
print('\n')
elif(CHECK_MODE == 'thread'):
print('Giving ZeroGo command a grace period to terminate...')
elapsed = 0.0
while(elapsed < 2.0):
if not(thread.is_alive()):
print('\nZeroGo terminated in time. Continuing...', end='')
break
time.sleep(0.1)
elapsed += 0.1
print(f'\rElapsed: {elapsed:.1f}s/2.0s', end='')
print('\n')
if(thread.is_alive()): # technically possible that it dies at the verrrry last moment, so may as well add an if-condition. What can I say? I'm merciful.
print('ZeroGo did not terminate. This is a sign of an error. Retrying...')
# the thread still hasn't died - this is a sign that the ZeroGo got caught up with some sort of error. Abandon, and retry.
ntnmr.Abort()
self.ZeroGo(lock=lock, interval=interval, check_time=check_time)
if lock:
print("Application locked during acquisition\n...waiting...")
print("Application locked during acquisition. Waiting...")
while self.acquisition_running():
time.sleep(interval)
# TODO: https://stackoverflow.com/questions/27586411/how-do-i-close-window-with-handle-using-win32gui-in-python to close any tecmag dialogues that show up. Need to determine proper search string, so next time it pops up, run some tests.
@@ -168,7 +232,7 @@ class TNMR:
False: if not running
"""
ntnmr = self.get_instance()
res = not(ntnmr.CheckAcquisition)
res = not(ntnmr.CheckAcquisition())
return res
def get_data(self):
@@ -178,7 +242,7 @@ class TNMR:
-------
a tuple of ([real_array], [imaginary_array])
'''
raw_data = self.get_instance().GetData
raw_data = self.get_instance().GetData()
reals = raw_data[::2]
imags = raw_data[1::2]
@@ -314,7 +378,7 @@ class TNMR:
ntnmr = self.get_instance()
print(f'Loading sequence at {filename}')
ntnmr.CloseActiveFile
ntnmr.CloseActiveFile()
success = ntnmr.OpenFile(TEMPLATE_FILE_PATH + 'tmp.tnt')
if(success):
print('Template file reloaded')