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 111KB

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