Surface NMR processing and inversion GUI
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mrsurvey.py 126KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455
  1. from PyQt5.QtCore import *
  2. import numpy as np
  3. import scipy.signal as signal
  4. import pylab
  5. import sys
  6. import scipy
  7. import copy
  8. import struct
  9. from scipy.io.matlab import mio
  10. from numpy import pi
  11. from math import floor
  12. import matplotlib as mpl
  13. from matplotlib.ticker import FuncFormatter
  14. import matplotlib.font_manager as fm
  15. import matplotlib.pyplot as plt
  16. import matplotlib.ticker
  17. from matplotlib.ticker import MaxNLocator
  18. import multiprocessing
  19. import itertools
  20. import akvo.tressel.adapt as adapt
  21. #import akvo.tressel.cadapt as adapt # cython for more faster
  22. import akvo.tressel.decay as decay
  23. import akvo.tressel.pca as pca
  24. import akvo.tressel.rotate as rotate
  25. import akvo.tressel.cmaps as cmaps
  26. import cmocean # colormaps for geophysical data
  27. plt.register_cmap(name='viridis', cmap=cmaps.viridis)
  28. plt.register_cmap(name='inferno', cmap=cmaps.inferno)
  29. plt.register_cmap(name='inferno_r', cmap=cmaps.inferno_r)
  30. plt.register_cmap(name='magma', cmap=cmaps.magma)
  31. plt.register_cmap(name='magma_r', cmap=cmaps.magma_r)
  32. def loadGMRBinaryFID( rawfname, istack, info ):
  33. """ Reads a single binary GMR file and fills into DATADICT
  34. """
  35. #################################################################################
  36. # figure out key data indices
  37. # Pulse
  38. nps = (int)((info["prePulseDelay"])*info["samp"])
  39. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  40. # Data
  41. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  42. nd1 = (int)(1.*self.samp) # samples in first pulse
  43. invGain = 1./self.RxGain
  44. invCGain = self.CurrentGain
  45. pulse = "Pulse 1"
  46. chan = self.DATADICT[pulse]["chan"]
  47. rchan = self.DATADICT[pulse]["rchan"]
  48. rawFile = open( rawfname, 'rb')
  49. T = N_samp * self.dt
  50. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  51. for ipm in range(self.nPulseMoments):
  52. buf1 = rawFile.read(4)
  53. buf2 = rawFile.read(4)
  54. N_chan = struct.unpack('>i', buf1 )[0]
  55. N_samp = struct.unpack('>i', buf2 )[0]
  56. DATA = np.zeros([N_samp, N_chan+1])
  57. for ichan in range(N_chan):
  58. DATADUMP = rawFile.read(4*N_samp)
  59. for irec in range(N_samp):
  60. DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
  61. return DATA, TIMES
  62. class SNMRDataProcessor(QObject):
  63. """ Revised class for preprocessing sNMR Data.
  64. Derived types can read GMR files
  65. """
  66. def __init__(self):
  67. QObject.__init__(self)
  68. self.numberOfMoments = 0
  69. self.numberOfPulsesPerMoment = 0
  70. self.pulseType = "NONE"
  71. self.transFreq = 0
  72. self.pulseLength = np.zeros(1)
  73. self.nPulseMoments = 0
  74. self.dt = 0
  75. def mfreqz(self, b,a=1):
  76. """ Plots the frequency response of a filter specified with a and b weights
  77. """
  78. import scipy.signal as signal
  79. pylab.figure(124)
  80. w,h = signal.freqz(b,a)
  81. w /= max(w)
  82. w *= .5/self.dt
  83. h_dB = 20 * pylab.log10 (abs(h))
  84. pylab.subplot(211)
  85. #pylab.plot(w/max(w),h_dB)
  86. pylab.plot(w,h_dB)
  87. pylab.ylim(-150, 5)
  88. pylab.ylabel('Magnitude (dB)')
  89. #pylab.xlabel(r'Normalized Frequency (x$\pi$rad/sample)')
  90. pylab.xlabel(r'Hz')
  91. pylab.title(r'Frequency response')
  92. pylab.subplot(212)
  93. h_Phase = pylab.unwrap(pylab.arctan2(pylab.imag(h), pylab.real(h)))
  94. #pylab.plot(w/max(w),h_Phase)
  95. pylab.plot(w,h_Phase)
  96. pylab.ylabel('Phase (radians)')
  97. pylab.xlabel(r'Hz')
  98. #pylab.xlabel(r'Normalized Frequency (x$\pi$rad/sample)')
  99. pylab.title(r'Phase response')
  100. pylab.subplots_adjust(hspace=0.5)
  101. def mfreqz2(self, b, a, canvas):
  102. "for analysing filt-filt"
  103. import scipy.signal as signal
  104. canvas.reAx2(False,False)
  105. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  106. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  107. #canvas.ax2.tick_params(axis='both', which='minor', labelsize=6)
  108. #pylab.figure(124)
  109. w,h = signal.freqz(b,a)
  110. w /= max(w)
  111. w *= .5/self.dt
  112. h_dB = 20 * pylab.log10(abs(h*h) + 1e-16)
  113. #ab.subplot(211)
  114. #pylab.plot(w/max(w),h_dB)
  115. canvas.ax1.plot(w,h_dB)
  116. canvas.ax1.set_ylim(-150, 5)
  117. canvas.ax1.set_ylabel('Magnitude [db]', fontsize=8)
  118. #pylab.xlabel(r'Normalized Frequency (x$\pi$rad/sample)')
  119. canvas.ax1.set_xlabel(r'[Hz]', fontsize=8)
  120. canvas.ax1.set_title(r'Frequency response', fontsize=8)
  121. canvas.ax1.grid(True)
  122. tt = np.arange(0, .02, self.dt)
  123. impulse = signal.dimpulse((self.filt_z, self.filt_p, self.filt_k, self.dt), t=tt)
  124. #impulse = signal.dstep((self.filt_z, self.filt_p, self.filt_k, self.dt), t=tt)
  125. #print impulse
  126. #for ii in range(len(impulse[1])):
  127. impulse_dB = 20.*np.log10(np.abs(np.array(impulse[1][0])))
  128. #canvas.ax2.plot(np.array(impulse[0]), impulse_dB)
  129. canvas.ax2.plot(np.array(impulse[0]), impulse[1][0])
  130. #h_Phase = pylab.unwrap(pylab.arctan2(pylab.imag(h), pylab.real(h)))
  131. #canvas.ax2.plot(w,h_Phase)
  132. canvas.ax2.set_ylabel('response [%]', fontsize=8)
  133. canvas.ax2.set_xlabel(r'time [s]', fontsize=8)
  134. canvas.ax2.set_title(r'impulse response', fontsize=8)
  135. #canvas.ax2.grid(True)
  136. canvas.draw()
  137. # search for last
  138. return impulse #[np.where(impulse[1][0] > .01)[-1]]
  139. class GMRDataProcessor(SNMRDataProcessor):
  140. # slots
  141. progressTrigger = pyqtSignal("int")
  142. doneTrigger = pyqtSignal()
  143. enableDSPTrigger = pyqtSignal()
  144. updateProcTrigger = pyqtSignal()
  145. def __init__(self):
  146. SNMRDataProcessor.__init__(self)
  147. self.maxBusV = 0.
  148. self.samp = 50000. # sampling frequency
  149. self.dt = 2e-5 # sampling rate
  150. self.deadTime = .0055 # instrument dead time before measurement
  151. self.prePulseDelay = 0.05 # delay before pulse
  152. self.windead = 0. # FD window filter dead time
  153. self.pulseType = -1
  154. self.transFreq = -1
  155. self.maxBusV = -1
  156. self.pulseLength = -1
  157. self.interpulseDelay = -1 # for T2, Spin Echo
  158. self.repetitionDelay = -1 # delay between first pulse
  159. self.nPulseMoments = -1 # Number of pulse moments per stack
  160. self.TuneCapacitance = -1 # tuning capac in uF
  161. self.nTransVersion = -1 # Transmitter version
  162. self.nDAQVersion = -1 # DAQ software version
  163. self.nInterleaves = -1 # num interleaves
  164. # self.nReceiveChannels = 4 # Num receive channels
  165. self.RotatedAmplitude = False
  166. # self.DATA = np.zeros(1) # Numpy array to hold all data, dimensions resized based on experiment
  167. # self.PULSES = np.zeros(1) # Numpy array to hold all data, dimensions resized based on experiment
  168. def Print(self):
  169. print ("pulse type", self.pulseType)
  170. print ("maxBusV", self.maxBusV)
  171. print ("inner pulse delay", self.interpulseDelay)
  172. print ("tuning capacitance", self.TuneCapacitance)
  173. print ("sampling rate", self.samp)
  174. print ("dt", self.dt)
  175. print ("dead time", self.deadTime)
  176. print ("pre pulse delay", self.prePulseDelay)
  177. print ("number of pulse moments", self.nPulseMoments)
  178. print ("pulse Length", self.pulseLength)
  179. print ("trans freq", self.transFreq)
  180. def readHeaderFile(self, FileName):
  181. HEADER = np.loadtxt(FileName)
  182. pulseTypeDict = {
  183. 1 : lambda: "FID",
  184. 2 : lambda: "T1",
  185. 3 : lambda: "SPINECHO",
  186. 4 : lambda: "4PhaseT1"
  187. }
  188. pulseLengthDict = {
  189. 1 : lambda x: np.ones(1) * x,
  190. 2 : lambda x: np.ones(2) * x,
  191. 3 : lambda x: np.array([x, 2.*x]),
  192. 4 : lambda x: np.ones(2) * x
  193. }
  194. self.pulseType = pulseTypeDict.get((int)(HEADER[0]))()
  195. self.transFreq = HEADER[1]
  196. self.maxBusV = HEADER[2]
  197. self.pulseLength = pulseLengthDict.get((int)(HEADER[0]))(1e-3*HEADER[3])
  198. self.interpulseDelay = 1e-3*HEADER[4] # for T2, Spin Echo
  199. self.repetitionDelay = HEADER[5] # delay between first pulse
  200. self.nPulseMoments = (int)(HEADER[6]) # Number of pulse moments per stack
  201. self.TuneCapacitance = HEADER[7] # tuning capacitance in uF
  202. self.nTransVersion = HEADER[8] # Transmitter version
  203. self.nDAQVersion = HEADER[9] # DAQ software version
  204. self.nInterleaves = HEADER[10] # num interleaves
  205. self.gain()
  206. # default
  207. self.samp = 50000. # sampling frequency
  208. self.dt = 2e-5 # sampling rate
  209. # newer header files contain 64 entries
  210. if self.nDAQVersion >= 2:
  211. #self.deadtime = HEADER[11]
  212. #self.unknown = HEADER[12]
  213. #self.PreAmpGain = HEADER[13]
  214. self.samp = HEADER[14] # sampling frequency
  215. self.dt = 1./self.samp # sampling rate
  216. self.deadTime = .0055 # instrument dead time before measurement
  217. self.prePulseDelay = 0.05 # delay before pulse
  218. #exit()
  219. def gain(self):
  220. #######################################################
  221. # Circuit gain
  222. # From MRSMatlab
  223. w = 2*np.pi*self.transFreq
  224. # 1e6 due to uF of reported capacitance
  225. L_coil = 1e6/(self.TuneCapacitance*(w**2))
  226. R_coil = 1.
  227. Z1_in = .5 + 1j*.5*w
  228. Z2_in = 1./(1j*w*.000001616)
  229. Z_eq_inv = (1./Z1_in) + (1./Z2_in)
  230. Zeq = 1./Z_eq_inv
  231. Zsource = R_coil + 1j*w*L_coil
  232. voltage_in = Zeq / (Zsource + Zeq)
  233. self.circuitGain = np.abs(voltage_in)
  234. self.circuitPhase_deg = (180/np.pi)+np.angle(voltage_in)
  235. circuitImpedance_ohms = np.abs(Zsource + Zeq)
  236. ######################################################
  237. # PreAmp gain
  238. if self.nTransVersion == 4:
  239. self.PreAmpGain = 1000.
  240. elif self.nTransVersion == 1 or self.nTransVersion == 2 or self.nTransVersion == 3 or self.nTransVersion == 6:
  241. self.PreAmpGain = 500.
  242. else:
  243. print ("unsupported transmitter version")
  244. exit(1)
  245. # Total Receiver Gain
  246. self.RxGain = self.circuitGain * self.PreAmpGain
  247. #####################################################
  248. # Current gain
  249. if floor(self.nDAQVersion) == 1:
  250. self.CurrentGain = 150.
  251. elif floor(self.nDAQVersion) == 2:
  252. self.CurrentGain = 180.
  253. def updateProgress(self):
  254. pass
  255. def TDSmartStack(self, outlierTest, MADcutoff, canvas):
  256. Stack = {}
  257. # align for stacking and modulate
  258. for pulse in self.DATADICT["PULSES"]:
  259. stack = np.zeros(( len(self.DATADICT[pulse]["chan"]), self.DATADICT["nPulseMoments"],\
  260. len(self.DATADICT["stacks"]), len(self.DATADICT[pulse]["TIMES"]) ))
  261. for ipm in range(self.DATADICT["nPulseMoments"]):
  262. istack = 0
  263. for sstack in self.DATADICT["stacks"]:
  264. if self.pulseType == "FID" or pulse == "Pulse 2":
  265. mod = (-1)**(ipm%2) * (-1)**(sstack%2)
  266. elif self.pulseType == "4PhaseT1":
  267. mod = (-1)**(ipm%2) * (-1)**(((sstack-1)/2)%2)
  268. ichan = 0
  269. for chan in self.DATADICT[pulse]["chan"]:
  270. stack[ichan,ipm,istack,:] += mod*self.DATADICT[pulse][chan][ipm][sstack]
  271. ichan += 1
  272. istack += 1
  273. Stack[pulse] = stack
  274. #########################################
  275. # simple stack and plot of simple stack #
  276. #########################################
  277. canvas.reAxH2(np.shape(stack)[0], False, False)
  278. axes = canvas.fig.axes
  279. SimpleStack = {}
  280. VarStack = {}
  281. for pulse in self.DATADICT["PULSES"]:
  282. SimpleStack[pulse] = {}
  283. VarStack[pulse] = {}
  284. ichan = 0
  285. for chan in self.DATADICT[pulse]["chan"]:
  286. SimpleStack[pulse][chan] = 1e9*np.average( Stack[pulse][ichan], 1 )
  287. VarStack[pulse][chan] = 1e9*np.std( Stack[pulse][ichan], 1 )
  288. ax1 = axes[ 2*ichan ]
  289. #ax1.get_yaxis().get_major_formatter().set_useOffset(False)
  290. y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
  291. ax1.yaxis.set_major_formatter(y_formatter)
  292. ax1.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( SimpleStack[pulse][chan], 0 )) #, color='darkblue' )
  293. ax1.set_title("Ch." + str(chan) + ": avg FID", fontsize=8)
  294. ax1.set_xlabel(r"time (ms)", fontsize=8)
  295. if ichan == 0:
  296. ax1.set_ylabel(r"signal [nV]", fontsize=8)
  297. else:
  298. plt.setp(ax1.get_yticklabels(), visible=False)
  299. plt.setp(ax1.get_yaxis().get_offset_text(), visible=False)
  300. # if ichan == 1:
  301. # canvas.ax2.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( SimpleStack[pulse][chan], 0 ), color='darkblue' )
  302. # canvas.ax2.set_title("Ch." + str(chan) + ": total average FID", fontsize=8)
  303. # canvas.ax2.set_xlabel(r"time [ms]", fontsize=8)
  304. # if ichan == 2:
  305. # canvas.ax3.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( SimpleStack[pulse][chan], 0 ), color='darkblue' )
  306. # canvas.ax3.set_title("Ch." + str(chan) + ": total average FID", fontsize=8)
  307. # canvas.ax3.set_xlabel(r"time [ms]", fontsize=8)
  308. # if ichan == 3:
  309. # canvas.ax4.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( SimpleStack[pulse][chan], 0 ), color='darkblue' )
  310. # canvas.ax4.set_title("Ch." + str(chan) + ": total average FID", fontsize=8)
  311. # canvas.ax4.set_xlabel(r"time [ms]", fontsize=8)
  312. ichan += 1
  313. #########################
  314. # Oulier rejectig stack #
  315. #########################
  316. if outlierTest == "MAD":
  317. MADStack = {}
  318. VarStack = {}
  319. #1.4826 is assumption of gaussian noise
  320. madstack = np.zeros(( len(self.DATADICT[pulse]["chan"]),\
  321. self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"]) ))
  322. varstack = np.zeros(( len(self.DATADICT[pulse]["chan"]),\
  323. self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"]) ))
  324. for pulse in self.DATADICT["PULSES"]:
  325. MADStack[pulse] = {}
  326. VarStack[pulse] = {}
  327. ichan = 0
  328. for chan in self.DATADICT[pulse]["chan"]:
  329. ax1 = axes[ 2*ichan ]
  330. for ipm in range(self.DATADICT["nPulseMoments"]):
  331. # # brutal loop over time, can this be vectorized?
  332. # for it in range(len(self.DATADICT[pulse]["TIMES"])):
  333. # x = 1e9 *Stack[pulse][ichan,ipm,:,it]
  334. # MAD = 1.4826 * np.median( np.abs(x-np.median(x)) )
  335. # good = 0
  336. # for istack in self.DATADICT["stacks"]:
  337. # if (np.abs(x[istack-1]-np.median(x))) / MAD < 2:
  338. # good += 1
  339. # madstack[ ichan, ipm, it ] += x[istack-1]
  340. # else:
  341. # pass
  342. # madstack[ichan, ipm, it] /= good
  343. # percent = int(1e2* (float)(ipm) / (float)(self.DATADICT["nPulseMoments"]) )
  344. # self.progressTrigger.emit(percent)
  345. # Vectorized version of above...much, much faster
  346. x = 1e9*copy.deepcopy(Stack[pulse][ichan][ipm,:,:]) # stack and time indices
  347. tile_med = np.tile( np.median(x, axis=0), (np.shape(x)[0],1))
  348. MAD = MADcutoff * np.median(np.abs(x - tile_med), axis=0)
  349. tile_MAD = np.tile( MAD, (np.shape(x)[0],1))
  350. good = np.abs(x-tile_med)/tile_MAD < 2. # 1.4826 # 2
  351. madstack[ichan][ipm] = copy.deepcopy( np.ma.masked_array(x, good != True).mean(axis=0) )
  352. varstack[ichan][ipm] = copy.deepcopy( np.ma.masked_array(x, good != True).std(axis=0) )
  353. # reporting
  354. percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) /
  355. (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
  356. self.progressTrigger.emit(percent)
  357. ax1.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( madstack[ichan], 0 ))# , color='darkred')
  358. MADStack[pulse][chan] = madstack[ichan]
  359. VarStack[pulse][chan] = varstack[ichan]
  360. ichan += 1
  361. self.DATADICT["stack"] = MADStack
  362. else:
  363. self.DATADICT["stack"] = SimpleStack
  364. #########################################
  365. # Plot Fourier Transform representation #
  366. #########################################
  367. # canvas.fig.subplots_adjust(right=0.8)
  368. # cbar_ax = canvas.fig.add_axes([0.85, 0.1, 0.015, 0.355])
  369. # cbar_ax.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  370. im2 = []
  371. im1 = []
  372. for pulse in self.DATADICT["PULSES"]:
  373. ichan = 0
  374. axes = canvas.fig.axes
  375. vvmin = 1e10
  376. vvmax = 0
  377. for chan in self.DATADICT[pulse]["chan"]:
  378. ax1 = axes[2*ichan ]
  379. ax2 = axes[2*ichan+1] # TODO fix hard coded number
  380. if outlierTest == "MAD":
  381. X = np.fft.rfft( MADStack[pulse][chan][0,:] )
  382. nu = np.fft.fftfreq(len( MADStack[pulse][chan][0,:]), d=self.dt)
  383. else:
  384. X = np.fft.rfft( SimpleStack[pulse][chan][0,:] )
  385. nu = np.fft.fftfreq(len( SimpleStack[pulse][chan][0,:]), d=self.dt)
  386. nu = nu[0:len(X)]
  387. nu[-1] = np.abs(nu[-1])
  388. df = nu[1] - nu[0]
  389. of = 0
  390. istart = int((self.transFreq-50.)/df)
  391. iend = int((self.transFreq+50.)/df)
  392. of = nu[istart]
  393. def freqlabel(xxx, pos):
  394. return '%1.0f' %(of + xxx*df)
  395. formatter = FuncFormatter(freqlabel)
  396. SFFT = np.zeros( (self.DATADICT["nPulseMoments"], len(X)), dtype=np.complex64 )
  397. SFFT[0,:] = X
  398. for ipm in range(1, self.DATADICT["nPulseMoments"]):
  399. if outlierTest == "MAD":
  400. SFFT[ipm,:] = np.fft.rfft( MADStack[pulse][chan][ipm,:] )
  401. else:
  402. SFFT[ipm,:] = np.fft.rfft( SimpleStack[pulse][chan][ipm,:] )
  403. # convert to dB and add colorbars
  404. #db = 20.*np.log10(np.abs(SFFT[:,istart:iend]))
  405. db = (np.abs(SFFT[:,istart:iend]))
  406. #db = (np.real(SFFT[:,istart:iend]))
  407. #dbr = (np.real(SFFT[:,istart:iend]))
  408. #db = (np.imag(SFFT[:,istart:iend]))
  409. vvmin = min(vvmin, np.min (db))
  410. vvmax = max(vvmax, np.max (db))
  411. im2.append(ax2.matshow( db, aspect='auto', cmap=cmocean.cm.ice, vmin=vvmin, vmax=vvmax))
  412. #im1.append(ax1.matshow( dbr, aspect='auto')) #, vmin=vvmin, vmax=vvmax))
  413. #im2.append(ax2.matshow( db, aspect='auto', vmin=vvmin, vmax=vvmax))
  414. #im2 = ax2.matshow( db, aspect='auto', cmap=cmocean.cm.ice, vmin=vvmin, vmax=vvmax)
  415. if ichan == 0:
  416. ax2.set_ylabel(r"$q$ (A $\cdot$ s)", fontsize=8)
  417. else:
  418. #ax2.yaxis.set_ticklabels([])
  419. plt.setp(ax2.get_yticklabels(), visible=False)
  420. ax2.xaxis.set_major_formatter(formatter)
  421. ax2.xaxis.set_ticks_position('bottom')
  422. ax2.xaxis.set_major_locator(MaxNLocator(3))
  423. y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
  424. ax2.yaxis.set_major_formatter(y_formatter)
  425. #if chan == self.DATADICT[pulse]["chan"][-1]:
  426. #cb2 = canvas.fig.colorbar(im2, cax=cbar_ax, format='%1.0e')
  427. #cb2 = canvas.fig.colorbar(im2[0], ax=ax2, format='%1.0e', orientation='horizontal')
  428. #cb2 = canvas.fig.colorbar(im2, ax=ax2, format='%1.0e', orientation='horizontal')
  429. #cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  430. #cb2.set_label("signal (dB)", fontsize=8)
  431. ichan += 1
  432. canvas.fig.subplots_adjust(hspace=.1, wspace=.05, left=.075, right=.95 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  433. #cb1 = canvas.fig.colorbar(im, ax=axes[0::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  434. #cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  435. #cb1.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  436. cb2 = canvas.fig.colorbar(im2[-1], ax=axes[1::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  437. cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  438. cb2.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  439. #canvas.fig.tight_layout()
  440. canvas.draw()
  441. self.doneTrigger.emit()
  442. def FDSmartStack(self, outlierTest, MADcutoff, canvas):
  443. print("FFT stuff")
  444. self.dataCubeFFT()
  445. Stack = {}
  446. # align phase cycling for stacking and modulate
  447. for pulse in self.DATADICT["PULSES"]:
  448. stack = np.zeros(( len(self.DATADICT[pulse]["chan"]), \
  449. self.DATADICT["nPulseMoments"],\
  450. len(self.DATADICT["stacks"]),\
  451. len(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0] ]["FFT"]["nu"])//2 + 1),\
  452. dtype=np.complex )
  453. for ipm in range(self.DATADICT["nPulseMoments"]):
  454. istack = 0
  455. for sstack in self.DATADICT["stacks"]:
  456. if self.pulseType == "FID" or pulse == "Pulse 2":
  457. mod = (-1)**(ipm%2) * (-1)**(sstack%2)
  458. elif self.pulseType == "4PhaseT1":
  459. mod = (-1)**(ipm%2) * (-1)**(((sstack-1)/2)%2)
  460. ichan = 0
  461. for chan in self.DATADICT[pulse]["chan"]:
  462. #stack[ichan,ipm,istack,:] += mod*self.DATADICT[pulse][chan][ipm][sstack]
  463. stack[ichan,ipm,istack,:] += mod*self.DATADICT[pulse][chan]["FFT"][sstack][ipm,:]
  464. ichan += 1
  465. istack += 1
  466. Stack[pulse] = stack
  467. #########################################
  468. # simple stack and plot of simple stack #
  469. ########################################https://faculty.apps.utah.edu/#
  470. canvas.reAxH2(np.shape(stack)[0], False, False)
  471. axes = canvas.fig.axes
  472. SimpleStack = {}
  473. VarStack = {}
  474. for pulse in self.DATADICT["PULSES"]:
  475. SimpleStack[pulse] = {}
  476. VarStack[pulse] = {}
  477. ichan = 0
  478. for chan in self.DATADICT[pulse]["chan"]:
  479. SimpleStack[pulse][chan] = 1e9*np.average( Stack[pulse][ichan], 1 )
  480. VarStack[pulse][chan] = 1e9*np.std( Stack[pulse][ichan], 1 )
  481. ax1 = axes[ 2*ichan ]
  482. #ax1.get_yaxis().get_major_formatter().set_useOffset(False)
  483. y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
  484. ax1.yaxis.set_major_formatter(y_formatter)
  485. #ax1.plot( 1e3*self.DATADICT[pulse][chan]["FFT"]["nu"][0:len(SimpleStack[pulse][chan])], np.average(SimpleStack[pulse][chan], 0 )) #, color='darkblue' )
  486. #ax1.pcolor( np.real(SimpleStack[pulse][chan]) ) #, color='darkblue' )
  487. ax1.matshow( np.real(SimpleStack[pulse][chan]), aspect='auto') #, color='darkblue' )
  488. ax1.set_title("Ch." + str(chan) + ": avg FID", fontsize=8)
  489. ax1.set_xlabel(r"time (ms)", fontsize=8)
  490. if ichan == 0:
  491. ax1.set_ylabel(r"signal [nV]", fontsize=8)
  492. else:
  493. plt.setp(ax1.get_yticklabels(), visible=False)
  494. plt.setp(ax1.get_yaxis().get_offset_text(), visible=False)
  495. ichan += 1
  496. #########################
  497. # Oulier rejectig stack #
  498. #########################
  499. if outlierTest == "MAD":
  500. MADStack = {}
  501. VarStack = {}
  502. #1.4826 is assumption of gaussian noise
  503. madstack = np.zeros(( len(self.DATADICT[pulse]["chan"]),\
  504. self.DATADICT["nPulseMoments"],\
  505. len(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0] ]["FFT"]["nu"])//2 + 1))
  506. varstack = np.zeros(( len(self.DATADICT[pulse]["chan"]),\
  507. self.DATADICT["nPulseMoments"],\
  508. len(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0] ]["FFT"]["nu"])//2 + 1))
  509. for pulse in self.DATADICT["PULSES"]:
  510. MADStack[pulse] = {}
  511. VarStack[pulse] = {}
  512. ichan = 0
  513. for chan in self.DATADICT[pulse]["chan"]:
  514. ax1 = axes[ 2*ichan ]
  515. for ipm in range(self.DATADICT["nPulseMoments"]):
  516. # # brutal loop over time, can this be vectorized?
  517. # for it in range(len(self.DATADICT[pulse]["TIMES"])):
  518. # x = 1e9 *Stack[pulse][ichan,ipm,:,it]
  519. # MAD = 1.4826 * np.median( np.abs(x-np.median(x)) )
  520. # good = 0
  521. # for istack in self.DATADICT["stacks"]:
  522. # if (np.abs(x[istack-1]-np.median(x))) / MAD < 2:
  523. # good += 1
  524. # madstack[ ichan, ipm, it ] += x[istack-1]
  525. # else:
  526. # pass
  527. # madstack[ichan, ipm, it] /= good
  528. # percent = int(1e2* (float)(ipm) / (float)(self.DATADICT["nPulseMoments"]) )
  529. # self.progressTrigger.emit(percent)
  530. # Vectorized version of above...much, much faster
  531. x = 1e9*copy.deepcopy(Stack[pulse][ichan][ipm,:,:]) # stack and time indices
  532. tile_med = np.tile( np.median(x, axis=0), (np.shape(x)[0],1))
  533. MAD = MADcutoff * np.median(np.abs(x - tile_med), axis=0)
  534. tile_MAD = np.tile( MAD, (np.shape(x)[0],1))
  535. good = np.abs(x-tile_med)/tile_MAD < 2. # 1.4826 # 2
  536. madstack[ichan][ipm] = copy.deepcopy( np.ma.masked_array(x, good != True).mean(axis=0) )
  537. varstack[ichan][ipm] = copy.deepcopy( np.ma.masked_array(x, good != True).std(axis=0) )
  538. # reporting
  539. percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) /
  540. (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
  541. self.progressTrigger.emit(percent)
  542. ax2 = axes[2*ichan+1] # TODO fix hard coded number
  543. #ax1.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( madstack[ichan], 0 ))# , color='darkred')
  544. MADStack[pulse][chan] = madstack[ichan]
  545. VarStack[pulse][chan] = varstack[ichan]
  546. ax2.matshow( np.real(MADStack[pulse][chan]), aspect='auto') #, color='darkblue' )
  547. ichan += 1
  548. self.DATADICT["stack"] = MADStack
  549. else:
  550. self.DATADICT["stack"] = SimpleStack
  551. # #########################################
  552. # # Plot Fourier Transform representation #
  553. # #########################################
  554. #
  555. # # canvas.fig.subplots_adjust(right=0.8)
  556. # # cbar_ax = canvas.fig.add_axes([0.85, 0.1, 0.015, 0.355])
  557. # # cbar_ax.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  558. # im2 = []
  559. # im1 = []
  560. # for pulse in self.DATADICT["PULSES"]:
  561. # ichan = 0
  562. # axes = canvas.fig.axes
  563. # vvmin = 1e10
  564. # vvmax = 0
  565. # for chan in self.DATADICT[pulse]["chan"]:
  566. # ax1 = axes[2*ichan ]
  567. # ax2 = axes[2*ichan+1] # TODO fix hard coded number
  568. # if outlierTest == "MAD":
  569. # X = np.fft.rfft( MADStack[pulse][chan][0,:] )
  570. # nu = np.fft.fftfreq(len( MADStack[pulse][chan][0,:]), d=self.dt)
  571. # else:
  572. # X = np.fft.rfft( SimpleStack[pulse][chan][0,:] )
  573. # nu = np.fft.fftfreq(len( SimpleStack[pulse][chan][0,:]), d=self.dt)
  574. #
  575. # nu = nu[0:len(X)]
  576. # nu[-1] = np.abs(nu[-1])
  577. # df = nu[1] - nu[0]
  578. # of = 0
  579. #
  580. # istart = int((self.transFreq-50.)/df)
  581. # iend = int((self.transFreq+50.)/df)
  582. # of = nu[istart]
  583. #
  584. # def freqlabel(xxx, pos):
  585. # return '%1.0f' %(of + xxx*df)
  586. # formatter = FuncFormatter(freqlabel)
  587. #
  588. # SFFT = np.zeros( (self.DATADICT["nPulseMoments"], len(X)), dtype=np.complex64 )
  589. # SFFT[0,:] = X
  590. # for ipm in range(1, self.DATADICT["nPulseMoments"]):
  591. # if outlierTest == "MAD":
  592. # SFFT[ipm,:] = np.fft.rfft( MADStack[pulse][chan][ipm,:] )
  593. # else:
  594. # SFFT[ipm,:] = np.fft.rfft( SimpleStack[pulse][chan][ipm,:] )
  595. #
  596. # # convert to dB and add colorbars
  597. # #db = 20.*np.log10(np.abs(SFFT[:,istart:iend]))
  598. # db = (np.abs(SFFT[:,istart:iend]))
  599. # #db = (np.real(SFFT[:,istart:iend]))
  600. # #dbr = (np.real(SFFT[:,istart:iend]))
  601. # #db = (np.imag(SFFT[:,istart:iend]))
  602. #
  603. # vvmin = min(vvmin, np.min (db))
  604. # vvmax = max(vvmax, np.max (db))
  605. # im2.append(ax2.matshow( db, aspect='auto', cmap=cmocean.cm.ice, vmin=vvmin, vmax=vvmax))
  606. # #im1.append(ax1.matshow( dbr, aspect='auto')) #, vmin=vvmin, vmax=vvmax))
  607. # #im2.append(ax2.matshow( db, aspect='auto', vmin=vvmin, vmax=vvmax))
  608. # #im2 = ax2.matshow( db, aspect='auto', cmap=cmocean.cm.ice, vmin=vvmin, vmax=vvmax)
  609. # if ichan == 0:
  610. # ax2.set_ylabel(r"$q$ (A $\cdot$ s)", fontsize=8)
  611. # else:
  612. # #ax2.yaxis.set_ticklabels([])
  613. # plt.setp(ax2.get_yticklabels(), visible=False)
  614. #
  615. # ax2.xaxis.set_major_formatter(formatter)
  616. # ax2.xaxis.set_ticks_position('bottom')
  617. # ax2.xaxis.set_major_locator(MaxNLocator(3))
  618. #
  619. # y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
  620. # ax2.yaxis.set_major_formatter(y_formatter)
  621. #
  622. #
  623. # #if chan == self.DATADICT[pulse]["chan"][-1]:
  624. # #cb2 = canvas.fig.colorbar(im2, cax=cbar_ax, format='%1.0e')
  625. #
  626. # #cb2 = canvas.fig.colorbar(im2[0], ax=ax2, format='%1.0e', orientation='horizontal')
  627. # #cb2 = canvas.fig.colorbar(im2, ax=ax2, format='%1.0e', orientation='horizontal')
  628. # #cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  629. # #cb2.set_label("signal (dB)", fontsize=8)
  630. #
  631. # ichan += 1
  632. #
  633. #
  634. # canvas.fig.subplots_adjust(hspace=.1, wspace=.05, left=.075, right=.95 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  635. #
  636. # #cb1 = canvas.fig.colorbar(im, ax=axes[0::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  637. # #cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  638. # #cb1.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  639. #
  640. # cb2 = canvas.fig.colorbar(im2[-1], ax=axes[1::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  641. # cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  642. # cb2.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  643. #canvas.fig.tight_layout()
  644. canvas.draw()
  645. self.doneTrigger.emit()
  646. def sumData(self, canvas, fred):
  647. chans = copy.deepcopy(self.DATADICT[self.DATADICT["PULSES"][0]]["chan"]) #= np.array( ( self.DATADICT[pulse]["chan"][0], ) )
  648. nchan = len(chans)
  649. # Sum permutations of two channel combos
  650. for ich in range(nchan-1):
  651. for ch in chans[ich+1:]:
  652. chsum = chans[ich] + "+" + ch
  653. for pulse in self.DATADICT["PULSES"]:
  654. self.DATADICT[pulse][chsum] = {}
  655. for ipm in range(self.DATADICT["nPulseMoments"]):
  656. self.DATADICT[pulse][chsum][ipm] = {}
  657. for istack in self.DATADICT["stacks"]:
  658. self.DATADICT[pulse][chsum][ipm][istack] = self.DATADICT[pulse][chans[ich]][ipm][istack] - self.DATADICT[pulse][ch][ipm][istack]
  659. if chsum == "1+2":
  660. #self.DATADICT[pulse]["rchan"].pop()
  661. #self.DATADICT[pulse]["rchan"].pop()
  662. self.DATADICT[pulse]["chan"].append(chsum)
  663. # Sum all channels
  664. sumall = False
  665. if sumall:
  666. chsum = ""
  667. for ch in chans:
  668. chsum += ch + "+"
  669. chsum = chsum[0:-1] # remove last "+"
  670. for pulse in self.DATADICT["PULSES"]:
  671. self.DATADICT[pulse][chsum] = {}
  672. for ipm in range(self.DATADICT["nPulseMoments"]):
  673. self.DATADICT[pulse][chsum][ipm] = {}
  674. for istack in self.DATADICT["stacks"]:
  675. self.DATADICT[pulse][chsum][ipm][istack] = copy.deepcopy(self.DATADICT[pulse][chans[0]][ipm][istack])
  676. for ch in chans[1:]:
  677. self.DATADICT[pulse][chsum][ipm][istack] += self.DATADICT[pulse][ch][ipm][istack]
  678. self.DATADICT[pulse]["chan"].append(chsum)
  679. # if nchan > 2:
  680. # for ch in chans:
  681. # chsum += ch
  682. # for ch2 in chans[1::]:
  683. # for pulse in self.DATADICT["PULSES"]:
  684. # self.DATADICT[pulse][chsum] = {}
  685. # for istack in self.DATADICT["stacks"]:
  686. # self.DATADICT[pulse][chsum][ipm][istack] = self.DATADICT[pulse][chans[ich]][ipm][istack] + self.DATADICT[pulse][ch][ipm][istack]
  687. self.doneTrigger.emit()
  688. def quadDet(self, clip, phase, canvas):
  689. from scipy import signal
  690. self.RotatedAmplitude = True
  691. wL = self.transFreq * 2*np.pi
  692. vL = self.transFreq
  693. #T = 50
  694. dt = self.dt
  695. #DT = 0.01
  696. CA = {} # corrected amplitude
  697. IP = {} # instantaneous phase
  698. NR = {} # Noise residual
  699. RE = {} # Real channel
  700. IM = {} # Imaginary channel
  701. # global maximums for plotting
  702. CAmax = {}
  703. NRmax = {}
  704. REmax = {}
  705. IMmax = {}
  706. E0,phi,df,T2 = 100.,0,0,.2
  707. first = False
  708. self.sigma = {}
  709. for pulse in self.DATADICT["PULSES"]:
  710. CA[pulse] = {}
  711. IP[pulse] = {}
  712. NR[pulse] = {}
  713. RE[pulse] = {}
  714. IM[pulse] = {}
  715. CAmax[pulse] = 0
  716. NRmax[pulse] = 0
  717. REmax[pulse] = 0
  718. IMmax[pulse] = 0
  719. ichan = 0
  720. self.sigma[pulse] = {}
  721. for chan in self.DATADICT[pulse]["chan"]:
  722. CA[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  723. IP[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  724. NR[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  725. RE[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  726. IM[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  727. for ipm in range(0, self.DATADICT["nPulseMoments"]):
  728. #t = self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1]
  729. xn = self.DATADICT["stack"][pulse][chan][ipm,:]
  730. ht = signal.hilbert(xn)*np.exp(-1j*wL*self.DATADICT[pulse]["TIMES"])
  731. #############################################################
  732. # Quadrature signal
  733. RE[pulse][chan][ipm,:] = np.real(ht[clip::])
  734. IM[pulse][chan][ipm,:] = np.imag(ht[clip::])
  735. REmax[pulse] = max(REmax[pulse], np.max(np.real(ht[clip::])))
  736. IMmax[pulse] = max(IMmax[pulse], np.max(np.imag(ht[clip::])))
  737. #############################################################
  738. # Instantaneous phase
  739. IP[pulse][chan][ipm,:] = np.angle(ht)[clip::]
  740. #############################################################
  741. # Rotated amplitude
  742. #if ipm != 0:
  743. #[success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"], (E0,phi,df,T2))
  744. #[success, E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
  745. #else:
  746. [success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"])
  747. #[success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"], (E0,phi,df,T2))
  748. #[success, E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
  749. #print("success", success, "E0", E0, "phi", phi, "df", df, "T2", T2)
  750. D = self.RotateAmplitude( ht.real, ht.imag, phi, df, self.DATADICT[pulse]["TIMES"] )
  751. CA[pulse][chan][ipm,:] = D.imag[clip::] # amplitude data
  752. NR[pulse][chan][ipm,:] = D.real[clip::] # noise data
  753. CAmax[pulse] = max(CAmax[pulse], np.max(D.imag[clip::]) )
  754. NRmax[pulse] = max(NRmax[pulse], np.max(D.real[clip::]) )
  755. self.sigma[pulse][chan] = np.std(NR[pulse][chan])
  756. # reporting
  757. percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) /
  758. (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
  759. self.progressTrigger.emit(percent)
  760. ichan += 1
  761. self.DATADICT["CA"] = CA
  762. self.DATADICT["IP"] = IP
  763. self.DATADICT["NR"] = NR
  764. self.DATADICT["RE"] = RE
  765. self.DATADICT["IM"] = IM
  766. self.DATADICT["CAmax"] = CAmax
  767. self.DATADICT["NRmax"] = NRmax
  768. self.DATADICT["REmax"] = REmax
  769. self.DATADICT["IMmax"] = IMmax
  770. self.doneTrigger.emit()
  771. def plotQuadDet(self, clip, phase, canvas):
  772. canvas.reAxH2( len(self.DATADICT[ self.DATADICT["PULSES"][0] ]["chan"] ), False, False)
  773. ###############
  774. # Plot on GUI #
  775. ###############
  776. dcmap = cmocean.cm.curl_r #"seismic_r" #cmocean.cm.balance_r #"RdBu" #YlGn" # "coolwarm_r" # diverging
  777. canvas.reAxH2( len(self.DATADICT[ self.DATADICT["PULSES"][0] ]["chan"] ), False, False)
  778. for pulse in self.DATADICT["PULSES"]:
  779. ichan = 0
  780. axes = canvas.fig.axes
  781. mmaxr = 0.
  782. mmaxi = 0.
  783. if clip > 0:
  784. time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"][clip-1::] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  785. else:
  786. time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  787. QQ = np.average(self.DATADICT[pulse]["Q"], axis=1 )
  788. for chan in self.DATADICT[pulse]["chan"]:
  789. ax1 = axes[2*ichan ]
  790. ax2 = axes[2*ichan+1] # TODO fix hard coded number
  791. if phase == 0: # Re Im
  792. im1 = ax1.pcolormesh( time_sp, QQ, self.DATADICT["RE"][pulse][chan], cmap=dcmap, rasterized=True,\
  793. vmin=-self.DATADICT["REmax"][pulse] , vmax=self.DATADICT["REmax"][pulse] )
  794. im2 = ax2.pcolormesh( time_sp, QQ, self.DATADICT["IM"][pulse][chan], cmap=dcmap, rasterized=True,\
  795. vmin=-self.DATADICT["IMmax"][pulse], vmax=self.DATADICT["IMmax"][pulse] )
  796. if phase == 1: # Amp phase
  797. im1 = ax1.pcolormesh( time_sp, QQ, self.DATADICT["CA"][pulse][chan], cmap=dcmap, rasterized=True,
  798. vmin=-self.DATADICT["CAmax"][pulse] , vmax=self.DATADICT["CAmax"][pulse] )
  799. im2 = ax2.pcolormesh( time_sp, QQ, self.DATADICT["IP"][pulse][chan], cmap=cmocean.cm.phase, rasterized=True,\
  800. vmin=-np.pi, vmax=np.pi)
  801. if phase == 2: # CA NR
  802. im1 = ax1.pcolormesh( time_sp, QQ, self.DATADICT["CA"][pulse][chan], cmap=dcmap, rasterized=True,\
  803. vmin=-self.DATADICT["CAmax"][pulse] , vmax=self.DATADICT["CAmax"][pulse] )
  804. im2 = ax2.pcolormesh( time_sp, QQ, self.DATADICT["NR"][pulse][chan], cmap=dcmap, rasterized=True,\
  805. vmin=-self.DATADICT["NRmax"][pulse] , vmax=self.DATADICT["NRmax"][pulse] )
  806. # cb2 = canvas.fig.colorbar(im2, ax=ax2, format='%1.0e')
  807. # cb2.set_label("Noise residual (nV)", fontsize=8)
  808. # cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  809. # cb1 = canvas.fig.colorbar(im1, ax=ax1, format='%1.0e')
  810. # cb1.set_label("Phased amplitude (nV)", fontsize=8)
  811. # cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  812. # cb2 = canvas.fig.colorbar(im2, ax=ax2, format="%1.0e")
  813. # cb2.set_label("Phase (rad)", fontsize=8)
  814. # cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  815. # cb1 = canvas.fig.colorbar(im1, ax=ax1, format="%1.0e")
  816. # cb1.set_label("FID (nV)", fontsize=8)
  817. # cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  818. # if you save these as pdf or eps, there are artefacts
  819. # for cbar in [cb1,cb2]:
  820. # #cbar.solids.set_rasterized(True)
  821. # cbar.solids.set_edgecolor("face")
  822. # reporting
  823. percent = int(1e2* (float)(ichan)/len(self.DATADICT[pulse]["chan"]))
  824. self.progressTrigger.emit(percent)
  825. if ichan == 0:
  826. ax1.set_ylabel(r"$q$ ( $\mathrm{A}\cdot\mathrm{s}$)", fontsize=8)
  827. ax2.set_ylabel(r"$q$ ( $\mathrm{A}\cdot\mathrm{s}$)", fontsize=8)
  828. else:
  829. #ax2.yaxis.set_ticklabels([])
  830. #ax1.yaxis.set_ticklabels([])
  831. plt.setp(ax1.get_yticklabels(), visible=False)
  832. plt.setp(ax2.get_yticklabels(), visible=False)
  833. ichan += 1
  834. ax1.set_yscale('log')
  835. ax2.set_yscale('log')
  836. plt.setp(ax1.get_xticklabels(), visible=False)
  837. ax1.set_ylim( np.min(QQ), np.max(QQ) )
  838. ax2.set_ylim( np.min(QQ), np.max(QQ) )
  839. ax1.set_xlim( np.min(time_sp), np.max(time_sp) )
  840. ax2.set_xlim( np.min(time_sp), np.max(time_sp) )
  841. #ax2.set_xlabel(r"Time since end of pulse (ms)", fontsize=8)
  842. ax2.set_xlabel(r"Time (ms)", fontsize=8)
  843. canvas.fig.subplots_adjust(hspace=.15, wspace=.05, left=.075, right=.95, bottom=.1, top=.95 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  844. tick_locator = MaxNLocator(nbins=3)
  845. cb1 = canvas.fig.colorbar(im1, ax=axes[0::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  846. cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  847. cb1.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  848. cb1.locator = tick_locator
  849. cb1.update_ticks()
  850. tick_locator2 = MaxNLocator(nbins=3)
  851. cb2 = canvas.fig.colorbar(im2, ax=axes[1::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30, pad=.2)
  852. cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  853. cb2.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  854. cb2.locator = tick_locator2
  855. cb2.update_ticks()
  856. canvas.draw()
  857. self.doneTrigger.emit()
  858. def RotateAmplitude(self, X, Y, zeta, df, t):
  859. V = X + 1j*Y
  860. return np.abs(V) * np.exp( 1j * ( np.angle(V) - zeta - 2.*np.pi*df*t ) )
  861. def gateIntegrate( self, gpd, clip, canvas ):
  862. """ Gate integrate the real, imaginary, phased, and noise residual channels
  863. """
  864. self.gated = True
  865. self.GATED = {}
  866. for pulse in self.DATADICT["PULSES"]:
  867. QQ = np.average(self.DATADICT[pulse]["Q"], axis=1 )
  868. ichan = 0
  869. for chan in self.DATADICT[pulse]["chan"]:
  870. self.GATED[chan] = {}
  871. for ipm in range(0, self.DATADICT["nPulseMoments"]):
  872. # Time since pulse rather than since record starts...
  873. #if clip > 0:
  874. # time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"][clip:] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  875. #else:
  876. time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  877. #GT, GD, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  878. #GT2, GP, GTT, sig_stack_err, isum = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  879. GT, GCA, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  880. GT, GNR, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  881. GT, GRE, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["RE"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  882. GT, GIM, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["IM"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  883. GT, GIP, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["IP"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  884. if ipm == 0:
  885. # self.GATED[chan]["DATA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
  886. # self.GATED[chan]["ERR"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
  887. # self.GATED[chan]["SIGMA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
  888. self.GATED[chan]["CA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  889. self.GATED[chan]["NR"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  890. self.GATED[chan]["RE"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  891. self.GATED[chan]["IM"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  892. self.GATED[chan]["IP"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  893. self.GATED[chan]["isum"] = isum
  894. #self.GATED[chan]["DATA"][ipm] = GD.real
  895. self.GATEDABSCISSA = GT[clip:]
  896. self.GATEDWINDOW = GTT[clip:]
  897. #self.GATED[chan]["SIGMA"][ipm] = sig_stack #_err # GP.real
  898. #self.GATED[chan]["ERR"][ipm] = GP.real
  899. self.GATED[chan]["CA"][ipm] = GCA.real[clip:]
  900. self.GATED[chan]["NR"][ipm] = GNR.real[clip:]
  901. self.GATED[chan]["RE"][ipm] = GRE.real[clip:]
  902. self.GATED[chan]["IM"][ipm] = GIM.real[clip:]
  903. self.GATED[chan]["IP"][ipm] = GIP.real[clip:]
  904. percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) /
  905. (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
  906. self.progressTrigger.emit(percent)
  907. self.GATED[chan]["GTT"] = GTT[clip:]
  908. self.GATED[chan]["GT"] = GT[clip:]
  909. self.GATED[chan]["QQ"] = QQ
  910. ichan += 1
  911. self.doneTrigger.emit()
  912. def bootstrap_resample(self, X, n=None):
  913. # from http://nbviewer.jupyter.org/gist/aflaxman/6871948
  914. """ Bootstrap resample an array_like
  915. Parameters
  916. ----------
  917. X : array_like
  918. data to resample
  919. n : int, optional
  920. length of resampled array, equal to len(X) if n==None
  921. Results
  922. -------
  923. returns X_resamples
  924. """
  925. if n == None:
  926. n = len(X)
  927. resample_i = np.floor(np.random.rand(n)*len(X)).astype(int)
  928. return X[resample_i]
  929. def bootstrap_sigma(self, pulse, chan):
  930. # bootstrap resample
  931. nt = len(self.GATED[chan]["GT"])
  932. nb = 5000
  933. XS = np.zeros( (nb, nt) )
  934. for ii in range(nb):
  935. for it in range(nt):
  936. if self.GATED[chan]["isum"][it] < 8:
  937. XS[ii, it] = self.sigma[pulse][chan]
  938. else:
  939. if it == 0:
  940. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it+1], \
  941. self.GATED[chan]["NR"][:,it+2], self.GATED[chan]["NR"][:,it+3] ) ), n=nt )
  942. elif it == 1:
  943. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it-1], self.GATED[chan]["NR"][:,it], \
  944. self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it+2] ) ), n=nt )
  945. elif it == nt-2:
  946. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it], \
  947. self.GATED[chan]["NR"][:,it-1], self.GATED[chan]["NR"][:,it-2] ) ), n=nt )
  948. elif it == nt-1:
  949. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it-1], \
  950. self.GATED[chan]["NR"][:,it-2], self.GATED[chan]["NR"][:,it-3] ) ), n=nt )
  951. else:
  952. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it-2] , self.GATED[chan]["NR"][:,it-1], \
  953. self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it+2] )), n=nt )
  954. XS[ii,it] = np.std(X)
  955. return XS
  956. def plotGateIntegrate( self, gpd, clip, phase, canvas ):
  957. """ Plot the gate integration
  958. """
  959. canvas.reAxH2( len(self.DATADICT[ self.DATADICT["PULSES"][0] ]["chan"] ), False, False)
  960. axes = canvas.fig.axes
  961. cmap = cmocean.cm.balance_r
  962. # Calculate maximum for plotting...TODO move into loop above
  963. vmax1 = 0
  964. vmax2 = 0
  965. for pulse in self.DATADICT["PULSES"]:
  966. for chan in self.DATADICT[pulse]["chan"]:
  967. if phase == 0:
  968. vmax1 = max(vmax1, np.max(np.abs(self.GATED[chan]["RE"])))
  969. vmax2 = max(vmax2, np.max(np.abs(self.GATED[chan]["IM"])))
  970. elif phase == 1:
  971. vmax1 = max(vmax1, np.max(np.abs(self.GATED[chan]["CA"])))
  972. vmax2 = np.pi
  973. elif phase == 2:
  974. vmax1 = max(vmax1, np.max(np.abs(self.GATED[chan]["CA"])))
  975. vmax2 = max(vmax2, np.max(np.abs(self.GATED[chan]["NR"])))
  976. for pulse in self.DATADICT["PULSES"]:
  977. ichan = 0
  978. for chan in self.DATADICT[pulse]["chan"]:
  979. ax1 = axes[2*ichan ]
  980. ax2 = axes[2*ichan+1]
  981. if phase == 0:
  982. im1 = ax1.pcolormesh(self.GATED[chan]["GTT"], self.GATED[chan]["QQ"], self.GATED[chan]["RE"], cmap=cmap, vmin=-vmax1, vmax=vmax1)
  983. im2 = ax2.pcolormesh(self.GATED[chan]["GTT"], self.GATED[chan]["QQ"], self.GATED[chan]["IM"], cmap=cmap, vmin=-vmax2, vmax=vmax2)
  984. elif phase == 1:
  985. im1 = ax1.pcolormesh(self.GATED[chan]["GTT"], self.GATED[chan]["QQ"], self.GATED[chan]["CA"], cmap=cmap, vmin=-vmax1, vmax=vmax1)
  986. im2 = ax2.pcolormesh(self.GATED[chan]["GTT"], self.GATED[chan]["QQ"], self.GATED[chan]["IP"], cmap=cmocean.cm.phase, vmin=-vmax2, vmax=vmax2)
  987. elif phase == 2:
  988. im1 = ax1.pcolormesh(self.GATED[chan]["GTT"], self.GATED[chan]["QQ"], self.GATED[chan]["CA"], cmap=cmap, vmin=-vmax1, vmax=vmax1)
  989. XS = self.bootstrap_sigma(pulse, chan)
  990. #im2 = ax2.pcolormesh(self.GATED[chan]["GTT"], self.GATED[chan]["QQ"], self.GATED[chan]["NR"], cmap=cmap, vmin=-vmax2, vmax=vmax2)
  991. # bootstrap resample
  992. # nt = len(self.GATED[chan]["GT"])
  993. # nb = 5000
  994. # XS = np.zeros( (nb, nt) )
  995. # for ii in range(nb):
  996. # #XS = []
  997. # for it in range(nt):
  998. # if self.GATED[chan]["isum"][it] < 8:
  999. # XS[ii, it] = self.sigma[pulse][chan]
  1000. # else:
  1001. # if it == 0:
  1002. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it+1], \
  1003. # self.GATED[chan]["NR"][:,it+2], self.GATED[chan]["NR"][:,it+3] ) ), n=nt )
  1004. # if it == 1:
  1005. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it-1], self.GATED[chan]["NR"][:,it], \
  1006. # self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it+2] ) ), n=nt )
  1007. # elif it == nt-2:
  1008. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it], \
  1009. # self.GATED[chan]["NR"][:,it-1], self.GATED[chan]["NR"][:,it-2] ) ), n=nt )
  1010. # elif it == nt-1:
  1011. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it-1], \
  1012. # self.GATED[chan]["NR"][:,it-2], self.GATED[chan]["NR"][:,it-3] ) ), n=nt )
  1013. # else:
  1014. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it-2] , self.GATED[chan]["NR"][:,it-1], \
  1015. # self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it+2] )), n=nt )
  1016. # XS[ii,it] = np.std(X)
  1017. #if ii == 0:
  1018. # ax2.plot( self.GATED[chan]["GT"], XS[ii], '-', linewidth=1, markersize=4, alpha=.5, color='lightgrey', label = "bootstrap sim" )
  1019. #else:
  1020. # ax2.plot( self.GATED[chan]["GT"], XS[ii], '-', linewidth=1, markersize=4, alpha=.5, color='lightgrey' )
  1021. ax2.plot( self.GATED[chan]["GT"], np.std(self.GATED[chan]["NR"], axis=0), color='darkgrey', linewidth=2, label="gate std" )
  1022. ax2.plot( np.tile(self.GATED[chan]["GT"], (5000,1) ), XS, '.', color='lightgrey', linewidth=1, alpha=.5 )
  1023. ax2.plot( self.GATED[chan]["GT"], np.average(XS, axis=0), color='black', linewidth=2, label="bootstrap avg." )
  1024. ax2.plot( self.GATED[chan]["GT"], np.median(XS, axis=0), color='black', linewidth=2, label="bootstrap med." )
  1025. ax2.legend()
  1026. im1.set_edgecolor('face')
  1027. if phase != 2:
  1028. im2.set_edgecolor('face')
  1029. plt.setp(ax1.get_xticklabels(), visible=False)
  1030. ax1.set_ylim( np.min(self.GATED[chan]["QQ"]), np.max(self.GATED[chan]["QQ"]) )
  1031. if phase != 2:
  1032. ax2.set_ylim( np.min(self.GATED[chan]["QQ"]), np.max(self.GATED[chan]["QQ"]) )
  1033. ax1.set_xlim( np.min(self.GATED[chan]["GTT"]), np.max(self.GATED[chan]["GTT"]) )
  1034. ax2.set_xlim( np.min(self.GATED[chan]["GTT"]), np.max(self.GATED[chan]["GTT"]) )
  1035. ax1.set_yscale('log')
  1036. ax2.set_yscale('log')
  1037. qlabs = np.append(np.concatenate( ( self.GATED[chan]["QQ"][0:1], self.GATED[chan]["QQ"][9::10] )), self.GATED[chan]["QQ"][-1] )
  1038. ax1.yaxis.set_ticks( qlabs ) # np.append(np.concatenate( (QQ[0:1],QQ[9::10] )), QQ[-1] ) )
  1039. if phase != 2:
  1040. ax2.yaxis.set_ticks( qlabs ) #np.append(np.concatenate( (QQ[0:1],QQ[9::10] )), QQ[-1] ) )
  1041. #formatter = matplotlib.ticker.LogFormatter(10, labelOnlyBase=False)
  1042. formatter = matplotlib.ticker.FuncFormatter(lambda x, pos: str((round(x,1))))
  1043. ax1.yaxis.set_major_formatter(formatter) #matplotlib.ticker.FormatStrFormatter('%d.1'))
  1044. ax2.yaxis.set_major_formatter(formatter) #matplotlib.ticker.FormatStrFormatter('%d.1'))
  1045. ax1.xaxis.set_major_formatter(formatter) #matplotlib.ticker.FormatStrFormatter('%d.1'))
  1046. ax2.xaxis.set_major_formatter(formatter) #matplotlib.ticker.FormatStrFormatter('%d.1'))
  1047. ax1.set_xscale('log')
  1048. ax2.set_xscale('log')
  1049. if ichan == 0:
  1050. ax1.set_ylabel(r"$q$ ( $\mathrm{A}\cdot\mathrm{s}$)", fontsize=8)
  1051. if phase == 2:
  1052. ax2.set_ylabel(r"noise est. (nV)", fontsize=8)
  1053. else:
  1054. ax2.set_ylabel(r"$q$ ( $\mathrm{A}\cdot\mathrm{s}$)", fontsize=8)
  1055. else:
  1056. plt.setp(ax1.get_yticklabels(), visible=False)
  1057. plt.setp(ax2.get_yticklabels(), visible=False)
  1058. ax2.set_xlabel(r"$t-\tau_p$ (ms)", fontsize=8)
  1059. ichan += 1
  1060. #canvas.fig.subplots_adjust(hspace=.1, wspace=.05, left=.075, right=.925 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  1061. #canvas.fig.tight_layout()
  1062. #canvas.draw()
  1063. canvas.fig.subplots_adjust(hspace=.15, wspace=.05, left=.075, right=.95, bottom=.1, top=.95 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  1064. tick_locator = MaxNLocator(nbins=5)
  1065. cb1 = canvas.fig.colorbar(im1, ax=axes[0::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  1066. cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  1067. cb1.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  1068. cb1.locator = tick_locator
  1069. cb1.update_ticks()
  1070. if phase != 2:
  1071. cb2 = canvas.fig.colorbar(im2, ax=axes[1::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30, pad=.2)
  1072. cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  1073. cb2.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  1074. cb2.locator = tick_locator
  1075. cb2.update_ticks()
  1076. canvas.draw()
  1077. self.doneTrigger.emit()
  1078. def FDSmartStack(self, cv, canvas):
  1079. from matplotlib.colors import LogNorm
  1080. from matplotlib.ticker import MaxNLocator
  1081. """
  1082. Currently this stacks 4-phase second pulse data only, we need to generalise
  1083. """
  1084. try:
  1085. canvas.fig.clear()
  1086. except:
  1087. pass
  1088. self.dataCubeFFT( )
  1089. # canvas.ax1 = canvas.fig.add_axes([.1, .1, .8, .8])
  1090. canvas.ax1 = canvas.fig.add_axes([.1, .1, .2, .8])
  1091. canvas.ax2 = canvas.fig.add_axes([.325, .1, .2, .8])
  1092. canvas.ax3 = canvas.fig.add_axes([.55, .1, .2, .8])
  1093. canvas.ax4 = canvas.fig.add_axes([.815, .1, .05, .8]) #cb
  1094. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1095. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1096. canvas.ax3.tick_params(axis='both', which='major', labelsize=8)
  1097. canvas.ax4.tick_params(axis='both', which='major', labelsize=8)
  1098. canvas.ax1.set_ylabel("pulse index", fontsize=8)
  1099. canvas.ax1.set_xlabel(r"$\omega$ bin", fontsize=8)
  1100. canvas.ax2.set_xlabel(r"$\omega$ bin", fontsize=8)
  1101. canvas.ax3.set_xlabel(r"$\omega$ bin", fontsize=8)
  1102. canvas.ax2.yaxis.set_ticklabels([])
  1103. canvas.ax3.yaxis.set_ticklabels([])
  1104. #canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1105. # # Look at pulses
  1106. # for pulse in self.DATADICT["PULSES"]:
  1107. # for istack in self.DATADICT["stacks"]:
  1108. # for ipm in range(0,3):
  1109. # canvas.ax1.plot( self.DATADICT[pulse]["CURRENT"][ipm][istack] , label="istack "+str(istack) + " ipm=" + str(ipm) + pulse )
  1110. # canvas.draw()
  1111. # Create Container for stacks
  1112. # sandbox determine pulse sequence again
  1113. for pulse in self.DATADICT["PULSES"]:
  1114. for ichan in self.DATADICT[pulse]["chan"]:
  1115. #for ipm in range(10,11):
  1116. CONTAINER = {}
  1117. CONTAINER["Cycle 1"] = [] # These are actually subtracted cycles... v+ - v
  1118. CONTAINER["Cycle 2"] = []
  1119. for istack in self.DATADICT["stacks"]:
  1120. #canvas.ax1.clear()
  1121. ipm = 8
  1122. #for ipm in range(self.DATADICT["nPulseMoments"]):
  1123. #canvas.ax1.matshow( np.real(self.DATADICT[pulse][ichan]["FFT"][istack]), aspect='auto' )
  1124. #canvas.draw()
  1125. if not istack%4%4:
  1126. # phase cycle 4, aligned with 1 after sub
  1127. CONTAINER["Cycle 1"].append(-self.DATADICT[pulse][ichan]["FFT"][istack])
  1128. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], -self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1129. elif not istack%4%3:
  1130. # phase cycle 3, aligned with 2 after sub
  1131. CONTAINER["Cycle 2"].append(-self.DATADICT[pulse][ichan]["FFT"][istack])
  1132. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], -self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1133. elif not istack%4%2:
  1134. # phase cycle 2
  1135. CONTAINER["Cycle 2"].append( self.DATADICT[pulse][ichan]["FFT"][istack])
  1136. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1137. else:
  1138. # phase cycle 1
  1139. CONTAINER["Cycle 1"].append( self.DATADICT[pulse][ichan]["FFT"][istack])
  1140. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1141. #canvas.ax1.matshow(np.array(np.average(self.DATADICT[pulse][ichan]["FFT"]), axis=2), aspect='auto' )
  1142. #canvas.ax1.plot( self.DATADICT[pulse]["PULSE_TIMES"], self.DATADICT[pulse]["CURRENT"][ipm][istack] , color='black', label="istack "+str(istack) )
  1143. #canvas.ax1.plot( self.DATADICT[pulse]["CURRENT"][ipm][istack] , label="istack "+str(istack) + " iFID" + str(iFID) )
  1144. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1145. #canvas.ax1.legend(prop={'size':6})
  1146. #canvas.draw()
  1147. # Boostrap
  1148. # stack.
  1149. #scipy.random.shuffle(x)
  1150. # Stack and calculate the pooled variance (http://en.wikipedia.org/wiki/Pooled_variance)
  1151. """ All this phase cycling wreaks havoc on a normal calculation of std. and variance. Instead, we resort to calculating
  1152. a pooled variance. In this assumption is that the precision of the measurment is constant. This is a poor choice for
  1153. any type of moving sensor.
  1154. """
  1155. # if a window filter has been applied
  1156. #self.WINDOW
  1157. #self.IWindowStart
  1158. #self.iWindowEnd
  1159. #self.FFTtimes
  1160. CONTAINER = .5*(np.array(CONTAINER["Cycle 2"]) - np.array(CONTAINER["Cycle 1"]))
  1161. print ("container shape", np.shape( CONTAINER), self.iWindowStart+1, self.iWindowEnd-1)
  1162. dmin = np.min(np.abs(np.average(np.array(CONTAINER)[:,:,self.iWindowStart+1:self.iWindowEnd-1], axis=0)))
  1163. dmax = np.max(np.abs(np.average(np.array(CONTAINER)[:,:,self.iWindowStart+1:self.iWindowEnd-1], axis=0)))
  1164. mn = canvas.ax1.matshow( 20.*np.log10(np.abs(np.average(np.array(CONTAINER)[:,:, self.iWindowStart+1:self.iWindowEnd-1], axis=0))), aspect='auto', vmin=-120, vmax=-40)
  1165. #mn = canvas.ax1.matshow(20.*np.log10(XA[:,istart:iend+1]), aspect='auto', vmax=-40, vmin=-120) #, norm=LogNorm())
  1166. canvas.ax2.matshow( 20*np.log10(np.std(np.real(np.array(CONTAINER)[:,:,self.iWindowStart+1:self.iWindowEnd-1]), axis=0)), aspect='auto', vmin=-120, vmax=-40)
  1167. canvas.ax3.matshow( 20*np.log10(np.std(np.imag(np.array(CONTAINER)[:,:,self.iWindowStart+1:self.iWindowEnd-1]), axis=0)), aspect='auto', vmin=-120, vmax=-40)
  1168. #canvas.ax1.legend(prop={'size':6})
  1169. cb1 = mpl.colorbar.Colorbar(canvas.ax4, mn)
  1170. cb1.ax.tick_params(labelsize=8)
  1171. cb1.set_label("power [dB]", fontsize=8)
  1172. canvas.ax1.xaxis.set_major_locator(MaxNLocator(4))
  1173. canvas.ax2.xaxis.set_major_locator(MaxNLocator(4))
  1174. canvas.ax3.xaxis.set_major_locator(MaxNLocator(4))
  1175. canvas.draw()
  1176. self.doneTrigger.emit()
  1177. def effectivePulseMoment(self, cv, canvas):
  1178. canvas.reAxH(2)
  1179. nstack = len(self.DATADICT["stacks"])
  1180. canvas.ax1.set_yscale('log')
  1181. for pulse in self.DATADICT["PULSES"]:
  1182. self.DATADICT[pulse]["qeff"] = {}
  1183. self.DATADICT[pulse]["q_nu"] = {}
  1184. for ipm in range(self.DATADICT["nPulseMoments"]):
  1185. self.DATADICT[pulse]["qeff"][ipm] = {}
  1186. self.DATADICT[pulse]["q_nu"][ipm] = {}
  1187. #canvas.ax1.clear()
  1188. #scolours = np.array( ( np.linspace(0.8,0.4,len(self.DATADICT["stacks"])), \
  1189. # np.linspace(0.0,0.6,len(self.DATADICT["stacks"])), \
  1190. # np.linspace(0.6,0.0,len(self.DATADICT["stacks"])) )
  1191. # ).T
  1192. #scolours = plt.cm.Spectral(np.linspace(0,1,len(self.DATADICT["stacks"])))
  1193. #scolours = plt.cm.Blues(np.linspace(0,1,1.5*len(self.DATADICT["stacks"])))
  1194. scolours = cmocean.cm.ice(np.linspace(0,1,1.5*len(self.DATADICT["stacks"])))
  1195. iistack = 0
  1196. for istack in self.DATADICT["stacks"]:
  1197. #self.DATADICT[pulse]["PULSE_TIMES"]
  1198. x = self.DATADICT[pulse]["CURRENT"][ipm][istack]
  1199. X = np.fft.rfft(x)
  1200. v = np.fft.fftfreq(len(x), self.dt)
  1201. v = v[0:len(X)]
  1202. v[-1] = np.abs(v[-1])
  1203. # calculate effective current/moment
  1204. I0 = np.abs(X)/len(X)
  1205. qeff = I0 * (self.DATADICT[pulse]["PULSE_TIMES"][-1]-self.DATADICT[pulse]["PULSE_TIMES"][0])
  1206. canvas.ax1.set_title(r"pulse moment index " +str(ipm), fontsize=10)
  1207. canvas.ax1.set_xlabel(r"$\nu$ [Hz]", fontsize=8)
  1208. canvas.ax1.set_ylabel(r"$q_{eff}$ [A$\cdot$sec]", fontsize=8)
  1209. canvas.ax1.plot(v, qeff, color=scolours[iistack] ) # eff current
  1210. self.DATADICT[pulse]["qeff"][ipm][istack] = qeff
  1211. self.DATADICT[pulse]["q_nu"][ipm][istack] = v
  1212. iistack += 1
  1213. canvas.draw()
  1214. percent = int(1e2* (float)((istack)+ipm*self.DATADICT["nPulseMoments"]) /
  1215. (float)(len(self.DATADICT["PULSES"])*self.DATADICT["nPulseMoments"]*nstack))
  1216. self.progressTrigger.emit(percent)
  1217. canvas.draw()
  1218. self.plotQeffNu(cv, canvas.ax2)
  1219. canvas.draw()
  1220. self.doneTrigger.emit()
  1221. def plotQeffNu(self, cv, ax):
  1222. ####################################
  1223. # TODO label fid1 and fid2, and make a legend, and colour by pulse
  1224. nstack = len(self.DATADICT["stacks"])
  1225. iFID = 0
  1226. for pulse in self.DATADICT["PULSES"]:
  1227. self.DATADICT[pulse]["Q"] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT["stacks"])) )
  1228. ilabel = True
  1229. for ipm in range(self.DATADICT["nPulseMoments"]):
  1230. #scolours = np.array([0.,0.,1.])
  1231. scolours = cmocean.cm.ice(np.linspace(0,1,1.5*len(self.DATADICT["stacks"])))
  1232. #scolours = plt.cm.Spectral(np.linspace(0,1,len(self.DATADICT["stacks"])))
  1233. #scolours = plt.cm.Spectral(np.linspace(0,1,len(self.DATADICT["stacks"])))
  1234. istack = 0
  1235. for stack in self.DATADICT["stacks"]:
  1236. # find index
  1237. icv = int (round(cv / self.DATADICT[pulse]["q_nu"][ipm][stack][1]))
  1238. self.DATADICT[pulse]["Q"][ipm,istack] = self.DATADICT[pulse]["qeff"][ipm][stack][icv]
  1239. if ilabel:
  1240. ax.scatter(ipm, self.DATADICT[pulse]["qeff"][ipm][stack][icv], facecolors='none', edgecolors=scolours[istack], label=(str(pulse)))
  1241. ilabel = False
  1242. else:
  1243. ax.scatter(ipm, self.DATADICT[pulse]["qeff"][ipm][stack][icv], facecolors='none', edgecolors=scolours[istack])
  1244. #scolours += np.array((0,1./(nstack+1),-1/(nstack+1.)))
  1245. percent = int(1e2* (float)((istack)+ipm*self.DATADICT["nPulseMoments"]) /
  1246. (float)(len(self.DATADICT["PULSES"])*self.DATADICT["nPulseMoments"]*nstack))
  1247. self.progressTrigger.emit(percent)
  1248. istack += 1
  1249. iFID += 1
  1250. ax.set_xlabel(r"pulse moment index", fontsize=8)
  1251. ax.set_ylabel(r"$q_{eff}$ [A$\cdot$sec]", fontsize=8)
  1252. ax.set_yscale('log')
  1253. ax.set_xlim(0, ax.get_xlim()[1])
  1254. ax.legend(loc='upper right', scatterpoints = 1, prop={'size':6})
  1255. def enableDSP(self):
  1256. self.enableDSPTrigger.emit()
  1257. def adaptiveFilter(self, M, flambda, truncate, mu, PCA, canvas):
  1258. canvas.reAx2(shx=False, shy=False)
  1259. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1260. canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1261. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1262. canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1263. if truncate:
  1264. itrunc =(int) ( round( 1e-3*truncate*self.samp ) )
  1265. print( "adaptive filter size", 1e3*self.dt*M, " [ms]" )
  1266. Filt = adapt.AdaptiveFilter(flambda)
  1267. H = {}
  1268. for pulse in self.DATADICT["PULSES"]:
  1269. H[pulse] = {}
  1270. for ichan in self.DATADICT[pulse]["chan"]:
  1271. print("setting H", pulse, ichan)
  1272. H[pulse][ichan] = np.zeros(M)
  1273. iFID = 0
  1274. # original ordering...
  1275. #for pulse in self.DATADICT["PULSES"]:
  1276. # for ipm in range(self.DATADICT["nPulseMoments"]):
  1277. # for istack in self.DATADICT["stacks"]:
  1278. # This order makes more sense, same as data collection, verify
  1279. for istack in self.DATADICT["stacks"]:
  1280. for ipm in range(self.DATADICT["nPulseMoments"]):
  1281. for pulse in self.DATADICT["PULSES"]:
  1282. canvas.ax1.clear()
  1283. canvas.ax2.clear()
  1284. for ichan in self.DATADICT[pulse]["chan"]:
  1285. #H = np.zeros(M)
  1286. canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], 1e9* self.DATADICT[pulse][ichan][ipm][istack],\
  1287. label = "noisy")
  1288. RX = []
  1289. for irchan in self.DATADICT[pulse]["rchan"]:
  1290. RX.append(self.DATADICT[pulse][irchan][ipm][istack][::-1])
  1291. if all(H[pulse][ichan]) == 0:
  1292. # call twice to reconcile filter wind-up
  1293. [e,H[pulse][ichan]] = Filt.adapt_filt_Ref( self.DATADICT[pulse][ichan][ipm][istack][::-1],\
  1294. RX,\
  1295. M, mu, PCA, flambda, H[pulse][ichan])
  1296. [e,H[pulse][ichan]] = Filt.adapt_filt_Ref( self.DATADICT[pulse][ichan][ipm][istack][::-1],\
  1297. RX,\
  1298. M, mu, PCA, flambda, H[pulse][ichan])
  1299. else:
  1300. [e,H[pulse][ichan]] = Filt.adapt_filt_Ref( self.DATADICT[pulse][ichan][ipm][istack][::-1],\
  1301. RX,\
  1302. M, mu, PCA, flambda, H[pulse][ichan])
  1303. # replace
  1304. if truncate:
  1305. canvas.ax1.plot( self.DATADICT[pulse]["TIMES"][0:itrunc], 1e9* e[::-1][0:itrunc],\
  1306. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1307. self.DATADICT[pulse][ichan][ipm][istack] = e[::-1][0:itrunc]
  1308. else:
  1309. canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], 1e9* e[::-1],\
  1310. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1311. self.DATADICT[pulse][ichan][ipm][istack] = e[::-1]
  1312. canvas.ax2.plot( H[pulse][ichan] , label="taps")
  1313. canvas.ax1.legend(prop={'size':6})
  1314. canvas.ax2.legend(prop={'size':6})
  1315. canvas.ax1.set_xlabel(r"time [s]", fontsize=8)
  1316. canvas.ax1.set_ylabel(r"signal [nV]", fontsize=8)
  1317. canvas.ax2.set_xlabel(r"filter index", fontsize=8)
  1318. canvas.ax2.set_ylabel(r"scale factor", fontsize=8)
  1319. canvas.draw()
  1320. # truncate the reference channels too, in case you still need them for something.
  1321. # Otherwise they are no longer aligned with the data
  1322. for rchan in self.DATADICT[pulse]["rchan"]:
  1323. if truncate:
  1324. self.DATADICT[pulse][rchan][ipm][istack] = self.DATADICT[pulse][rchan][ipm][istack][0:itrunc]
  1325. #percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1326. percent = (int)(1e2*((float)(istack*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments*(len(self.DATADICT["stacks"])+1) )))
  1327. self.progressTrigger.emit(percent)
  1328. # # why is this loop here, istack is not part of rest?
  1329. # for istack in self.DATADICT["stacks"]:
  1330. # if truncate:
  1331. # self.DATADICT[pulse]["TIMES"] = self.DATADICT[pulse]["TIMES"][0:itrunc]
  1332. # percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1333. # self.progressTrigger.emit(percent)
  1334. # iFID += 1
  1335. if truncate:
  1336. self.DATADICT[pulse]["TIMES"] = self.DATADICT[pulse]["TIMES"][0:itrunc]
  1337. self.doneTrigger.emit()
  1338. self.updateProcTrigger.emit()
  1339. #self.plotFT(canvas)
  1340. def plotFT(self, canvas, istart=0, iend=0):
  1341. try:
  1342. canvas.fig.clear()
  1343. except:
  1344. pass
  1345. canvas.ax1 = canvas.fig.add_axes([.1, .1, .65, .8])
  1346. canvas.ax1c = canvas.fig.add_axes([.8, .1, .05, .8])
  1347. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1348. for pulse in self.DATADICT["PULSES"]:
  1349. for istack in self.DATADICT["stacks"]:
  1350. for ichan in self.DATADICT[pulse]["chan"]:
  1351. # FFT of stack
  1352. XA = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])/2+1))
  1353. nu = np.fft.fftfreq(self.DATADICT[pulse][ichan][0][istack].size, d=self.dt)
  1354. nu[-1] *= -1
  1355. df = nu[1]
  1356. of = 0
  1357. if istart:
  1358. of = nu[istart]
  1359. def freqlabel(x, pos):
  1360. return '%1.0f' %(of + x*df)
  1361. formatter = FuncFormatter(freqlabel)
  1362. canvas.ax1.clear()
  1363. for ipm in range(self.DATADICT["nPulseMoments"]):
  1364. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1365. XA[ipm,:] = np.abs(X)
  1366. if istart:
  1367. mn = canvas.ax1.matshow(20.*np.log10(XA[:,istart:iend+1]), aspect='auto', vmax=-40, vmin=-120) #, norm=LogNorm())
  1368. else:
  1369. mn = canvas.ax1.matshow(20.*np.log10(XA), aspect='auto', vmax=-40, vmin=-120) #, norm=LogNorm())
  1370. smin = np.min(20.*np.log10(XA))
  1371. smax = np.max(20.*np.log10(XA))
  1372. canvas.ax1.xaxis.set_major_formatter(formatter)
  1373. cb1 = mpl.colorbar.Colorbar(canvas.ax1c, mn)
  1374. cb1.ax.tick_params(labelsize=8)
  1375. cb1.set_label("signal [dB]", fontsize=8)
  1376. canvas.ax1.set_xlabel(r"$\nu$ [Hz]", fontsize=10)
  1377. canvas.ax1.set_ylabel(r"$q_{index}$", fontsize=10)
  1378. canvas.draw()
  1379. def plotFT(self, canvas, istart=0, iend=0):
  1380. try:
  1381. canvas.fig.clear()
  1382. except:
  1383. pass
  1384. canvas.ax1 = canvas.fig.add_axes([.1, .1, .65, .8])
  1385. canvas.ax1c = canvas.fig.add_axes([.8, .1, .05, .8])
  1386. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1387. for pulse in self.DATADICT["PULSES"]:
  1388. for istack in self.DATADICT["stacks"]:
  1389. for ichan in self.DATADICT[pulse]["chan"]:
  1390. # FFT of stack
  1391. XA = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])//2+1))
  1392. nu = np.fft.fftfreq(self.DATADICT[pulse][ichan][0][istack].size, d=self.dt)
  1393. nu[-1] *= -1
  1394. df = nu[1]
  1395. of = 0
  1396. if istart:
  1397. of = nu[istart]
  1398. def freqlabel(x, pos):
  1399. return '%1.0f' %(of + x*df)
  1400. formatter = FuncFormatter(freqlabel)
  1401. canvas.ax1.clear()
  1402. for ipm in range(self.DATADICT["nPulseMoments"]):
  1403. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1404. XA[ipm,:] = np.abs(X)
  1405. if istart:
  1406. mn = canvas.ax1.matshow(20.*np.log10(XA[:,istart:iend+1]), aspect='auto', vmax=-40, vmin=-120, cmap='viridis') #, norm=LogNorm())
  1407. else:
  1408. mn = canvas.ax1.matshow(20.*np.log10(XA), aspect='auto', vmax=-40, vmin=-120, cmap='viridis') #, norm=LogNorm())
  1409. canvas.ax1.xaxis.set_major_formatter(formatter)
  1410. cb1 = mpl.colorbar.Colorbar(canvas.ax1c, mn)
  1411. cb1.ax.tick_params(labelsize=8)
  1412. cb1.set_label("signal [dB]", fontsize=8)
  1413. canvas.ax1.set_xlabel(r"$\nu$ [Hz]", fontsize=10)
  1414. canvas.ax1.set_ylabel(r"$q_{index}$", fontsize=10)
  1415. canvas.draw()
  1416. def dataCubeFFT(self):
  1417. """
  1418. Performs FFT on entire cube of DATA, and REFERENCE channels, but not pulse currents,
  1419. Results are saved to a new field in the data structure
  1420. The GMR varies phase as a function of pulse moment index, so that the first pusle moment is zero phase,
  1421. the second is pi/2 the third is zero. This method corrects for this, so that all pulse moments are in phase.
  1422. Technically we may not want to do this, if there is some system response that this cycles away, and we lose track of
  1423. how many of each cycle we have, could this be problomatic? I think it will come out in the wash as we keep track of the
  1424. rest of the phase cycles. Holy phase cycling batman.
  1425. """
  1426. for pulse in self.DATADICT["PULSES"]:
  1427. for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  1428. # FFT of stack
  1429. self.DATADICT[pulse][ichan]["FFT"] = {}
  1430. self.DATADICT[pulse][ichan]["FFT"]["nu"] = np.fft.fftfreq(self.DATADICT[pulse][ichan][0][self.DATADICT["stacks"][0]].size, d=self.dt)
  1431. self.DATADICT[pulse][ichan]["FFT"]["nu"][-1] *= -1
  1432. for istack in self.DATADICT["stacks"]:
  1433. self.DATADICT[pulse][ichan]["FFT"][istack] = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])//2+1), dtype=complex)
  1434. for ipm in range(self.DATADICT["nPulseMoments"]):
  1435. # Mod works for FID pulse sequences, TODO generalize this for 4 phase T1, etc..
  1436. mod = (-1)**(ipm%2) * (-1)**(istack%2)
  1437. self.DATADICT[pulse][ichan]["FFT"][istack][ipm,:] = np.fft.rfft( self.DATADICT[pulse][ichan][ipm][istack] )
  1438. #if ipm%2:
  1439. # odd, phase cycled from previous
  1440. # self.DATADICT[pulse][ichan]["FFT"][istack][ipm,:] = np.fft.rfft(-self.DATADICT[pulse][ichan][ipm][istack])
  1441. #else:
  1442. # even, we define as zero phase, first pulse moment has this
  1443. # self.DATADICT[pulse][ichan]["FFT"][istack][ipm,:] = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1444. def adaptiveFilterFD(self, ftype, band, centre, canvas):
  1445. try:
  1446. canvas.fig.clear()
  1447. except:
  1448. pass
  1449. canvas.ax1 = canvas.fig.add_axes([.1, .5, .7, .4])
  1450. canvas.ax1c = canvas.fig.add_axes([.85, .5, .05, .4])
  1451. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1452. #canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1453. canvas.ax2 = canvas.fig.add_axes([.1, .05, .7, .4])
  1454. canvas.ax2c = canvas.fig.add_axes([.85, .05, .05, .4])
  1455. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1456. #canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1457. self.dataCubeFFT()
  1458. Filt = adapt.AdaptiveFilter(0.)
  1459. for pulse in self.DATADICT["PULSES"]:
  1460. # Compute window function and dimensions
  1461. [WINDOW, nd, wstart, wend, dead] = self.computeWindow(pulse, band, centre, ftype)
  1462. for istack in self.DATADICT["stacks"]:
  1463. for ichan in self.DATADICT[pulse]["chan"]:
  1464. # FFT of stack
  1465. nd = len(self.DATADICT[pulse][ichan][0][istack])
  1466. XX = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])//2+1), dtype=complex)
  1467. nu = np.fft.fftfreq(self.DATADICT[pulse][ichan][0][istack].size, d=self.dt)
  1468. nu[-1] *= -1
  1469. #nu = self.DATADICT[pulse][ichan]["FFT"]["nu"]
  1470. def freqlabel(x, pos):
  1471. return '%1.0f' %((wstart)*nu[1] + x*nu[1])
  1472. formatter = FuncFormatter(freqlabel)
  1473. canvas.ax1.clear()
  1474. for ipm in range(self.DATADICT["nPulseMoments"]):
  1475. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1476. XX[ipm,:] = X
  1477. XX = XX*WINDOW
  1478. XX = XX[:,wstart:wend]
  1479. smin = np.min(20.*np.log10(np.abs(XX)))
  1480. smax = np.max(20.*np.log10(np.abs(XX)))
  1481. #if smin != smin:
  1482. smax = -40
  1483. smin = -120
  1484. mn = canvas.ax1.matshow(20.*np.log10(np.abs(XX)), aspect='auto', vmin=smin, vmax=smax) #, norm=LogNorm())
  1485. canvas.ax1.xaxis.set_major_formatter(formatter)
  1486. cb1 = mpl.colorbar.Colorbar(canvas.ax1c, mn)
  1487. RX = []
  1488. for ichan in self.DATADICT[pulse]["rchan"]:
  1489. R = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])//2+1), dtype=complex)
  1490. for ipm in range(self.DATADICT["nPulseMoments"]):
  1491. R[ipm,:] = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1492. RX.append(R[:,wstart:wend])
  1493. XC = Filt.transferFunctionFFT(XX, RX)
  1494. # TODO inverse FFT, but we need to map back to origional matrix size
  1495. #for ichan in self.DATADICT[pulse]["chan"]:
  1496. # for ipm in range(self.DATADICT["nPulseMoments"]):
  1497. # self.DATADICT[pulse][ichan][ipm][istack] = np.fft.irfft(XC[] , nd)
  1498. mc = canvas.ax2.matshow(20.*np.log10(np.abs(XC)), aspect='auto', vmin=smin, vmax=smax) #, norm=LogNorm())
  1499. cb2 = mpl.colorbar.Colorbar(canvas.ax2c, mc)
  1500. cmin = np.min(20.*np.log10(np.abs(XC)))
  1501. cmax = np.max(20.*np.log10(np.abs(XC)))
  1502. canvas.ax2.xaxis.set_major_formatter(formatter)
  1503. #canvas.ax2.colorbar(mn)
  1504. canvas.draw()
  1505. ##############################3
  1506. # TODO inverse FFT to get the damn data back!!!
  1507. # self.progressTrigger.emit(percent)
  1508. # #label = "iFID="+str(iFID) + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1509. self.doneTrigger.emit()
  1510. def findSpikes(self, x, width, threshold, rollOn):
  1511. import scipy.ndimage as im
  1512. spikes = np.zeros( len(x) )
  1513. med = im.median_filter(x, width,mode='nearest')
  1514. std = np.std(x)
  1515. spikes = (np.abs(x-med) > threshold * std)
  1516. return np.array(np.where(spikes[rollOn::])) + rollOn
  1517. # def despike(self, width, threshold, itype, rollOn, win, canvas):
  1518. # from scipy import interpolate
  1519. # """ This was a stab at a despike filter. Better results were achieved using the SmartStack approach
  1520. # """
  1521. # try:
  1522. # canvas.fig.clear()
  1523. # except:
  1524. # pass
  1525. #
  1526. # canvas.ax1 = canvas.fig.add_axes([.125,.1,.725,.8])
  1527. # canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1528. # canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1529. # iFID = 0
  1530. # for pulse in self.DATADICT["PULSES"]:
  1531. # for ipm in range(self.DATADICT["nPulseMoments"]):
  1532. # for istack in self.DATADICT["stacks"]:
  1533. # canvas.ax1.clear()
  1534. # for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  1535. # x = self.findSpikes(self.DATADICT[pulse][ichan][ipm][istack], width, threshold, rollOn)
  1536. # canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], self.DATADICT[pulse][ichan][ipm][istack],
  1537. # label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1538. # canvas.ax1.plot( self.DATADICT[pulse]["TIMES"][x], self.DATADICT[pulse][ichan][ipm][istack][x], '.', color='red' , markersize=6 )
  1539. #
  1540. # FIXED = np.zeros(len(x[0]))
  1541. # ii = 0
  1542. # for spike in np.array(x[0]).tolist():
  1543. # f = interpolate.interp1d(np.delete(self.DATADICT[pulse]["TIMES"][spike-win/2:spike+win/2], x[0]-(spike-win/2)), \
  1544. # np.delete(self.DATADICT[pulse][ichan][ipm][istack][spike-win/2:spike+win/2], x[0]-(spike-win/2)), itype)
  1545. # FIXED[ii] = f(self.DATADICT[pulse]["TIMES"][spike])
  1546. # ii += 1
  1547. # canvas.ax1.plot( self.DATADICT[pulse]["TIMES"][x[0]] , FIXED, '.', color='black' , markersize=4 )
  1548. # self.DATADICT[pulse][ichan][ipm][istack][x[0]] = FIXED
  1549. #
  1550. # canvas.ax1.legend(prop={'size':6})
  1551. # canvas.draw()
  1552. # percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1553. # self.progressTrigger.emit(percent)
  1554. # iFID += 1
  1555. # self.doneTrigger.emit()
  1556. def designFilter(self, cf, PB, SB, gpass, gstop, ftype, canvas):
  1557. ''' cf is central frequency
  1558. pb is pass band
  1559. sb is stop band
  1560. '''
  1561. TS = (cf) / (.5/self.dt)
  1562. PB = PB / (.5/self.dt) # 1/2 width pass band Muddy Creek
  1563. SB = SB / (.5/self.dt) # 1/2 width stop band Muddy Creek
  1564. # if butterworth
  1565. #[bord, wn] = signal.buttord([TS-PB,TS+PB], [TS-SB,TS+SB], 1e-1, 5.)
  1566. if ftype=="Butterworth":
  1567. [bord, wn] = signal.buttord([TS-PB,TS+PB], [TS-SB,TS+SB], gpass, gstop)
  1568. [self.filt_b, self.filt_a] = signal.butter(bord, wn, btype='bandpass', output='ba')
  1569. [self.filt_z, self.filt_p, self.filt_k] = signal.butter(bord, wn, btype='band', output='zpk')
  1570. elif ftype == "Chebychev Type II":
  1571. [bord, wn] = signal.cheb2ord([TS-PB,TS+PB], [TS-SB,TS+SB], gpass, gstop)
  1572. [self.filt_b, self.filt_a] = signal.cheby2(bord, gstop, wn, btype='bandpass', output='ba')
  1573. [self.filt_z, self.filt_p, self.filt_k] = signal.cheby2(bord, gstop, wn, btype='band', output='zpk')
  1574. elif ftype == "Elliptic":
  1575. [bord, wn] = signal.ellipord([TS-PB,TS+PB], [TS-SB,TS+SB], gpass, gstop)
  1576. [self.filt_b, self.filt_a] = signal.ellip(bord, gpass, gstop, wn, btype='bandpass', output='ba')
  1577. [self.filt_z, self.filt_p, self.filt_k] = signal.ellip(bord, gpass, gstop, wn, btype='band', output='zpk')
  1578. # if cheby2
  1579. impulse = self.mfreqz2(self.filt_b, self.filt_a, canvas)
  1580. self.fe = -5
  1581. for it in range(len(impulse[0])):
  1582. if abs(impulse[1][0][it][0]) >= .1 * gpass:# gpass:
  1583. self.fe = impulse[0][it]
  1584. return [bord, self.fe]
  1585. def downsample(self, truncate, dec, canvas):
  1586. """ Downsamples and truncates the raw signal.
  1587. """
  1588. canvas.reAx2()
  1589. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1590. canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1591. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1592. canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1593. self.samp /= dec
  1594. self.dt = 1./self.samp
  1595. if truncate:
  1596. itrunc = (int)( 1e-3*truncate*self.samp )
  1597. iFID = 0
  1598. for pulse in self.DATADICT["PULSES"]:
  1599. for ipm in range(self.DATADICT["nPulseMoments"]):
  1600. for istack in self.DATADICT["stacks"]:
  1601. canvas.ax1.clear()
  1602. canvas.ax2.clear()
  1603. for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  1604. # trim off indices that don't divide evenly
  1605. ndi = np.shape(self.DATADICT[pulse][ichan][ipm][istack])[0]%dec
  1606. if ndi:
  1607. [self.DATADICT[pulse][ichan][ipm][istack], RSTIMES] = signal.resample(self.DATADICT[pulse][ichan][ipm][istack][0:-ndi],\
  1608. len(self.DATADICT[pulse][ichan][ipm][istack][0:-ndi])//dec,\
  1609. self.DATADICT[pulse]["TIMES"][0:-ndi], window='hamm')
  1610. else:
  1611. [self.DATADICT[pulse][ichan][ipm][istack], RSTIMES] = signal.resample(self.DATADICT[pulse][ichan][ipm][istack],\
  1612. len(self.DATADICT[pulse][ichan][ipm][istack])//dec,\
  1613. self.DATADICT[pulse]["TIMES"], window='hamm')
  1614. if truncate:
  1615. self.DATADICT[pulse][ichan][ipm][istack] = self.DATADICT[pulse][ichan][ipm][istack][0:itrunc]
  1616. RSTIMES = RSTIMES[0:itrunc]
  1617. for ichan in self.DATADICT[pulse]["chan"]:
  1618. canvas.ax2.plot( RSTIMES, 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  1619. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1620. for ichan in self.DATADICT[pulse]["rchan"]:
  1621. canvas.ax1.plot( RSTIMES, 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  1622. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1623. canvas.ax1.set_xlabel(r"time [s]", fontsize=8)
  1624. canvas.ax1.set_ylabel(r"signal [nV]", fontsize=8)
  1625. canvas.ax2.set_xlabel(r"time [s]", fontsize=8)
  1626. canvas.ax2.set_ylabel(r"signal [nV]", fontsize=8)
  1627. canvas.ax1.legend(prop={'size':6})
  1628. canvas.ax2.legend(prop={'size':6})
  1629. canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1630. canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1631. canvas.draw()
  1632. percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1633. self.progressTrigger.emit(percent)
  1634. iFID += 1
  1635. self.DATADICT[pulse]["TIMES"] = RSTIMES
  1636. #####################################
  1637. # resample pulse data
  1638. for pulse in self.DATADICT["PULSES"]:
  1639. for ipm in range(self.DATADICT["nPulseMoments"]):
  1640. for istack in self.DATADICT["stacks"]:
  1641. ndi = np.shape(self.DATADICT[pulse]["CURRENT"][ipm][istack])[0]%dec
  1642. if ndi:
  1643. [self.DATADICT[pulse]["CURRENT"][ipm][istack], RSPTIMES] = signal.resample(self.DATADICT[pulse]["CURRENT"][ipm][istack][0:-ndi],\
  1644. len(self.DATADICT[pulse]["CURRENT"][ipm][istack][0:-ndi])//dec,\
  1645. self.DATADICT[pulse]["PULSE_TIMES"][0:-ndi], window='hamm')
  1646. else:
  1647. [self.DATADICT[pulse]["CURRENT"][ipm][istack], RSPTIMES] = signal.resample(self.DATADICT[pulse]["CURRENT"][ipm][istack],\
  1648. len(self.DATADICT[pulse]["CURRENT"][ipm][istack])//dec,\
  1649. self.DATADICT[pulse]["PULSE_TIMES"], window='hamm')
  1650. self.DATADICT[pulse]["PULSE_TIMES"] = RSPTIMES
  1651. self.doneTrigger.emit()
  1652. self.updateProcTrigger.emit()
  1653. def computeWindow(self, pulse, band, centre, ftype, canvas=None):
  1654. # Compute window
  1655. nd = len(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0]][0][self.DATADICT["stacks"][0]])
  1656. fft1 = np.fft.rfft(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0]][0][self.DATADICT["stacks"][0]])
  1657. freqs = np.fft.fftfreq(nd, self.dt)
  1658. df = freqs[1] - freqs[0]
  1659. N = int((round)(band/df))
  1660. if ftype == "Hamming":
  1661. window = np.hamming(N)
  1662. elif ftype == "Hanning":
  1663. window = np.hanning(N)
  1664. elif ftype == "Rectangular":
  1665. window = np.ones(N)
  1666. elif ftype == "Flat top":
  1667. window = signal.flattop(N)
  1668. else:
  1669. print ("in windowFilter, window type undefined")
  1670. WINDOW = np.zeros(len(fft1))
  1671. ifreq = int(round(centre/df))
  1672. istart = ifreq-len(window)//2
  1673. iend = 0
  1674. if N%2:
  1675. WINDOW[ifreq-N//2:ifreq+N//2+1] = window
  1676. iend = ifreq+N//2+1
  1677. else:
  1678. WINDOW[ifreq-N//2:ifreq+N//2] = window
  1679. iend = ifreq+N//2
  1680. self.WINDOW = WINDOW
  1681. self.iWindowStart = istart
  1682. self.iWindowEnd = iend
  1683. self.FFTtimes = nd
  1684. fft1 = np.fft.irfft(WINDOW)
  1685. # calculate dead time
  1686. self.windead = 0.
  1687. for ift in np.arange(100,0,-1):
  1688. #print( ift, fft1[ift] )
  1689. if fft1[ift] > .001:
  1690. #print ("DEAD TIME", 1e3*self.DATADICT[pulse]["TIMES"][ift] - 1e3*self.DATADICT[pulse]["TIMES"][0] )
  1691. dead = 1e3*self.DATADICT[pulse]["TIMES"][ift] - 1e3*self.DATADICT[pulse]["TIMES"][0]
  1692. self.windead = self.DATADICT[pulse]["TIMES"][ift] - self.DATADICT[pulse]["TIMES"][0]
  1693. break
  1694. if canvas != None:
  1695. canvas.fig.clear()
  1696. canvas.ax1 = canvas.fig.add_axes([.1, .6, .75, .35])
  1697. canvas.ax2 = canvas.fig.add_axes([.1, .1, .75, .35])
  1698. canvas.ax1.plot(WINDOW)
  1699. canvas.ax2.plot( 1e3* self.DATADICT[pulse]["TIMES"][0:100] - 1e3*self.DATADICT[pulse]["TIMES"][0], fft1[0:100] )
  1700. canvas.ax2.set_xlabel("time (ms)")
  1701. canvas.ax2.set_title("IFFT")
  1702. canvas.draw()
  1703. return [WINDOW, nd, istart, iend, dead]
  1704. def windowFilter(self, ftype, band, centre, canvas):
  1705. ###############################
  1706. # Window Filter (Ormsby filter http://www.xsgeo.com/course/filt.htm)
  1707. # apply window
  1708. iFID = 0
  1709. for pulse in self.DATADICT["PULSES"]:
  1710. [WINDOW, nd, istart, iend,dead] = self.computeWindow(pulse, band, centre, ftype)
  1711. for istack in self.DATADICT["stacks"]:
  1712. for ipm in range(self.DATADICT["nPulseMoments"]):
  1713. for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  1714. fft = np.fft.rfft( self.DATADICT[pulse][ichan][ipm][istack] )
  1715. fft *= WINDOW
  1716. self.DATADICT[pulse][ichan][ipm][istack] = np.fft.irfft(fft , nd)
  1717. percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/(len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1718. self.progressTrigger.emit(percent)
  1719. iFID += 1
  1720. self.plotFT(canvas, istart, iend)
  1721. self.doneTrigger.emit()
  1722. def bandpassFilter(self, canvas, blank, plot=True):
  1723. if plot:
  1724. canvas.reAx2()
  1725. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1726. canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1727. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1728. canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1729. ife = (int)( max(self.fe, self.windead) * self.samp )
  1730. # Data
  1731. iFID = 0
  1732. for pulse in self.DATADICT["PULSES"]:
  1733. self.DATADICT[pulse]["TIMES"] = self.DATADICT[pulse]["TIMES"][ife:-ife]
  1734. for ipm in range(self.DATADICT["nPulseMoments"]):
  1735. for istack in self.DATADICT["stacks"]:
  1736. canvas.ax1.clear()
  1737. canvas.ax2.clear()
  1738. #for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  1739. for ichan in self.DATADICT[pulse]["rchan"]:
  1740. # reflect signal back on itself to reduce gibbs effects on early times
  1741. #nr = len( self.DATADICT[pulse][ichan][ipm][istack] ) - 1 + ife
  1742. #refl = np.append( -1*self.DATADICT[pulse][ichan][ipm][istack][::-1][0:-1], self.DATADICT[pulse][ichan][ipm][istack] )
  1743. #reflfilt = signal.filtfilt( self.filt_b, self.filt_a, refl )
  1744. #self.DATADICT[pulse][ichan][ipm][istack] = reflfilt[nr:-ife]
  1745. # don't reflect
  1746. self.DATADICT[pulse][ichan][ipm][istack] = \
  1747. signal.filtfilt(self.filt_b, self.filt_a, self.DATADICT[pulse][ichan][ipm][istack])[ife:-ife]
  1748. # plot
  1749. if plot:
  1750. canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  1751. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " rchan=" + str(ichan))
  1752. for ichan in self.DATADICT[pulse]["chan"]:
  1753. # reflect signal back on itself to reduce gibbs effects on early times
  1754. #nr = len( self.DATADICT[pulse][ichan][ipm][istack] ) - 1 + ife
  1755. #refl = np.append( -1*self.DATADICT[pulse][ichan][ipm][istack][::-1][0:-1], self.DATADICT[pulse][ichan][ipm][istack] )
  1756. #reflfilt = signal.filtfilt( self.filt_b, self.filt_a, refl )
  1757. #self.DATADICT[pulse][ichan][ipm][istack] = reflfilt[nr:-ife]
  1758. # don't reflect
  1759. self.DATADICT[pulse][ichan][ipm][istack] = \
  1760. scipy.signal.filtfilt(self.filt_b, self.filt_a, self.DATADICT[pulse][ichan][ipm][istack])[ife:-ife]
  1761. # plot
  1762. if plot:
  1763. canvas.ax2.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  1764. label = "data " + pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " chan=" + str(ichan))
  1765. if plot:
  1766. canvas.ax1.set_xlabel(r"time [s]", fontsize=8)
  1767. canvas.ax1.set_ylabel(r"signal [nV]", fontsize=8)
  1768. canvas.ax2.set_xlabel(r"time [s]", fontsize=8)
  1769. canvas.ax2.set_ylabel(r"signal [nV]", fontsize=8)
  1770. canvas.ax1.legend(prop={'size':6})
  1771. canvas.ax2.legend(prop={'size':6})
  1772. canvas.draw()
  1773. percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/(len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1774. self.progressTrigger.emit(percent)
  1775. iFID += 1
  1776. self.doneTrigger.emit()
  1777. self.updateProcTrigger.emit()
  1778. def loadGMRBinaryFID( self, rawfname, istack ):
  1779. """ Reads a single binary GMR file and fills into DATADICT
  1780. """
  1781. #################################################################################
  1782. # figure out key data indices
  1783. # Pulse
  1784. nps = (int)((self.prePulseDelay)*self.samp)
  1785. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  1786. # Data
  1787. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  1788. nd1 = (int)(1.*self.samp) # samples in first pulse
  1789. invGain = 1./self.RxGain
  1790. invCGain = self.CurrentGain
  1791. pulse = "Pulse 1"
  1792. chan = self.DATADICT[pulse]["chan"]
  1793. rchan = self.DATADICT[pulse]["rchan"]
  1794. rawFile = open( rawfname, 'rb')
  1795. for ipm in range(self.nPulseMoments):
  1796. buf1 = rawFile.read(4)
  1797. buf2 = rawFile.read(4)
  1798. N_chan = struct.unpack('>i', buf1 )[0]
  1799. N_samp = struct.unpack('>i', buf2 )[0]
  1800. T = N_samp * self.dt
  1801. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  1802. DATA = np.zeros([N_samp, N_chan+1])
  1803. for ichan in range(N_chan):
  1804. DATADUMP = rawFile.read(4*N_samp)
  1805. for irec in range(N_samp):
  1806. DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
  1807. # Save into Data Cube
  1808. for ichan in chan:
  1809. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain
  1810. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  1811. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
  1812. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  1813. # reference channels?
  1814. for ichan in rchan:
  1815. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain
  1816. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  1817. def loadGMRASCIIFID( self, rawfname, istack ):
  1818. """Based on the geoMRI instrument manufactured by VistaClara. Imports
  1819. a suite of raw .lvm files with the following format (on one line)
  1820. time(s) DC_Bus/100(V) Current+/75(A) Curr-/75(A) Voltage+/200(V) \
  1821. Ch1(V) Ch2(V) Ch3(V) Ch4(V)
  1822. Sampling rate is assumed at 50 kHz
  1823. """
  1824. import pandas as pd
  1825. #################################################################################
  1826. # figure out key data indices
  1827. # Pulse
  1828. nps = (int)((self.prePulseDelay)*self.samp)
  1829. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  1830. # Data
  1831. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  1832. nd1 = (int)(1.*self.samp) - nds # samples in first pulse
  1833. ndr = (int)(1.*self.samp) # samples in record
  1834. invGain = 1./self.RxGain
  1835. invCGain = self.CurrentGain
  1836. pulse = "Pulse 1"
  1837. chan = self.DATADICT[pulse]["chan"]
  1838. rchan = self.DATADICT[pulse]["rchan"]
  1839. T = 1.5 #N_samp * self.dt
  1840. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  1841. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  1842. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  1843. # pandas is much faster than numpy for io
  1844. #DATA = np.loadtxt(rawfname)
  1845. DATA = pd.read_csv(rawfname, header=None, sep="\t").values
  1846. for ipm in range(self.nPulseMoments):
  1847. for ichan in np.append(chan,rchan):
  1848. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:, eval(ichan)+4][nds:(nds+nd1)] * invGain
  1849. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,2][nps:nps+npul] * invCGain
  1850. nds += ndr
  1851. nps += ndr
  1852. def loadFIDData(self, base, procStacks, chanin, rchanin, FIDProc, canvas, deadTime, plot):
  1853. '''
  1854. Loads a GMR FID dataset, reads binary and ASCII format files
  1855. '''
  1856. canvas.reAx3(True,False)
  1857. chan = []
  1858. for ch in chanin:
  1859. chan.append(str(ch))
  1860. rchan = []
  1861. for ch in rchanin:
  1862. rchan.append(str(ch))
  1863. # not in any headers but this has changed, NOT the place to do this. MOVE
  1864. #self.prePulseDelay = 0.01 # delay before pulse
  1865. self.deadTime = deadTime # instrument dead time before measurement
  1866. self.samp = 50000. # in case this is a reproc, these might have
  1867. self.dt = 1./self.samp # changed
  1868. #################################################################################
  1869. # Data structures
  1870. PULSES = [FIDProc]
  1871. PULSES = ["Pulse 1"]
  1872. self.DATADICT = {}
  1873. self.DATADICT["nPulseMoments"] = self.nPulseMoments
  1874. self.DATADICT["stacks"] = procStacks
  1875. self.DATADICT["PULSES"] = PULSES
  1876. for pulse in PULSES:
  1877. self.DATADICT[pulse] = {}
  1878. self.DATADICT[pulse]["chan"] = chan # TODO these should not be a subet of pulse! for GMR all
  1879. self.DATADICT[pulse]["rchan"] = rchan # data are consistent
  1880. self.DATADICT[pulse]["CURRENT"] = {}
  1881. for ichan in np.append(chan,rchan):
  1882. self.DATADICT[pulse][ichan] = {}
  1883. for ipm in range(self.nPulseMoments):
  1884. self.DATADICT[pulse][ichan][ipm] = {}
  1885. self.DATADICT[pulse]["CURRENT"][ipm] = {}
  1886. for istack in procStacks:
  1887. self.DATADICT[pulse][ichan][ipm][istack] = np.zeros(3)
  1888. self.DATADICT[pulse]["CURRENT"][ipm][istack] = np.zeros(3)
  1889. ##############################################
  1890. # Read in binary (.lvm) data
  1891. iistack = 0
  1892. fnames = []
  1893. for istack in procStacks:
  1894. if self.nDAQVersion < 2.3:
  1895. #rawfname = base + "_" + str(istack)
  1896. self.loadGMRASCIIFID( base + "_" + str(istack), istack )
  1897. else:
  1898. self.loadGMRBinaryFID( base + "_" + str(istack) + ".lvm", istack )
  1899. #fnames.append( base + "_" + str(istack) + ".lvm" )
  1900. percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  1901. self.progressTrigger.emit(percent)
  1902. iistack += 1
  1903. # multiprocessing load data
  1904. #info = {}
  1905. #info["prePulseDelay"] = self.prePulseDelay
  1906. #with multiprocessing.Pool() as pool:
  1907. # results = pool.starmap( loadGMRBinaryFID, zip(itertools.repeat(self), fnames, info ) ) # zip(np.tile(vc, (ns, 1)), np.tile(vgc, (ns,1)), itertools.repeat(sys.argv[1]), itertools.repeat(sys.argv[2]), EPS_CMR))
  1908. # Plotting
  1909. if plot:
  1910. iistack = 0
  1911. for istack in procStacks:
  1912. for ipm in range(self.nPulseMoments):
  1913. canvas.ax1.clear()
  1914. canvas.ax2.clear()
  1915. canvas.ax3.clear()
  1916. #canvas.fig.patch.set_facecolor('blue')
  1917. for ichan in chan:
  1918. canvas.ax1.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
  1919. canvas.ax3.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID data ch. "+str(ichan)) #, color='blue')
  1920. for ichan in rchan:
  1921. canvas.ax2.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID ref ch. "+str(ichan)) #, color='blue')
  1922. canvas.ax3.legend(prop={'size':6})
  1923. canvas.ax2.legend(prop={'size':6})
  1924. canvas.ax1.set_title("stack "+str(istack)+" pulse index " + str(ipm), fontsize=8)
  1925. canvas.ax1.set_xlabel("time [s]", fontsize=8)
  1926. canvas.ax1.set_ylabel("Current [A]", fontsize=8)
  1927. canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1928. canvas.ax2.set_ylabel("RAW signal [V]", fontsize=8)
  1929. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1930. canvas.ax2.tick_params(axis='both', which='minor', labelsize=6)
  1931. canvas.ax2.set_xlabel("time [s]", fontsize=8)
  1932. canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1933. canvas.ax3.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1934. canvas.draw()
  1935. percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  1936. self.progressTrigger.emit(percent)
  1937. iistack += 1
  1938. self.enableDSP()
  1939. self.doneTrigger.emit()
  1940. def load4PhaseT1Data(self, base, procStacks, chan, rchan, FIDProc, canvas, deadTime, plot):
  1941. """
  1942. Designed to load GMR 4-phase data which use the following convention for phase cycles
  1943. P1 P2
  1944. Stack 1 -> 0 0 <-- <--
  1945. Stack 2 -> 0 pi/2 | <-- <--
  1946. Stack 3 -> pi/2 0 <-- | <--
  1947. Stack 4 -> pi/2 pi/2 <-- <--
  1948. The cycle is determined by stack indice. Walbrecker proposes for pulse2 data (Stack2 - Stack1) / 2
  1949. equivalently (Stack 4 - Stack3) will yield the same voltage response wrt. the second pulse.
  1950. Alternatively Stack 4 can be converted to be aligned with Stack 1 by negating, and Stack 3 Can be aligned with Stack 2 by negating
  1951. Then there are just the two phase cycles that can be stacked like normal.
  1952. Unfortunately, we need to stack each cycle first, then perform corrections for phase cycling. The reason for this is that otherwise,
  1953. the entire point is lost, as the signal that is desired to be cancelled out may not be balanced evenly across the stacks. That is to say,
  1954. if there is an uneven number of a certain phase cycle.
  1955. We could, I suppose impose this condition, but I think I would rather not?
  1956. + more samples for std. deviation calculation
  1957. + single spikes will have less residual effect
  1958. - can no longer do normality tests etc. and remove data that are suspect.
  1959. - requires a dumb stack, and may also require removal of entire stacks of data
  1960. Additonally, the GMR varies phase as a function of pulse moment index, so that the first pusle moment is zero phase, the second is pi/2 the third is zero ...
  1961. This however, is altered by the above convention. It gets a little complicated...
  1962. """
  1963. import struct
  1964. canvas.reAx2()
  1965. # not in any headers but this has changed, NOT the place to do this. MOVE
  1966. self.prePulseDelay = 0.01 # delay before pulse
  1967. self.deadTime = deadTime # instrument dead time before measurement
  1968. self.samp = 50000. # in case this is a reproc, these might have
  1969. self.dt = 1./self.samp # changed
  1970. invGain = 1./self.RxGain
  1971. invCGain = self.CurrentGain
  1972. #################################################################################
  1973. # figure out key data indices
  1974. # Pulse
  1975. nps = (int)((self.prePulseDelay)*self.samp)
  1976. nps2 = (int)((self.prePulseDelay+self.interpulseDelay)*self.samp)
  1977. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  1978. np2 = (int)(self.pulseLength[1]*self.samp) #+ 100
  1979. # Data
  1980. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  1981. nd1 = (int)((self.interpulseDelay)*self.samp) # samples in first pulse
  1982. nd2s = nps+npul+nd1+(int)((self.deadTime)*self.samp); # indice pulse 2 data starts
  1983. nd2 = (int)((1.)*self.samp) # samples in first pulse
  1984. nd1 -= (int)((.028)*self.samp) + nps # some time to get ready for next pulse
  1985. #################################################################################
  1986. # Data structures
  1987. PULSES = [FIDProc]
  1988. if FIDProc == "Both":
  1989. PULSES = ["Pulse 1","Pulse 2"]
  1990. self.DATADICT = {}
  1991. self.DATADICT["nPulseMoments"] = self.nPulseMoments
  1992. self.DATADICT["stacks"] = procStacks
  1993. self.DATADICT["PULSES"] = PULSES
  1994. for pulse in PULSES:
  1995. self.DATADICT[pulse] = {}
  1996. self.DATADICT[pulse]["chan"] = chan
  1997. self.DATADICT[pulse]["rchan"] = rchan
  1998. self.DATADICT[pulse]["CURRENT"] = {}
  1999. for ichan in np.append(chan,rchan):
  2000. self.DATADICT[pulse][ichan] = {}
  2001. for ipm in range(self.nPulseMoments):
  2002. self.DATADICT[pulse][ichan][ipm] = {}
  2003. self.DATADICT[pulse]["CURRENT"][ipm] = {}
  2004. for istack in procStacks:
  2005. self.DATADICT[pulse][ichan][ipm][istack] = np.zeros(3)
  2006. self.DATADICT[pulse]["CURRENT"][ipm][istack] = np.zeros(3)
  2007. ##############################################
  2008. # Read in binary data
  2009. iistack = 0
  2010. for istack in procStacks:
  2011. rawFile = open(base + "_" + str(istack) + ".lvm", 'rb')
  2012. for ipm in range(self.nPulseMoments):
  2013. N_chan = struct.unpack('>i', rawFile.read(4))[0]
  2014. N_samp = struct.unpack('>i', rawFile.read(4))[0]
  2015. T = N_samp * self.dt
  2016. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  2017. DATA = np.zeros([N_samp, N_chan+1])
  2018. for ichan in range(N_chan):
  2019. DATADUMP = rawFile.read(4*N_samp)
  2020. for irec in range(N_samp):
  2021. DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
  2022. if plot:
  2023. canvas.ax1.clear()
  2024. canvas.ax2.clear()
  2025. li = np.shape( DATA[:,4][nd2s:nd2s+nd2] )[0]
  2026. ######################################
  2027. # save into DATA cube
  2028. # TODO, changing iFID to 'Pulse 1' or 'Pulse 2'
  2029. for ichan in chan:
  2030. if FIDProc == "Pulse 1":
  2031. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,ichan+3][nds:nds+nd1] * invGain
  2032. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2033. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
  2034. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  2035. if plot:
  2036. canvas.ax1.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID data ch. "+str(ichan)) #, color='blue')
  2037. canvas.ax2.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
  2038. elif FIDProc == "Pulse 2":
  2039. self.DATADICT["Pulse 2"][ichan][ipm][istack] = DATA[:,ichan+3][nd2s:nd2s+nd2] *invGain
  2040. self.DATADICT["Pulse 2"]["TIMES"] = TIMES[nd2s:nd2s+nd2]
  2041. self.DATADICT["Pulse 2"]["CURRENT"][ipm][istack] = DATA[:,1][nps2:nps2+np2] * invCGain
  2042. self.DATADICT["Pulse 2"]["PULSE_TIMES"] = TIMES[nps2:nps2+np2]
  2043. if plot:
  2044. canvas.ax1.plot(self.DATADICT["Pulse 2"]["TIMES"], self.DATADICT["Pulse 2"][ichan][ipm][istack], label="Pulse 2 FID data ch. "+str(ichan)) #, color='blue')
  2045. canvas.ax2.plot( self.DATADICT["Pulse 2"]["PULSE_TIMES"], self.DATADICT["Pulse 2"]["CURRENT"][ipm][istack], color='black' )
  2046. else:
  2047. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,ichan+3][nds:nds+nd1] * invGain
  2048. self.DATADICT["Pulse 2"][ichan][ipm][istack] = DATA[:,ichan+3][nd2s:nd2s+nd2] * invGain
  2049. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2050. self.DATADICT["Pulse 2"]["TIMES"] = TIMES[nd2s:nd2s+nd2]
  2051. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
  2052. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  2053. self.DATADICT["Pulse 2"]["CURRENT"][ipm][istack] = DATA[:,1][nps2:nps2+np2] * invCGain
  2054. self.DATADICT["Pulse 2"]["PULSE_TIMES"] = TIMES[nps2:nps2+np2]
  2055. if plot:
  2056. canvas.ax1.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID data ch. "+str(ichan)) #, color='blue')
  2057. canvas.ax1.plot(self.DATADICT["Pulse 2"]["TIMES"], self.DATADICT["Pulse 2"][ichan][ipm][istack], label="Pulse 2 FID data ch. "+str(ichan)) #, color='blue')
  2058. canvas.ax2.plot( self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black' )
  2059. canvas.ax2.plot( self.DATADICT["Pulse 2"]["PULSE_TIMES"], self.DATADICT["Pulse 2"]["CURRENT"][ipm][istack] , color='black')
  2060. for ichan in rchan:
  2061. if FIDProc == "Pulse 1":
  2062. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,ichan+3][nds:nds+nd1] * invGain
  2063. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2064. if plot:
  2065. canvas.ax1.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID ref ch. "+str(ichan)) #, color='blue')
  2066. elif FIDProc == "Pulse 2":
  2067. self.DATADICT["Pulse 2"][ichan][ipm][istack] = DATA[:,ichan+3][nd2s:nd2s+nd2] * invGain
  2068. self.DATADICT["Pulse 2"]["TIMES"] = TIMES[nd2s:nd2s+nd2]
  2069. if plot:
  2070. canvas.ax1.plot(self.DATADICT["Pulse 2"]["TIMES"], self.DATADICT["Pulse 2"][ichan][ipm][istack], label="Pulse 2 FID ref ch. "+str(ichan)) #, color='blue')
  2071. else:
  2072. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,ichan+3][nds:nds+nd1] * invGain
  2073. self.DATADICT["Pulse 2"][ichan][ipm][istack] = DATA[:,ichan+3][nd2s:nd2s+nd2] * invGain
  2074. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2075. self.DATADICT["Pulse 2"]["TIMES"] = TIMES[nd2s:nd2s+nd2]
  2076. if plot:
  2077. canvas.ax1.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID ref ch. "+str(ichan)) #, color='blue')
  2078. canvas.ax1.plot(self.DATADICT["Pulse 2"]["TIMES"], self.DATADICT["Pulse 2"][ichan][ipm][istack], label="Pulse 2 FID ref ch. "+str(ichan)) #, color='blue')
  2079. if plot:
  2080. canvas.ax1.legend(prop={'size':6})
  2081. canvas.ax1.set_title("stack "+str(istack)+" pulse index " + str(ipm), fontsize=8)
  2082. canvas.ax1.set_xlabel("time [s]", fontsize=8)
  2083. canvas.ax1.set_ylabel("RAW signal [V]", fontsize=8)
  2084. canvas.ax2.set_ylabel("Current [A]", fontsize=8)
  2085. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  2086. canvas.ax2.tick_params(axis='both', which='minor', labelsize=6)
  2087. canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2088. canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2089. canvas.draw()
  2090. # update GUI of where we are
  2091. percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  2092. self.progressTrigger.emit(percent)
  2093. iistack += 1
  2094. self.enableDSP()
  2095. self.doneTrigger.emit()
  2096. if __name__ == "__main__":
  2097. if len(sys.argv) < 4:
  2098. print( "mrsurvey path/to/header <stack1> <stackN> ")
  2099. exit()
  2100. GMR = GMRDataProcessor()
  2101. GMR.readHeaderFile(sys.argv[1])
  2102. GMR.Print()
  2103. if GMR.pulseType == "FID":
  2104. GMR.loadFIDData(sys.argv[1], sys.argv[2], sys.argv[3], 5)
  2105. if GMR.pulseType == "4PhaseT1":
  2106. GMR.load4PhaseT1Data(sys.argv[1], sys.argv[2], sys.argv[3], 5)
  2107. pylab.show()