# -*- coding: utf-8 -*-
"""
This is a set of scripts that pretend to be an xepr spectromter, this allows for the further development of
this code without having to be sat in front of an actual spectrometer:
Manufactuer: Hugo Karas (me)
Model: DUMMY
Features: The most relibable Bruker EPR spectromter in the world (possibly?)
"""
import re
import numpy as np
import deerlab as dl
import time
import os
import random as rand
from .XeprAPI_link import XeprAPILink,dataset
import logging
[docs]
hw_log = logging.getLogger('hardware.dummy')
rand.seed(1212)
[docs]
class dummy_api(XeprAPILink):
def __init__(self) -> None:
pass
[docs]
def find_Xepr(self):
self.Xepr = dummy_xepr()
pass
[docs]
def is_exp_running(self):
# Since all experiments are instantaneous this has to be always false
return False
[docs]
def acquire_scan(self):
return self.acquire_dataset()
[docs]
def acquire_scan_at(self, scan_num: int):
return self.acquire_dataset()
[docs]
def acquire_scan_2d(self):
return self.acquire_dataset()
[docs]
def compile_PulseSpel_def(self):
pass
[docs]
def compile_PulseSpel_prg(self):
pass
[docs]
def set_PulseSpel_var(self, variable: str, value: int):
# This should be expanded into a dictionary that can then effect the simulation. In an ideal world
pass
[docs]
def run_exp(self):
hw_log.info('Experiment started')
pass
[docs]
def stop_exp(self):
hw_log.info('Experiment stopped')
pass
[docs]
def abort_exp(self):
hw_log.info('Experiment aborted')
pass
[docs]
class dummy_cur_exp:
def __init__(self):
# if experiment == "DEER_quick":
# print("Experiment set to: DEER_quick")
# self.NbScansToDo = dummy_param(0,10000,int,112)
# self.SweepsPExp = dummy_param(0,10000,int,112) # This is the same as scans in a 1D experiment
# self.ShotsPLoop = dummy_param(0,10000,int,4) # This is the same as shots per point
# self.XSpecRes = dummy_param(0,10000,int,256) # This is the length of the X direction
# self.NbScansDone = dummy_param(0,self.NbScansToDo,int,0) # Intially zero scans have been done
# self.FTAcqModeSlct = dummy_param(None,None,str,'Run from PulseSPEL')# Setting teh acquistion mode
# self.PlsSPELPrgPaF = dummy_param(None,None,str,'rand_path') # Pulse Spel Experiment Path
# self.PlsSPELGlbPaF = dummy_param(None,None,str,'rand_path') # Pulse Spel Definitions Path
# self.PlsSPELLISTSlct = dummy_param(None,None,str,'Phase Cycle') # Selecting the phase cycling
# self.PlsSPELEXPSlct = dummy_param(None,None,str,'Experiment') # Selecting the Experiment
# self.ReplaceMode = dummy_param(None,None,bool,False) # Setting the replace mode state
# elif experiment == "DEER_std":#TODO
# print("Experiment set to: DEER_std")
# self.NbScansToDo = dummy_param(0,10000,int,2000)
# self.SweepsPExp = dummy_param(0,10000,int,112)
# elif experiment == "2D_DEC":#TODO
# print("Experiment set to 2D Dec")
[docs]
self.ShotRepTime = dummy_param(0,100000,int,6000)
[docs]
self.NbScansToDo = dummy_param(0,10000,int,112)
[docs]
self.SweepsPExp = dummy_param(0,10000,int,112) # This is the same as scans in a 1D experiment
[docs]
self.ShotsPLoop = dummy_param(0,10000,int,4) # This is the same as shots per point
[docs]
self.XSpecRes = dummy_param(0,10000,int,256) # This is the length of the X direction
[docs]
self.NbScansDone = dummy_param(0,self.NbScansToDo,int,0) # Intially zero scans have been done
[docs]
self.FTAcqModeSlct = dummy_param(None,None,str,'Run from PulseSPEL')# Setting teh acquistion mode
[docs]
self.PlsSPELPrgPaF = dummy_param(None,None,str,'rand_path') # Pulse Spel Experiment Path
[docs]
self.PlsSPELGlbPaF = dummy_param(None,None,str,'rand_path') # Pulse Spel Definitions Path
[docs]
self.PlsSPELLISTSlct = dummy_param(None,None,str,'Phase Cycle') # Selecting the phase cycling
[docs]
self.PlsSPELEXPSlct = dummy_param(None,None,str,'Experiment') # Selecting the Experiment
[docs]
self.ReplaceMode = dummy_param(None,None,bool,False) # Setting the replace mode state
[docs]
self.CenterField = dummy_param(0,15000,int)
[docs]
self.FrequencyMon = dummy_param(33,35,float)
[docs]
self.SweepWidth = dummy_param(0,10000,int,300)
[docs]
def __getitem__(self, name):
"""This method returns a given parameter class for the name provided
:param name: The name of parameter needed
:type name: string
:return: Instance of dummy_param class corresponding to the searched name
:rtype: dummy_param class
"""
if '.' in name:
# This removes the category allowing the paramter class to be found
name = name.split('.')[1]
return(getattr(self,name))
[docs]
def getParam(self,name:str):
"""Extracts a paremeter from the current exeperiment
:param name: The name of the parameter to be extracted
:type name: string
:return: An instance of the parameter class for the given paramter name
:rtype: parameter Class
"""
return getattr(self,name)
[docs]
def aqExpRun(self):
#Dummy function, for running the experiment
print('Dummy Experiment running')
pass
[docs]
class dummy_param:
def __init__(self,min,max,type=int,par=None):
[docs]
self.aqGetParMaxValue = max
[docs]
self.aqGetParMinValue = min
if (par == None) & (type == int): # If no value is given a random one is picked
self.val = rand.randint(self.aqGetParMinValue,self.aqGetParMaxValue)
@property
[docs]
def value(self):
return self.val
@value.setter
def value(self, par):
self.val = par
[docs]
class dummy_xepr:
def __init__(self) -> None:
[docs]
self.cur_exp = dummy_cur_exp()
[docs]
self.hidden = dummy_hidden()
pass
[docs]
def XeprExperiment(self,hidden=None):
if hidden == None:
# return dummy_cur_exp("DEER_quick")
return self.cur_exp
elif hidden == "AcqHidden":
return self.hidden
[docs]
def XeprDataset(self): # Returns the current dataset
cur_exp = self.cur_exp
hidden = self.hidden
# Generates simulated DEER data
def generateDEER(num_points,max_time):
"""
Modified from an example in DEERlab.
Copyright 2019-2021, Luis Fábregas Ibáñez, Stefan Stoll, and others.
"""
t = np.linspace(0,max_time/1000,num_points) # time axis, µs
r = np.linspace(2,5,200) # distance axis, nm
param = [3, 0.1, 0.2, 3.5, 0.1, 0.65, 3.8, 0.05, 0.15] # parameters for three-Gaussian model
P = dl.dd_gauss3(r,param) # model distance distribution
lam = 0.3 # modulation depth
B = dl.bg_hom3d(t,300,lam) # background decay
K = dl.dipolarkernel(t,r,mod=lam,bg=B) # kernel matrix
Vexp = K@P + dl.whitegaussnoise(t,0.01,seed=0) # DEER signal with added noise
return t, Vexp
def generateCarrPurcell(num_points,max_time):
t = np.linspace(0,4000,256)
V = np.exp(-((t*3e-3)/(4.2))**4)
noise = np.random.normal(0, .05, V.shape) *np.array([1+1j])
Vexp = V + noise
return t, Vexp
def generate2D(numpoints,max_time):
t = np.linspace(0,max_time,numpoints)
X, Y = np.meshgrid(t, t)
def fun_2d(x,y):
return (np.exp(-((x*2.8e-3)/(4.2))**4) + np.exp(-((y*2.8e-3)/(4.2))**4))
Z = fun_2d(X,Y)
noise = np.random.normal(0, .05, Z.shape) *np.array([1+1j])
return [t,t],Z+noise
def generate_hahn_echo_transient(numpoints,phase,phase_offset=0.2):
def gaussian(t,a,b,c,phase):
return a * np.exp(-(t-b)**2/c) * np.exp(-1j*(phase+phase_offset))
t = np.arange(0,numpoints,1)
noise = np.random.normal(loc=0.0,scale=0.1,size=t.shape) + 1j * np.random.normal(loc=0.0,scale=0.03,size=t.shape)
V = gaussian(t,1,numpoints/2,numpoints*2,phase) + noise
return t,V
def generate_phase_experiment(numpoints,pg,phase,phase_offset=0.2):
vals = np.zeros(numpoints)
vals = vals + 1j* vals
t = range(0,numpoints)
for i in t:
ta,V = generate_hahn_echo_transient(pg,phase,phase_offset)
vals[i,] = np.trapz(V,x=ta)
return t, vals
if cur_exp.PlsSPELEXPSlct.value == 'DEER':
t,V = generateDEER(250,4000)
return dummy_dataset(t,V)
elif cur_exp.PlsSPELEXPSlct.value =='2D Dec. 64':
t,V = generate2D(64,4000)
return dummy_dataset(t,V)
elif cur_exp.PlsSPELEXPSlct.value =='Carr Purcell exp':
t,V = generateCarrPurcell(250,4000)
return dummy_dataset(t,V)
elif cur_exp.PlsSPELEXPSlct.value =='tau 2 scan':
t,V = generateCarrPurcell(250,4000)
return dummy_dataset(t,V)
elif cur_exp.PlsSPELEXPSlct.value == 'Hahn Echo':
def phases(x,range):
m = 6/range *np.pi
c = -3*np.pi
return m*x + c
if cur_exp.PlsSPELLISTSlct.value == 'MainPhase':
phase_setting = hidden['SignalPhase'].value
phase = phases(phase_setting,hidden['SignalPhase'].aqGetParMaxValue)
elif cur_exp.PlsSPELLISTSlct.value in ['BrXPhase','BrYPhase','MinBrXPhase','MinBrYPhase']:
phase_setting = hidden[cur_exp.PlsSPELLISTSlct.value].value
phase = phases(phase_setting,hidden[cur_exp.PlsSPELLISTSlct.value].aqGetParMaxValue)
else:
print("Sorry, This can not be simulated yet, returning Main Phase")
phase_setting = hidden['SignalPhase']
phase = phases(phase_setting,hidden['SignalPhase'].aqGetParMaxValue())
t,V = generate_phase_experiment(10,512,phase)
return dummy_dataset(t,V)
else:
print("Sorry, This can not be simulated yet, returning white noise")
t = np.linspace(0,2500,250)
noise = np.random.normal(0, .1, t.shape) *np.array([1+1j])
return dummy_dataset(t,noise)
[docs]
class XeprCmds:
[docs]
def aqPgShowPrg():
# Opens the Pulse Spel Panel, obvs does nothing
pass
[docs]
def aqPgCompile():
# Compiles Pulse Spel, obvs does nothing
pass
[docs]
def aqExpSelect(exp):
# Would normally select the experiment, dummy does nothing
pass
[docs]
def aqPgLoad(filepath):
# Loads the pulse spel program, does nothing
pass
[docs]
def aqPgDefLoad(filepath):
# Loas the pulse spel definitions file, does nothing
pass
[docs]
def aqPgDefSaveAs(path):
pass
[docs]
def aqPgSaveAs(path):
pass
[docs]
def vpSave(viewpoint:str,title:str,path:str):
pass
[docs]
class dummy_hidden:
# This is a dummy version of the hidden class containing the extra current experiment features
def __init__(self) -> None:
[docs]
self.SignalPhase = dummy_param(0,4095,type=int,par=rand.randint(0,4096))
[docs]
self.BrXPhase = dummy_param(0,100,type=float,par=rand.random())
[docs]
self.BrYPhase = dummy_param(0,100,type=float,par=rand.random())
[docs]
self.MinBrXPhase = dummy_param(0,100,type=float,par=rand.random())
[docs]
self.MinBrYPhase = dummy_param(0,100,type=float,par=rand.random())
[docs]
self.BrXAmp = dummy_param(0,100,type=float,par=rand.random())
[docs]
self.BrYAmp = dummy_param(0,100,type=float,par=rand.random())
[docs]
self.MinBrXAmp = dummy_param(0,100,type=float,par=rand.random())
[docs]
self.MinBrYAmp = dummy_param(0,100,type=float,par=rand.random())
pass
[docs]
def __getitem__(self, name):
"""This method returns a given parameter class for the name provided
:param name: The name of parameter needed
:type name: string
:return: Instance of dummy_param class corresponding to the searched name
:rtype: dummy_param class
"""
if '.' in name:
# This removes the category allowing the paramter class to be found
name = name.split('.')[1]
return(getattr(self,name))
[docs]
def getParam(self,name:str):
"""Extracts a paremeter from the current exeperiment
:param name: The name of the parameter to be extracted
:type name: string
:return: An instance of the parameter class for the given paramter name
:rtype: parameter Class
"""
return getattr(self,name)
[docs]
class dummy_dataset:
def __init__(self,t,V) -> None:
[docs]
self.size = np.shape(V)
if len(self.size) == 1:
self.X = t
elif len(self.size) == 2:
self.X = t[0]
self.Y = t[1]