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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166
  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. from scipy import stats
  8. import copy
  9. import struct, glob
  10. from scipy.io.matlab import mio
  11. import pandas as pd
  12. from numpy import pi
  13. from math import floor
  14. import matplotlib as mpl
  15. from matplotlib.ticker import FuncFormatter
  16. import matplotlib.font_manager as fm
  17. import matplotlib.pyplot as plt
  18. import matplotlib.ticker
  19. from matplotlib.ticker import MaxNLocator
  20. import seaborn as sns
  21. from akvo.tressel.SlidesPlot import deSpine
  22. import multiprocessing
  23. import itertools
  24. import padasip as pa
  25. import akvo.tressel.adapt as adapt
  26. #import akvo.tressel.cadapt as adapt # cython for more faster
  27. import akvo.tressel.decay as decay
  28. import akvo.tressel.pca as pca
  29. import akvo.tressel.rotate as rotate
  30. import akvo.tressel.cmaps as cmaps
  31. import akvo.tressel.harmonic as harmonic
  32. import cmocean # colormaps for geophysical data
  33. # not needed with latest matplotlib
  34. #plt.register_cmap(name='viridis', cmap=cmaps.viridis)
  35. #plt.register_cmap(name='inferno', cmap=cmaps.inferno)
  36. #plt.register_cmap(name='inferno_r', cmap=cmaps.inferno_r)
  37. #plt.register_cmap(name='magma', cmap=cmaps.magma)
  38. #plt.register_cmap(name='magma_r', cmap=cmaps.magma_r)
  39. def xxloadGMRBinaryFID( rawfname, info ):
  40. """ Reads a single binary GMR file and fills into DATADICT
  41. """
  42. #################################################################################
  43. # figure out key data indices
  44. # Pulse
  45. nps = (int)((info["prePulseDelay"])*info["samp"])
  46. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  47. # Data
  48. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  49. nd1 = (int)(1.*self.samp) # samples in first pulse
  50. invGain = 1./self.RxGain
  51. invCGain = self.CurrentGain
  52. pulse = "Pulse 1"
  53. chan = self.DATADICT[pulse]["chan"]
  54. rchan = self.DATADICT[pulse]["rchan"]
  55. rawFile = open( rawfname, 'rb')
  56. T = N_samp * self.dt
  57. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  58. for ipm in range(self.nPulseMoments):
  59. buf1 = rawFile.read(4)
  60. buf2 = rawFile.read(4)
  61. N_chan = struct.unpack('>i', buf1 )[0]
  62. N_samp = struct.unpack('>i', buf2 )[0]
  63. DATA = np.zeros([N_samp, N_chan+1])
  64. for ichan in range(N_chan):
  65. DATADUMP = rawFile.read(4*N_samp)
  66. for irec in range(N_samp):
  67. DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
  68. return DATA, TIMES
  69. class SNMRDataProcessor(QObject):
  70. """ Revised class for preprocessing sNMR Data.
  71. Derived types can read GMR files
  72. """
  73. def __init__(self):
  74. QObject.__init__(self)
  75. self.numberOfMoments = 0
  76. self.numberOfPulsesPerMoment = 0
  77. self.pulseType = "NONE"
  78. self.transFreq = 0
  79. self.pulseLength = np.zeros(1)
  80. self.nPulseMoments = 0
  81. self.dt = 0
  82. def mfreqz(self, b,a=1):
  83. """ Plots the frequency response of a filter specified with a and b weights
  84. """
  85. import scipy.signal as signal
  86. pylab.figure(124)
  87. w,h = signal.freqz(b,a)
  88. w /= max(w)
  89. w *= .5/self.dt
  90. h_dB = 20 * pylab.log10 (abs(h))
  91. pylab.subplot(211)
  92. #pylab.plot(w/max(w),h_dB)
  93. pylab.plot(w,h_dB)
  94. pylab.ylim(-150, 5)
  95. pylab.ylabel('Magnitude (dB)')
  96. #pylab.xlabel(r'Normalized Frequency (x$\pi$rad/sample)')
  97. pylab.xlabel(r'Hz')
  98. pylab.title(r'Frequency response')
  99. pylab.subplot(212)
  100. h_Phase = pylab.unwrap(pylab.arctan2(pylab.imag(h), pylab.real(h)))
  101. #pylab.plot(w/max(w),h_Phase)
  102. pylab.plot(w,h_Phase)
  103. pylab.ylabel('Phase (radians)')
  104. pylab.xlabel(r'Hz')
  105. #pylab.xlabel(r'Normalized Frequency (x$\pi$rad/sample)')
  106. pylab.title(r'Phase response')
  107. pylab.subplots_adjust(hspace=0.5)
  108. def mfreqz2(self, b, a, canvas):
  109. "for analysing filt-filt"
  110. import scipy.signal as signal
  111. canvas.reAx2(False,False)
  112. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  113. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  114. #canvas.ax2.tick_params(axis='both', which='minor', labelsize=6)
  115. #pylab.figure(124)
  116. w,h = signal.freqz(b,a)
  117. w /= max(w)
  118. w *= .5/self.dt
  119. h_dB = 20 * pylab.log10(abs(h*h) + 1e-16)
  120. #ab.subplot(211)
  121. #pylab.plot(w/max(w),h_dB)
  122. canvas.ax1.plot(w,h_dB)
  123. canvas.ax1.set_ylim(-150, 5)
  124. canvas.ax1.set_ylabel('Magnitude [db]', fontsize=8)
  125. #pylab.xlabel(r'Normalized Frequency (x$\pi$rad/sample)')
  126. canvas.ax1.set_xlabel(r'[Hz]', fontsize=8)
  127. canvas.ax1.set_title(r'Frequency response', fontsize=8)
  128. canvas.ax1.grid(True)
  129. tt = np.arange(0, .02, self.dt)
  130. impulse = signal.dimpulse((self.filt_z, self.filt_p, self.filt_k, self.dt), t=tt)
  131. #impulse = signal.dstep((self.filt_z, self.filt_p, self.filt_k, self.dt), t=tt)
  132. #print impulse
  133. #for ii in range(len(impulse[1])):
  134. impulse_dB = 20.*np.log10(np.abs(np.array(impulse[1][0])))
  135. #canvas.ax2.plot(np.array(impulse[0]), impulse_dB)
  136. canvas.ax2.plot(np.array(impulse[0]), impulse[1][0])
  137. #h_Phase = pylab.unwrap(pylab.arctan2(pylab.imag(h), pylab.real(h)))
  138. #canvas.ax2.plot(w,h_Phase)
  139. canvas.ax2.set_ylabel('response [%]', fontsize=8)
  140. canvas.ax2.set_xlabel(r'time [s]', fontsize=8)
  141. canvas.ax2.set_title(r'impulse response', fontsize=8)
  142. #canvas.ax2.grid(True)
  143. canvas.draw()
  144. # search for last
  145. return impulse #[np.where(impulse[1][0] > .01)[-1]]
  146. class GMRDataProcessor(SNMRDataProcessor):
  147. # slots
  148. progressTrigger = pyqtSignal("int")
  149. doneTrigger = pyqtSignal()
  150. enableDSPTrigger = pyqtSignal()
  151. updateProcTrigger = pyqtSignal()
  152. def __init__(self):
  153. SNMRDataProcessor.__init__(self)
  154. self.maxBusV = 0.
  155. self.samp = 50000. # sampling frequency
  156. self.dt = 2e-5 # sampling rate
  157. self.deadTime = .0055 # instrument dead time before measurement
  158. self.prePulseDelay = 0.05 # delay before pulse
  159. self.windead = 0. # FD window filter dead time
  160. self.pulseType = -1
  161. self.transFreq = -1
  162. self.maxBusV = -1
  163. self.pulseLength = -1
  164. self.interpulseDelay = -1 # for T2, Spin Echo
  165. self.repetitionDelay = -1 # delay between first pulse
  166. self.nPulseMoments = -1 # Number of pulse moments per stack
  167. self.TuneCapacitance = -1 # tuning capac in uF
  168. self.nTransVersion = -1 # Transmitter version
  169. self.nDAQVersion = -1 # DAQ software version
  170. self.nInterleaves = -1 # num interleaves
  171. # self.nReceiveChannels = 4 # Num receive channels
  172. self.RotatedAmplitude = False
  173. # self.DATA = np.zeros(1) # Numpy array to hold all data, dimensions resized based on experiment
  174. # self.PULSES = np.zeros(1) # Numpy array to hold all data, dimensions resized based on experiment
  175. def Print(self):
  176. print ("pulse type", self.pulseType)
  177. print ("maxBusV", self.maxBusV)
  178. print ("inner pulse delay", self.interpulseDelay)
  179. print ("tuning capacitance", self.TuneCapacitance)
  180. print ("sampling rate", self.samp)
  181. print ("dt", self.dt)
  182. print ("dead time", self.deadTime)
  183. print ("pre pulse delay", self.prePulseDelay)
  184. print ("number of pulse moments", self.nPulseMoments)
  185. print ("pulse Length", self.pulseLength)
  186. print ("trans freq", self.transFreq)
  187. def readHeaderFile(self, FileName):
  188. HEADER = np.loadtxt(FileName)
  189. pulseTypeDict = {
  190. 1 : lambda: "FID",
  191. 2 : lambda: "T1",
  192. 3 : lambda: "SPINECHO",
  193. 4 : lambda: "4PhaseT1"
  194. }
  195. pulseLengthDict = {
  196. 1 : lambda x: np.ones(1) * x,
  197. 2 : lambda x: np.ones(2) * x,
  198. 3 : lambda x: np.array([x, 2.*x]),
  199. 4 : lambda x: np.ones(2) * x
  200. }
  201. self.pulseType = pulseTypeDict.get((int)(HEADER[0]))()
  202. self.transFreq = HEADER[1]
  203. self.maxBusV = HEADER[2]
  204. self.pulseLength = pulseLengthDict.get((int)(HEADER[0]))(1e-3*HEADER[3])
  205. self.interpulseDelay = 1e-3*HEADER[4] # for T2, Spin Echo
  206. self.repetitionDelay = HEADER[5] # delay between first pulse
  207. self.nPulseMoments = (int)(HEADER[6]) # Number of pulse moments per stack
  208. self.TuneCapacitance = HEADER[7] # tuning capacitance in uF
  209. self.nTransVersion = HEADER[8] # Transmitter version
  210. self.nDAQVersion = HEADER[9] # DAQ software version
  211. self.nInterleaves = HEADER[10] # num interleaves
  212. self.Instrument = "GMR"
  213. self.gain()
  214. # default
  215. self.samp = 50000. # sampling frequency
  216. self.dt = 2e-5 # sampling rate
  217. # newer header files contain 64 entries
  218. if self.nDAQVersion >= 2:
  219. #self.deadtime = HEADER[11]
  220. #self.unknown = HEADER[12]
  221. #self.PreAmpGain = HEADER[13]
  222. self.samp = HEADER[14] # sampling frequency
  223. self.dt = 1./self.samp # sampling rate
  224. self.deadTime = .0055 # instrument dead time before measurement
  225. self.prePulseDelay = 0.05 # delay before pulse
  226. #exit()
  227. def gain(self):
  228. #######################################################
  229. # Circuit gain
  230. # From MRSMatlab
  231. w = 2*np.pi*self.transFreq
  232. # 1e6 due to uF of reported capacitance
  233. L_coil = 1e6/(self.TuneCapacitance*(w**2))
  234. R_coil = 1.
  235. Z1_in = .5 + 1j*.5*w
  236. Z2_in = 1./(1j*w*.000001616)
  237. Z_eq_inv = (1./Z1_in) + (1./Z2_in)
  238. Zeq = 1./Z_eq_inv
  239. Zsource = R_coil + 1j*w*L_coil
  240. voltage_in = Zeq / (Zsource + Zeq)
  241. self.circuitGain = np.abs(voltage_in)
  242. self.circuitPhase_deg = (180/np.pi)+np.angle(voltage_in)
  243. circuitImpedance_ohms = np.abs(Zsource + Zeq)
  244. ######################################################
  245. # PreAmp gain
  246. if self.nTransVersion == 4:
  247. self.PreAmpGain = 1000.
  248. elif self.nTransVersion == 1 or self.nTransVersion == 2 or self.nTransVersion == 3 or self.nTransVersion == 6:
  249. self.PreAmpGain = 500.
  250. else:
  251. print ("unsupported transmitter version")
  252. exit(1)
  253. # Total Receiver Gain
  254. self.RxGain = self.circuitGain * self.PreAmpGain
  255. #####################################################
  256. # Current gain
  257. if floor(self.nDAQVersion) == 1:
  258. self.CurrentGain = 150.
  259. elif floor(self.nDAQVersion) == 2:
  260. self.CurrentGain = 180.
  261. def updateProgress(self):
  262. pass
  263. def TDSmartStack(self, outlierTest, MADcutoff, canvas):
  264. fs = 10 # fontsize
  265. #print("Line 300 in mrsurvey")
  266. Stack = {}
  267. # align for stacking and modulate
  268. for pulse in self.DATADICT["PULSES"]:
  269. stack = np.zeros(( len(self.DATADICT[pulse]["chan"]), self.DATADICT["nPulseMoments"],\
  270. len(self.DATADICT["stacks"]), len(self.DATADICT[pulse]["TIMES"]) ))
  271. for ipm in range(self.DATADICT["nPulseMoments"]):
  272. istack = 0
  273. for sstack in self.DATADICT["stacks"]:
  274. if self.pulseType == "FID" or pulse == "Pulse 2":
  275. if self.Instrument == "MIDI 2":
  276. mod = 1
  277. elif self.Instrument == "GMR" and floor(self.nDAQVersion) < 2:
  278. mod = 1
  279. else:
  280. mod = (-1.)**(ipm%2) * (-1.)**(sstack%2)
  281. elif self.pulseType == "T1":
  282. #mod = (-1.)**(sstack%2)
  283. #mod = (-1)**(ipm%2) * (-1)**(sstack%2)
  284. #mod = (-1)**(ipm%2) * (-1.**(((sstack-1)/2)%2))
  285. #print("mod", mod, ipm, sstack, (-1.)**(ipm%2), -1.0**(((sstack-1)/2)%2 ))
  286. #mod = (-1.)**((ipm+1)%2) * (-1.**(((sstack)/2)%2))
  287. #mod = (-1.)**((ipm-1)%2) * (-1.)**((sstack-1)%2)
  288. #mod = 1 # (-1.**(((sstack-1)/2)%2))
  289. # These two give great noise estimate
  290. #qcycler = np.array([1,-1,-1,1])
  291. #scycler = np.array([1,-1,1,-1])
  292. qcycler = np.array([ 1, 1])
  293. scycler = np.array([ 1, 1])
  294. mod = qcycler.take([ipm], mode='wrap')*scycler.take([sstack], mode='wrap')
  295. #mod = (-1.)**(ipm%2) * (-1.)**(sstack%2)
  296. elif self.pulseType == "4PhaseT1":
  297. mod = (-1.)**(ipm%2) * (-1.**(((sstack-1)/2)%2))
  298. ichan = 0
  299. for chan in self.DATADICT[pulse]["chan"]:
  300. stack[ichan,ipm,istack,:] += mod*self.DATADICT[pulse][chan][ipm][sstack]
  301. ichan += 1
  302. istack += 1
  303. Stack[pulse] = stack
  304. #########################################
  305. # simple stack and plot of simple stack #
  306. #########################################
  307. canvas.reAxH2(np.shape(stack)[0], False, False)
  308. axes = canvas.fig.axes
  309. SimpleStack = {}
  310. VarStack = {}
  311. for pulse in self.DATADICT["PULSES"]:
  312. SimpleStack[pulse] = {}
  313. VarStack[pulse] = {}
  314. ichan = 0
  315. for chan in self.DATADICT[pulse]["chan"]:
  316. SimpleStack[pulse][chan] = 1e9*np.average( Stack[pulse][ichan], 1 )
  317. VarStack[pulse][chan] = 1e9*np.std( Stack[pulse][ichan], 1 )
  318. ax1 = axes[ 2*ichan ]
  319. #ax1.get_yaxis().get_major_formatter().set_useOffset(False)
  320. y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
  321. ax1.yaxis.set_major_formatter(y_formatter)
  322. ax1.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( SimpleStack[pulse][chan], 0 )) #, color='darkblue' )
  323. ax1.set_title("Ch." + str(chan) + ": avg FID", fontsize=fs)
  324. ax1.set_xlabel(r"time (ms)", fontsize=fs)
  325. if ichan == 0:
  326. ax1.set_ylabel(r"signal (nV)", fontsize=fs)
  327. else:
  328. plt.setp(ax1.get_yticklabels(), visible=False)
  329. plt.setp(ax1.get_yaxis().get_offset_text(), visible=False)
  330. # if ichan == 1:
  331. # canvas.ax2.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( SimpleStack[pulse][chan], 0 ), color='darkblue' )
  332. # canvas.ax2.set_title("Ch." + str(chan) + ": total average FID", fontsize=8)
  333. # canvas.ax2.set_xlabel(r"time [ms]", fontsize=8)
  334. # if ichan == 2:
  335. # canvas.ax3.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( SimpleStack[pulse][chan], 0 ), color='darkblue' )
  336. # canvas.ax3.set_title("Ch." + str(chan) + ": total average FID", fontsize=8)
  337. # canvas.ax3.set_xlabel(r"time [ms]", fontsize=8)
  338. # if ichan == 3:
  339. # canvas.ax4.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( SimpleStack[pulse][chan], 0 ), color='darkblue' )
  340. # canvas.ax4.set_title("Ch." + str(chan) + ": total average FID", fontsize=8)
  341. # canvas.ax4.set_xlabel(r"time [ms]", fontsize=8)
  342. ichan += 1
  343. #########################
  344. # Oulier rejectig stack #
  345. #########################
  346. if outlierTest == "MAD":
  347. MADStack = {}
  348. VarStack = {}
  349. #1.4826 is assumption of gaussian noise
  350. madstack = np.zeros(( len(self.DATADICT[pulse]["chan"]),\
  351. self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"]) ))
  352. varstack = np.zeros(( len(self.DATADICT[pulse]["chan"]),\
  353. self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"]) ))
  354. for pulse in self.DATADICT["PULSES"]:
  355. MADStack[pulse] = {}
  356. VarStack[pulse] = {}
  357. ichan = 0
  358. for chan in self.DATADICT[pulse]["chan"]:
  359. ax1 = axes[ 2*ichan ]
  360. for ipm in range(self.DATADICT["nPulseMoments"]):
  361. # # brutal loop over time, can this be vectorized?
  362. # for it in range(len(self.DATADICT[pulse]["TIMES"])):
  363. # x = 1e9 *Stack[pulse][ichan,ipm,:,it]
  364. # MAD = 1.4826 * np.median( np.abs(x-np.median(x)) )
  365. # good = 0
  366. # for istack in self.DATADICT["stacks"]:
  367. # if (np.abs(x[istack-1]-np.median(x))) / MAD < 2:
  368. # good += 1
  369. # madstack[ ichan, ipm, it ] += x[istack-1]
  370. # else:
  371. # pass
  372. # madstack[ichan, ipm, it] /= good
  373. # percent = int(1e2* (float)(ipm) / (float)(self.DATADICT["nPulseMoments"]) )
  374. # self.progressTrigger.emit(percent)
  375. # Vectorized version of above...much, much faster
  376. x = 1e9*copy.deepcopy(Stack[pulse][ichan][ipm,:,:]) # stack and time indices
  377. tile_med = np.tile( np.median(x, axis=0), (np.shape(x)[0],1))
  378. MAD = MADcutoff * np.median(np.abs(x - tile_med), axis=0)
  379. tile_MAD = np.tile( MAD, (np.shape(x)[0],1))
  380. good = np.abs(x-tile_med)/tile_MAD < 2. # 1.4826 # 2
  381. madstack[ichan][ipm] = copy.deepcopy( np.ma.masked_array(x, good != True).mean(axis=0) )
  382. varstack[ichan][ipm] = copy.deepcopy( np.ma.masked_array(x, good != True).std(axis=0) )
  383. # reporting
  384. percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) /
  385. (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
  386. self.progressTrigger.emit(percent)
  387. ax1.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( madstack[ichan], 0 ))# , color='darkred')
  388. MADStack[pulse][chan] = madstack[ichan]
  389. VarStack[pulse][chan] = varstack[ichan]
  390. ichan += 1
  391. self.DATADICT["stack"] = MADStack
  392. else:
  393. self.DATADICT["stack"] = SimpleStack
  394. #########################################
  395. # Plot Fourier Transform representation #
  396. #########################################
  397. # canvas.fig.subplots_adjust(right=0.8)
  398. # cbar_ax = canvas.fig.add_axes([0.85, 0.1, 0.015, 0.355])
  399. # cbar_ax.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  400. im2 = []
  401. im1 = []
  402. for pulse in self.DATADICT["PULSES"]:
  403. ichan = 0
  404. axes = canvas.fig.axes
  405. vvmin = 1e10
  406. vvmax = 0
  407. for chan in self.DATADICT[pulse]["chan"]:
  408. ax1 = axes[2*ichan ]
  409. ax2 = axes[2*ichan+1] # TODO fix hard coded number
  410. if outlierTest == "MAD":
  411. X = np.fft.rfft( MADStack[pulse][chan][0,:] )
  412. nu = np.fft.fftfreq(len( MADStack[pulse][chan][0,:]), d=self.dt)
  413. else:
  414. X = np.fft.rfft( SimpleStack[pulse][chan][0,:] )
  415. nu = np.fft.fftfreq(len( SimpleStack[pulse][chan][0,:]), d=self.dt)
  416. nu = nu[0:len(X)]
  417. nu[-1] = np.abs(nu[-1])
  418. df = nu[1] - nu[0]
  419. of = 0
  420. istart = int((self.transFreq-50.)/df)
  421. iend = int((self.transFreq+50.)/df)
  422. of = nu[istart]
  423. def freqlabel(xxx, pos):
  424. return '%1.0f' %(of + xxx*df)
  425. formatter = FuncFormatter(freqlabel)
  426. SFFT = np.zeros( (self.DATADICT["nPulseMoments"], len(X)), dtype=np.complex64 )
  427. SFFT[0,:] = X
  428. for ipm in range(1, self.DATADICT["nPulseMoments"]):
  429. if outlierTest == "MAD":
  430. SFFT[ipm,:] = np.fft.rfft( MADStack[pulse][chan][ipm,:] )
  431. else:
  432. SFFT[ipm,:] = np.fft.rfft( SimpleStack[pulse][chan][ipm,:] )
  433. # convert to dB and add colorbars
  434. #db = 20.*np.log10(np.abs(SFFT[:,istart:iend]))
  435. db = (np.abs(SFFT[:,istart:iend]))
  436. #db = (np.real(SFFT[:,istart:iend]))
  437. #db = (np.imag(SFFT[:,istart:iend]))
  438. #dbr = (np.real(SFFT[:,istart:iend]))
  439. #db = (np.imag(SFFT[:,istart:iend]))
  440. vvmin = min(vvmin, np.min(db) + 1e-16 )
  441. vvmax = max(vvmax, np.max(db) + 1e-16 )
  442. im2.append(ax2.matshow( db, aspect='auto', cmap=cmocean.cm.ice, vmin=vvmin, vmax=vvmax))
  443. #im1.append(ax1.matshow( dbr, aspect='auto')) #, vmin=vvmin, vmax=vvmax))
  444. #im2.append(ax2.matshow( db, aspect='auto', vmin=vvmin, vmax=vvmax))
  445. #im2 = ax2.matshow( db, aspect='auto', cmap=cmocean.cm.ice, vmin=vvmin, vmax=vvmax)
  446. if ichan == 0:
  447. #ax2.set_ylabel(r"$q$ (A $\cdot$ s)", fontsize=8)
  448. ax2.set_ylabel(r"pulse index", fontsize=10)
  449. #ax1.set_ylabel(r"FID (nV)", fontsize=8)
  450. else:
  451. #ax2.yaxis.set_ticklabels([])
  452. plt.setp(ax2.get_yticklabels(), visible=False)
  453. ax2.xaxis.set_major_formatter(formatter)
  454. ax2.xaxis.set_ticks_position('bottom')
  455. ax2.xaxis.set_major_locator(MaxNLocator(3))
  456. y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
  457. ax2.yaxis.set_major_formatter(y_formatter)
  458. #if chan == self.DATADICT[pulse]["chan"][-1]:
  459. #cb2 = canvas.fig.colorbar(im2, cax=cbar_ax, format='%1.0e')
  460. #cb2 = canvas.fig.colorbar(im2[0], ax=ax2, format='%1.0e', orientation='horizontal')
  461. #cb2 = canvas.fig.colorbar(im2, ax=ax2, format='%1.0e', orientation='horizontal')
  462. #cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  463. #cb2.set_label("signal (dB)", fontsize=8)
  464. ichan += 1
  465. canvas.fig.subplots_adjust(hspace=.35, wspace=.15, left=.15, right=.8 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  466. deSpine(ax1)
  467. #cb1 = canvas.fig.colorbar(im, ax=axes[0::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  468. #cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  469. #cb1.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  470. cb2 = canvas.fig.colorbar(im2[-1], ax=axes[1::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  471. cb2.ax.tick_params(axis='both', which='major', labelsize=fs)
  472. cb2.set_label(r"$\left| \mathcal{V}_N \right|$ (nV)", fontsize=fs)
  473. #canvas.fig.tight_layout()
  474. canvas.draw()
  475. self.doneTrigger.emit()
  476. def harmonicModel(self, nF, \
  477. f0, f0K1, f0KN, f0Ks, f0ns, \
  478. f1, f1K1, f1KN, f1Ks, \
  479. Nsearch, Bounds, procRefs, \
  480. plot, canvas):
  481. """ nF = number of base frequencies, must be 1 or 2
  482. f0 = first base frequency
  483. f0K1 = first harmonic to model for first base frequency
  484. f0KN = last harmonic to model for the first base frequency
  485. f0Ks = subharmonic spacing, set to 1 for no subharmonics.
  486. f0Ns = number of segments for f0
  487. f1 = second base frequency
  488. f1K1 = first harmonic to model for second base frequency
  489. f1KN = last harmonic to model for the second base frequency
  490. f1Ks = subharmonic spacing for the second base frequency, set to 1 for no subharmonics.
  491. Nsearch = the number of harmonics to use when determining base frequency
  492. bounds = 1/2 the width of the space where baseline frequency will be searched
  493. procRefs = should the reference loops be processed as well
  494. plot = should Akvo plot the results
  495. canvas = mpl plotting axis
  496. """
  497. TDPlot = True
  498. fs = 10
  499. if plot:
  500. canvas.reAx2(shy=False)
  501. canvas.ax1.set_ylabel(r"signal (nV)", fontsize=fs)
  502. canvas.ax2.set_ylabel(r"signal (nV)", fontsize=fs)
  503. if TDPlot:
  504. canvas.ax2.set_xlabel(r"time (s)", fontsize=fs)
  505. else:
  506. canvas.ax2.set_xlabel(r"frequency (Hz)", fontsize=fs)
  507. canvas.ax1.set_yscale('log')
  508. canvas.ax2.set_yscale('log')
  509. # Data
  510. iFID = 0
  511. # stores previous f0 as starting point in non-linear search
  512. f0p = {}
  513. f1p = {}
  514. for pulse in self.DATADICT["PULSES"]:
  515. for rchan in self.DATADICT[pulse]["rchan"]:
  516. f0p[rchan] = f0
  517. f1p[rchan] = f1+1e-1
  518. for chan in self.DATADICT[pulse]["chan"]:
  519. f0p[chan] = f0
  520. f1p[chan] = f1+1e-1
  521. for pulse in self.DATADICT["PULSES"]:
  522. Nseg = int( np.floor(len( self.DATADICT[pulse]["TIMES"] ) / f0ns) )
  523. for istack in self.DATADICT["stacks"]:
  524. for ipm in range(self.DATADICT["nPulseMoments"]):
  525. if plot:
  526. canvas.softClear()
  527. mmaxr = 0
  528. mmaxd = 0
  529. if procRefs:
  530. for ichan in self.DATADICT[pulse]["rchan"]:
  531. if TDPlot:
  532. canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack], alpha=.5)
  533. mmaxr = max( mmaxr, np.max(1e9*self.DATADICT[pulse][ichan][ipm][istack]))
  534. else:
  535. ww = np.fft.fftfreq(len(self.DATADICT[pulse][ichan][ipm][istack]), d=self.dt)
  536. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  537. canvas.ax1.plot(np.abs(ww[0:len(X)]), np.abs(X), alpha=.5)
  538. canvas.ax1.set_prop_cycle(None)
  539. canvas.ax1.set_ylim(-mmaxr, mmaxr)
  540. for ichan in self.DATADICT[pulse]["chan"]:
  541. if TDPlot:
  542. canvas.ax2.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack], alpha=.5)
  543. mmaxd = max( mmaxd, np.max(1e9*self.DATADICT[pulse][ichan][ipm][istack]))
  544. else:
  545. ww = np.fft.fftfreq(len(self.DATADICT[pulse][ichan][ipm][istack]), d=self.dt)
  546. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  547. canvas.ax2.plot(np.abs(ww[0:len(X)]), np.abs(X), alpha=.5)
  548. canvas.ax2.set_prop_cycle(None)
  549. canvas.ax2.set_ylim(-mmaxd, mmaxd)
  550. if procRefs:
  551. for ichan in self.DATADICT[pulse]["rchan"]:
  552. if nF == 1:
  553. for iseg in range(f0ns):
  554. if iseg < f0ns-1:
  555. self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg:(iseg+1)*Nseg], f0p[ichan] = \
  556. harmonic.minHarmonic( self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg:(iseg+1)*Nseg], \
  557. self.samp, self.DATADICT[pulse]["TIMES"][iseg*Nseg:(iseg+1)*Nseg], \
  558. f0p[ichan], f0K1, f0KN, f0Ks, Bounds, Nsearch )
  559. else:
  560. self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg::], f0p[ichan] = \
  561. harmonic.minHarmonic( self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg::], \
  562. self.samp, self.DATADICT[pulse]["TIMES"][iseg*Nseg::], \
  563. f0p[ichan], f0K1, f0KN, f0Ks, Bounds, Nsearch )
  564. elif nF == 2:
  565. for iseg in range(f0ns):
  566. if iseg < f0ns-1:
  567. self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg:(iseg+1)*Nseg], f0p[ichan], f1p[ichan] = \
  568. harmonic.minHarmonic2( self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg:(iseg+1)*Nseg],\
  569. self.samp, self.DATADICT[pulse]["TIMES"][iseg*Nseg:(iseg+1)*Nseg], \
  570. f0p[ichan], f0K1, f0KN, f0Ks, \
  571. f1p[ichan], f1K1, f1KN, f1Ks, Bounds, Nsearch )
  572. else:
  573. self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg::], f0p[ichan], f1p[ichan] = \
  574. harmonic.minHarmonic2( self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg::],\
  575. self.samp, self.DATADICT[pulse]["TIMES"][iseg*Nseg::], \
  576. f0p[ichan], f0K1, f0KN, f0Ks, \
  577. f1p[ichan], f1K1, f1KN, f1Ks, Bounds, Nsearch )
  578. # plot
  579. if plot:
  580. if TDPlot:
  581. canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  582. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " rchan=" + str(ichan))
  583. else:
  584. ww = np.fft.fftfreq(len(self.DATADICT[pulse][ichan][ipm][istack]), d=self.dt)
  585. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  586. canvas.ax1.plot(np.abs(ww[0:len(X)]), np.abs(X),\
  587. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " rchan=" + str(ichan))
  588. for ichan in self.DATADICT[pulse]["chan"]:
  589. if nF == 1:
  590. for iseg in range(f0ns):
  591. if iseg < f0ns-1:
  592. self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg:(iseg+1)*Nseg], f0p[ichan] = \
  593. harmonic.minHarmonic( self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg:(iseg+1)*Nseg],
  594. self.samp, self.DATADICT[pulse]["TIMES"][iseg*Nseg:(iseg+1)*Nseg], \
  595. f0p[ichan], f0K1, f0KN, f0Ks, Bounds, Nsearch )
  596. else:
  597. self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg::], f0p[ichan] = \
  598. harmonic.minHarmonic( self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg::],
  599. self.samp, self.DATADICT[pulse]["TIMES"][iseg*Nseg::], \
  600. f0p[ichan], f0K1, f0KN, f0Ks, Bounds, Nsearch )
  601. elif nF == 2:
  602. for iseg in range(f0ns):
  603. if iseg < f0ns-1:
  604. self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg:(iseg+1)*Nseg], f0p[ichan], f1p[ichan] = \
  605. harmonic.minHarmonic2( self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg:(iseg+1)*Nseg],\
  606. self.samp, self.DATADICT[pulse]["TIMES"][iseg*Nseg:(iseg+1)*Nseg], \
  607. f0p[ichan], f0K1, f0KN, f0Ks, \
  608. f1p[ichan], f1K1, f1KN, f1Ks, Bounds, Nsearch )
  609. else:
  610. self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg::], f0p[ichan], f1p[ichan] = \
  611. harmonic.minHarmonic2( self.DATADICT[pulse][ichan][ipm][istack][iseg*Nseg::],\
  612. self.samp, self.DATADICT[pulse]["TIMES"][iseg*Nseg::], \
  613. f0p[ichan], f0K1, f0KN, f0Ks, \
  614. f1p[ichan], f1K1, f1KN, f1Ks, Bounds, Nsearch )
  615. # plot
  616. if plot:
  617. if TDPlot:
  618. canvas.ax2.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  619. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " chan=" + str(ichan))
  620. else:
  621. ww = np.fft.fftfreq(len(self.DATADICT[pulse][ichan][ipm][istack]), d=self.dt)
  622. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  623. canvas.ax2.plot(np.abs(ww[0:len(X)]), np.abs(X), \
  624. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " chan=" + str(ichan))
  625. if plot:
  626. if procRefs:
  627. canvas.ax1.legend(prop={'size':fs}, loc='upper right')
  628. plt.setp(canvas.ax1.get_xticklabels(), visible=False)
  629. canvas.ax2.legend(prop={'size':fs}, loc='upper right')
  630. deSpine(canvas.ax1)
  631. deSpine(canvas.ax2)
  632. canvas.fig.tight_layout()
  633. canvas.draw()
  634. percent = (int)(1e2*((ipm+istack*self.nPulseMoments)/(self.nPulseMoments*len(self.DATADICT["stacks"]))))
  635. self.progressTrigger.emit(percent)
  636. iFID += 1
  637. self.doneTrigger.emit()
  638. self.updateProcTrigger.emit()
  639. self.doneTrigger.emit()
  640. def FDSmartStack(self, outlierTest, MADcutoff, canvas):
  641. print("FFT stuff")
  642. self.dataCubeFFT()
  643. Stack = {}
  644. # align phase cycling for stacking and modulate
  645. for pulse in self.DATADICT["PULSES"]:
  646. stack = np.zeros(( len(self.DATADICT[pulse]["chan"]), \
  647. self.DATADICT["nPulseMoments"],\
  648. len(self.DATADICT["stacks"]),\
  649. len(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0] ]["FFT"]["nu"])//2 + 1),\
  650. dtype=np.complex )
  651. for ipm in range(self.DATADICT["nPulseMoments"]):
  652. istack = 0
  653. for sstack in self.DATADICT["stacks"]:
  654. if self.pulseType == "FID" or pulse == "Pulse 2":
  655. mod = (-1)**(ipm%2) * (-1)**(sstack%2)
  656. elif self.pulseType == "4PhaseT1":
  657. mod = (-1)**(ipm%2) * (-1)**(((sstack-1)/2)%2)
  658. ichan = 0
  659. for chan in self.DATADICT[pulse]["chan"]:
  660. #stack[ichan,ipm,istack,:] += mod*self.DATADICT[pulse][chan][ipm][sstack]
  661. stack[ichan,ipm,istack,:] += mod*self.DATADICT[pulse][chan]["FFT"][sstack][ipm,:]
  662. ichan += 1
  663. istack += 1
  664. Stack[pulse] = stack
  665. #########################################
  666. # simple stack and plot of simple stack #
  667. ########################################https://faculty.apps.utah.edu/#
  668. canvas.reAxH2(np.shape(stack)[0], False, False)
  669. axes = canvas.fig.axes
  670. SimpleStack = {}
  671. VarStack = {}
  672. for pulse in self.DATADICT["PULSES"]:
  673. SimpleStack[pulse] = {}
  674. VarStack[pulse] = {}
  675. ichan = 0
  676. for chan in self.DATADICT[pulse]["chan"]:
  677. SimpleStack[pulse][chan] = 1e9*np.average( Stack[pulse][ichan], 1 )
  678. VarStack[pulse][chan] = 1e9*np.std( Stack[pulse][ichan], 1 )
  679. ax1 = axes[ 2*ichan ]
  680. #ax1.get_yaxis().get_major_formatter().set_useOffset(False)
  681. y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
  682. ax1.yaxis.set_major_formatter(y_formatter)
  683. #ax1.plot( 1e3*self.DATADICT[pulse][chan]["FFT"]["nu"][0:len(SimpleStack[pulse][chan])], np.average(SimpleStack[pulse][chan], 0 )) #, color='darkblue' )
  684. #ax1.pcolor( np.real(SimpleStack[pulse][chan]) ) #, color='darkblue' )
  685. ax1.matshow( np.real(SimpleStack[pulse][chan]), aspect='auto') #, color='darkblue' )
  686. ax1.set_title("Ch." + str(chan) + ": avg FID", fontsize=10)
  687. ax1.set_xlabel(r"time (ms)", fontsize=10)
  688. if ichan == 0:
  689. ax1.set_ylabel(r"signal [nV]", fontsize=10)
  690. else:
  691. plt.setp(ax1.get_yticklabels(), visible=False)
  692. plt.setp(ax1.get_yaxis().get_offset_text(), visible=False)
  693. ichan += 1
  694. #########################
  695. # Oulier rejectig stack #
  696. #########################
  697. if outlierTest == "MAD":
  698. MADStack = {}
  699. VarStack = {}
  700. #1.4826 is assumption of gaussian noise
  701. madstack = np.zeros(( len(self.DATADICT[pulse]["chan"]),\
  702. self.DATADICT["nPulseMoments"],\
  703. len(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0] ]["FFT"]["nu"])//2 + 1))
  704. varstack = np.zeros(( len(self.DATADICT[pulse]["chan"]),\
  705. self.DATADICT["nPulseMoments"],\
  706. len(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0] ]["FFT"]["nu"])//2 + 1))
  707. for pulse in self.DATADICT["PULSES"]:
  708. MADStack[pulse] = {}
  709. VarStack[pulse] = {}
  710. ichan = 0
  711. for chan in self.DATADICT[pulse]["chan"]:
  712. ax1 = axes[ 2*ichan ]
  713. for ipm in range(self.DATADICT["nPulseMoments"]):
  714. # # brutal loop over time, can this be vectorized?
  715. # for it in range(len(self.DATADICT[pulse]["TIMES"])):
  716. # x = 1e9 *Stack[pulse][ichan,ipm,:,it]
  717. # MAD = 1.4826 * np.median( np.abs(x-np.median(x)) )
  718. # good = 0
  719. # for istack in self.DATADICT["stacks"]:
  720. # if (np.abs(x[istack-1]-np.median(x))) / MAD < 2:
  721. # good += 1
  722. # madstack[ ichan, ipm, it ] += x[istack-1]
  723. # else:
  724. # pass
  725. # madstack[ichan, ipm, it] /= good
  726. # percent = int(1e2* (float)(ipm) / (float)(self.DATADICT["nPulseMoments"]) )
  727. # self.progressTrigger.emit(percent)
  728. # Vectorized version of above...much, much faster
  729. x = 1e9*copy.deepcopy(Stack[pulse][ichan][ipm,:,:]) # stack and time indices
  730. tile_med = np.tile( np.median(x, axis=0), (np.shape(x)[0],1))
  731. MAD = MADcutoff * np.median(np.abs(x - tile_med), axis=0)
  732. tile_MAD = np.tile( MAD, (np.shape(x)[0],1))
  733. good = np.abs(x-tile_med)/tile_MAD < 2. # 1.4826 # 2
  734. madstack[ichan][ipm] = copy.deepcopy( np.ma.masked_array(x, good != True).mean(axis=0) )
  735. varstack[ichan][ipm] = copy.deepcopy( np.ma.masked_array(x, good != True).std(axis=0) )
  736. # reporting
  737. percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) /
  738. (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
  739. self.progressTrigger.emit(percent)
  740. ax2 = axes[2*ichan+1] # TODO fix hard coded number
  741. #ax1.plot( 1e3*self.DATADICT[pulse]["TIMES"], np.average( madstack[ichan], 0 ))# , color='darkred')
  742. MADStack[pulse][chan] = madstack[ichan]
  743. VarStack[pulse][chan] = varstack[ichan]
  744. ax2.matshow( np.real(MADStack[pulse][chan]), aspect='auto') #, color='darkblue' )
  745. ichan += 1
  746. self.DATADICT["stack"] = MADStack
  747. else:
  748. self.DATADICT["stack"] = SimpleStack
  749. # #########################################
  750. # # Plot Fourier Transform representation #
  751. # #########################################
  752. #
  753. # # canvas.fig.subplots_adjust(right=0.8)
  754. # # cbar_ax = canvas.fig.add_axes([0.85, 0.1, 0.015, 0.355])
  755. # # cbar_ax.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  756. # im2 = []
  757. # im1 = []
  758. # for pulse in self.DATADICT["PULSES"]:
  759. # ichan = 0
  760. # axes = canvas.fig.axes
  761. # vvmin = 1e10
  762. # vvmax = 0
  763. # for chan in self.DATADICT[pulse]["chan"]:
  764. # ax1 = axes[2*ichan ]
  765. # ax2 = axes[2*ichan+1] # TODO fix hard coded number
  766. # if outlierTest == "MAD":
  767. # X = np.fft.rfft( MADStack[pulse][chan][0,:] )
  768. # nu = np.fft.fftfreq(len( MADStack[pulse][chan][0,:]), d=self.dt)
  769. # else:
  770. # X = np.fft.rfft( SimpleStack[pulse][chan][0,:] )
  771. # nu = np.fft.fftfreq(len( SimpleStack[pulse][chan][0,:]), d=self.dt)
  772. #
  773. # nu = nu[0:len(X)]
  774. # nu[-1] = np.abs(nu[-1])
  775. # df = nu[1] - nu[0]
  776. # of = 0
  777. #
  778. # istart = int((self.transFreq-50.)/df)
  779. # iend = int((self.transFreq+50.)/df)
  780. # of = nu[istart]
  781. #
  782. # def freqlabel(xxx, pos):
  783. # return '%1.0f' %(of + xxx*df)
  784. # formatter = FuncFormatter(freqlabel)
  785. #
  786. # SFFT = np.zeros( (self.DATADICT["nPulseMoments"], len(X)), dtype=np.complex64 )
  787. # SFFT[0,:] = X
  788. # for ipm in range(1, self.DATADICT["nPulseMoments"]):
  789. # if outlierTest == "MAD":
  790. # SFFT[ipm,:] = np.fft.rfft( MADStack[pulse][chan][ipm,:] )
  791. # else:
  792. # SFFT[ipm,:] = np.fft.rfft( SimpleStack[pulse][chan][ipm,:] )
  793. #
  794. # # convert to dB and add colorbars
  795. # #db = 20.*np.log10(np.abs(SFFT[:,istart:iend]))
  796. # db = (np.abs(SFFT[:,istart:iend]))
  797. # #db = (np.real(SFFT[:,istart:iend]))
  798. # #dbr = (np.real(SFFT[:,istart:iend]))
  799. # #db = (np.imag(SFFT[:,istart:iend]))
  800. #
  801. # vvmin = min(vvmin, np.min (db))
  802. # vvmax = max(vvmax, np.max (db))
  803. # im2.append(ax2.matshow( db, aspect='auto', cmap=cmocean.cm.ice, vmin=vvmin, vmax=vvmax))
  804. # #im1.append(ax1.matshow( dbr, aspect='auto')) #, vmin=vvmin, vmax=vvmax))
  805. # #im2.append(ax2.matshow( db, aspect='auto', vmin=vvmin, vmax=vvmax))
  806. # #im2 = ax2.matshow( db, aspect='auto', cmap=cmocean.cm.ice, vmin=vvmin, vmax=vvmax)
  807. # if ichan == 0:
  808. # ax2.set_ylabel(r"$q$ (A $\cdot$ s)", fontsize=8)
  809. # else:
  810. # #ax2.yaxis.set_ticklabels([])
  811. # plt.setp(ax2.get_yticklabels(), visible=False)
  812. #
  813. # ax2.xaxis.set_major_formatter(formatter)
  814. # ax2.xaxis.set_ticks_position('bottom')
  815. # ax2.xaxis.set_major_locator(MaxNLocator(3))
  816. #
  817. # y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
  818. # ax2.yaxis.set_major_formatter(y_formatter)
  819. #
  820. #
  821. # #if chan == self.DATADICT[pulse]["chan"][-1]:
  822. # #cb2 = canvas.fig.colorbar(im2, cax=cbar_ax, format='%1.0e')
  823. #
  824. # #cb2 = canvas.fig.colorbar(im2[0], ax=ax2, format='%1.0e', orientation='horizontal')
  825. # #cb2 = canvas.fig.colorbar(im2, ax=ax2, format='%1.0e', orientation='horizontal')
  826. # #cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  827. # #cb2.set_label("signal (dB)", fontsize=8)
  828. #
  829. # ichan += 1
  830. #
  831. #
  832. # canvas.fig.subplots_adjust(hspace=.1, wspace=.05, left=.075, right=.95 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  833. #
  834. # #cb1 = canvas.fig.colorbar(im, ax=axes[0::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  835. # #cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  836. # #cb1.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  837. #
  838. # cb2 = canvas.fig.colorbar(im2[-1], ax=axes[1::2], format='%1.0e', orientation='horizontal', shrink=.35, aspect=30)
  839. # cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  840. # cb2.set_label("$\mathcal{V}_N$ (nV)", fontsize=8)
  841. #canvas.fig.tight_layout()
  842. deSpine(ax1)
  843. canvas.draw()
  844. self.doneTrigger.emit()
  845. def sumData(self, canvas, plusminus, sumAll):
  846. print("In sumData", plusminus, sumAll)
  847. if plusminus == "sum":
  848. splus = "+"
  849. else:
  850. splus = "-"
  851. chans = copy.deepcopy(self.DATADICT[self.DATADICT["PULSES"][0]]["chan"]) #= np.array( ( self.DATADICT[pulse]["chan"][0], ) )
  852. nchan = len(chans)
  853. # Sum permutations of two channel combos
  854. for ich in range(nchan-1):
  855. for ch in chans[ich+1:]:
  856. chsum = chans[ich] + splus + ch
  857. for pulse in self.DATADICT["PULSES"]:
  858. self.DATADICT[pulse][chsum] = {}
  859. for ipm in range(self.DATADICT["nPulseMoments"]):
  860. self.DATADICT[pulse][chsum][ipm] = {}
  861. for istack in self.DATADICT["stacks"]:
  862. if plusminus == "sum":
  863. self.DATADICT[pulse][chsum][ipm][istack] = self.DATADICT[pulse][chans[ich]][ipm][istack] + self.DATADICT[pulse][ch][ipm][istack]
  864. else:
  865. self.DATADICT[pulse][chsum][ipm][istack] = self.DATADICT[pulse][chans[ich]][ipm][istack] - self.DATADICT[pulse][ch][ipm][istack]
  866. #if chsum == "1+2":
  867. #self.DATADICT[pulse]["rchan"].pop()
  868. #self.DATADICT[pulse]["rchan"].pop()
  869. self.DATADICT[pulse]["chan"].append(chsum)
  870. # Sum all channels
  871. #sumall = False
  872. if sumAll:
  873. chsum = ""
  874. for ch in chans:
  875. chsum += ch + "+"
  876. chsum = chsum[0:-1] # remove last "+"
  877. for pulse in self.DATADICT["PULSES"]:
  878. self.DATADICT[pulse][chsum] = {}
  879. for ipm in range(self.DATADICT["nPulseMoments"]):
  880. self.DATADICT[pulse][chsum][ipm] = {}
  881. for istack in self.DATADICT["stacks"]:
  882. self.DATADICT[pulse][chsum][ipm][istack] = copy.deepcopy(self.DATADICT[pulse][chans[0]][ipm][istack])
  883. for ch in chans[1:]:
  884. self.DATADICT[pulse][chsum][ipm][istack] += self.DATADICT[pulse][ch][ipm][istack]
  885. self.DATADICT[pulse]["chan"].append(chsum)
  886. # if nchan > 2:
  887. # for ch in chans:
  888. # chsum += ch
  889. # for ch2 in chans[1::]:
  890. # for pulse in self.DATADICT["PULSES"]:
  891. # self.DATADICT[pulse][chsum] = {}
  892. # for istack in self.DATADICT["stacks"]:
  893. # self.DATADICT[pulse][chsum][ipm][istack] = self.DATADICT[pulse][chans[ich]][ipm][istack] + self.DATADICT[pulse][ch][ipm][istack]
  894. self.doneTrigger.emit()
  895. def quadDet(self, clip, method, loss, canvas):
  896. from scipy import signal
  897. self.RotatedAmplitude = True
  898. wL = self.transFreq * 2*np.pi
  899. vL = self.transFreq
  900. #T = 50
  901. dt = self.dt
  902. #DT = 0.01
  903. CA = {} # corrected amplitude
  904. IP = {} # instantaneous phase
  905. NR = {} # Noise residual
  906. RE = {} # Real channel
  907. IM = {} # Imaginary channel
  908. # global maximums for plotting
  909. CAmax = {}
  910. NRmax = {}
  911. REmax = {}
  912. IMmax = {}
  913. E0,phi,df,T2 = 100.,0,0,.2
  914. first = False
  915. self.sigma = {}
  916. for pulse in self.DATADICT["PULSES"]:
  917. CA[pulse] = {}
  918. IP[pulse] = {}
  919. NR[pulse] = {}
  920. RE[pulse] = {}
  921. IM[pulse] = {}
  922. CAmax[pulse] = 0
  923. NRmax[pulse] = 0
  924. REmax[pulse] = 0
  925. IMmax[pulse] = 0
  926. ichan = 0
  927. self.sigma[pulse] = {}
  928. for chan in self.DATADICT[pulse]["chan"]:
  929. CA[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  930. IP[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  931. NR[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  932. RE[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  933. IM[pulse][chan] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT[pulse]["TIMES"])-clip ) )
  934. #QQ = np.average(self.DATADICT[pulse]["Q"], axis=1 )
  935. #for ipm in np.argsort(QQ):
  936. for ipm in range(0, self.DATADICT["nPulseMoments"]):
  937. #t = self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1]
  938. xn = self.DATADICT["stack"][pulse][chan][ipm,:]
  939. ht = signal.hilbert(xn)*np.exp(-1j*wL*self.DATADICT[pulse]["TIMES"])
  940. #############################################################
  941. # Quadrature signal
  942. RE[pulse][chan][ipm,:] = np.real(ht[clip::]) # *-1 for negative for consistency with VC ??
  943. IM[pulse][chan][ipm,:] = np.imag(ht[clip::])
  944. REmax[pulse] = max(REmax[pulse], np.max(np.real(ht[clip::])))
  945. IMmax[pulse] = max(IMmax[pulse], np.max(np.imag(ht[clip::])))
  946. #############################################################
  947. # Instantaneous phase
  948. IP[pulse][chan][ipm,:] = np.angle(ht)[clip::]
  949. #############################################################
  950. # Rotated amplitude
  951. if ipm != 0:
  952. [success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"], method, loss, (E0,phi,df,T2))
  953. #[success, E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
  954. else:
  955. [success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"], method, loss)
  956. #[success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"], (E0,phi,df,T2))
  957. #[success, E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
  958. #print("success", success, "E0", E0, "phi", phi, "df", df, "T2", T2)
  959. D = self.RotateAmplitude( ht.real, ht.imag, phi, df, self.DATADICT[pulse]["TIMES"] )
  960. CA[pulse][chan][ipm,:] = D.imag[clip::] # amplitude data
  961. NR[pulse][chan][ipm,:] = D.real[clip::] # noise data
  962. CAmax[pulse] = max(CAmax[pulse], np.max(D.imag[clip::]) )
  963. NRmax[pulse] = max(NRmax[pulse], np.max(D.real[clip::]) )
  964. self.sigma[pulse][chan] = np.std(NR[pulse][chan])
  965. # reporting
  966. percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) /
  967. (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
  968. self.progressTrigger.emit(percent)
  969. ichan += 1
  970. self.DATADICT[pulse]["TIMES"] = self.DATADICT[pulse]["TIMES"][clip::]
  971. self.DATADICT["CA"] = CA
  972. self.DATADICT["IP"] = IP
  973. self.DATADICT["NR"] = NR
  974. self.DATADICT["RE"] = RE
  975. self.DATADICT["IM"] = IM
  976. self.DATADICT["CAmax"] = CAmax
  977. self.DATADICT["NRmax"] = NRmax
  978. self.DATADICT["REmax"] = REmax
  979. self.DATADICT["IMmax"] = IMmax
  980. self.doneTrigger.emit()
  981. def plotQuadDet(self, clip, phase, canvas):
  982. canvas.reAxH2( len(self.DATADICT[ self.DATADICT["PULSES"][0] ]["chan"] ), False, False)
  983. ###############
  984. # Plot on GUI #
  985. ###############
  986. fs = 10
  987. dcmap = cmocean.cm.curl_r #"seismic_r" #cmocean.cm.balance_r #"RdBu" #YlGn" # "coolwarm_r" # diverging
  988. canvas.reAxH2( len(self.DATADICT[ self.DATADICT["PULSES"][0] ]["chan"] ), False, False)
  989. for pulse in self.DATADICT["PULSES"]:
  990. ichan = 0
  991. axes = canvas.fig.axes
  992. mmaxr = 0.
  993. mmaxi = 0.
  994. #if clip > 0:
  995. # time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"][clip-1::] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  996. #else:
  997. # time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  998. time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  999. QQ = np.average(self.DATADICT[pulse]["Q"], axis=1 )
  1000. iQ = np.argsort(QQ)
  1001. for chan in self.DATADICT[pulse]["chan"]:
  1002. ax1 = axes[2*ichan ]
  1003. ax2 = axes[2*ichan+1]
  1004. if phase == 0: # Re Im
  1005. im1 = ax1.pcolormesh( time_sp, QQ[iQ], self.DATADICT["RE"][pulse][chan][iQ], cmap=dcmap, \
  1006. vmin=-self.DATADICT["REmax"][pulse] , vmax=self.DATADICT["REmax"][pulse] , rasterized=True)
  1007. im2 = ax2.pcolormesh( time_sp, QQ[iQ], self.DATADICT["IM"][pulse][chan][iQ], cmap=dcmap, \
  1008. vmin=-self.DATADICT["IMmax"][pulse], vmax=self.DATADICT["IMmax"][pulse] , rasterized=True )
  1009. #im1 = ax1.matshow( self.DATADICT["RE"][pulse][chan][iQ], cmap=dcmap, aspect='auto', \
  1010. # vmin=-self.DATADICT["REmax"][pulse] , vmax=self.DATADICT["REmax"][pulse] )
  1011. #im2 = ax2.matshow( self.DATADICT["IM"][pulse][chan][iQ], cmap=dcmap, aspect='auto', \
  1012. # vmin=-self.DATADICT["REmax"][pulse] , vmax=self.DATADICT["REmax"][pulse] )
  1013. if phase == 1: # Amp phase
  1014. im1 = ax1.pcolormesh( time_sp, QQ[iQ], self.DATADICT["CA"][pulse][chan][iQ], cmap=dcmap, \
  1015. vmin=-self.DATADICT["CAmax"][pulse] , vmax=self.DATADICT["CAmax"][pulse], rasterized=True )
  1016. #im2 = ax2.pcolormesh( time_sp, QQ, self.DATADICT["IP"][pulse][chan], cmap=cmocean.cm.balance, rasterized=True,\
  1017. im2 = ax2.pcolormesh( time_sp, QQ[iQ], self.DATADICT["IP"][pulse][chan][iQ], cmap=cmocean.cm.delta, \
  1018. vmin=-np.pi, vmax=np.pi, rasterized=True)
  1019. if phase == 2: # CA NR
  1020. im1 = ax1.pcolormesh( time_sp, QQ[iQ], self.DATADICT["CA"][pulse][chan][iQ], cmap=dcmap, \
  1021. vmin=-self.DATADICT["CAmax"][pulse] , vmax=self.DATADICT["CAmax"][pulse], rasterized=True )
  1022. im2 = ax2.pcolormesh( time_sp, QQ[iQ], self.DATADICT["NR"][pulse][chan][iQ], cmap=dcmap, \
  1023. vmin=-self.DATADICT["NRmax"][pulse] , vmax=self.DATADICT["NRmax"][pulse], rasterized=True )
  1024. # cb2 = canvas.fig.colorbar(im2, ax=ax2, format='%1.0e')
  1025. # cb2.set_label("Noise residual (nV)", fontsize=8)
  1026. # cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  1027. # cb1 = canvas.fig.colorbar(im1, ax=ax1, format='%1.0e')
  1028. # cb1.set_label("Phased amplitude (nV)", fontsize=8)
  1029. # cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  1030. # cb2 = canvas.fig.colorbar(im2, ax=ax2, format="%1.0e")
  1031. # cb2.set_label("Phase (rad)", fontsize=8)
  1032. # cb2.ax.tick_params(axis='both', which='major', labelsize=8)
  1033. # cb1 = canvas.fig.colorbar(im1, ax=ax1, format="%1.0e")
  1034. # cb1.set_label("FID (nV)", fontsize=8)
  1035. # cb1.ax.tick_params(axis='both', which='major', labelsize=8)
  1036. # if you save these as pdf or eps, there are artefacts
  1037. # for cbar in [cb1,cb2]:
  1038. # #cbar.solids.set_rasterized(True)
  1039. # cbar.solids.set_edgecolor("face")
  1040. # reporting
  1041. percent = int(1e2* (float)(ichan)/len(self.DATADICT[pulse]["chan"]))
  1042. self.progressTrigger.emit(percent)
  1043. if ichan == 0:
  1044. ax1.set_ylabel(r"$q$ ( $\mathrm{A}\cdot\mathrm{s}$)", fontsize=fs)
  1045. ax2.set_ylabel(r"$q$ ( $\mathrm{A}\cdot\mathrm{s}$)", fontsize=fs)
  1046. else:
  1047. #ax2.yaxis.set_ticklabels([])
  1048. #ax1.yaxis.set_ticklabels([])
  1049. plt.setp(ax1.get_yticklabels(), visible=False)
  1050. plt.setp(ax2.get_yticklabels(), visible=False)
  1051. ichan += 1
  1052. ax1.set_yscale('log')
  1053. ax2.set_yscale('log')
  1054. plt.setp(ax1.get_xticklabels(), visible=False)
  1055. ax1.set_ylim( np.min(QQ), np.max(QQ) )
  1056. ax2.set_ylim( np.min(QQ), np.max(QQ) )
  1057. ax1.set_xlim( np.min(time_sp), np.max(time_sp) )
  1058. ax2.set_xlim( np.min(time_sp), np.max(time_sp) )
  1059. #ax2.set_xlabel(r"Time since end of pulse (ms)", fontsize=8)
  1060. ax2.set_xlabel(r"time (ms)", fontsize=fs)
  1061. #canvas.fig.subplots_adjust(hspace=.15, wspace=.05, left=.15, right=.85, bottom=.15, top=.9 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  1062. canvas.fig.subplots_adjust(hspace=.15, wspace=.05, left=.15, right=.90, bottom=.15, top=.95 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  1063. tick_locator = MaxNLocator(nbins=3)
  1064. cb1 = canvas.fig.colorbar(im1, ax=axes[0::2], format='%1.0f', orientation='vertical')
  1065. #cb1 = canvas.fig.colorbar(im1, ax=axes[0::2], format='%1.0f', orientation='horizontal', shrink=.35, aspect=30, pad=.4)
  1066. cb1.ax.tick_params(axis='both', which='major', labelsize=fs)
  1067. cb1.locator = tick_locator
  1068. cb1.update_ticks()
  1069. tick_locator2 = MaxNLocator(nbins=3)
  1070. cb2 = canvas.fig.colorbar(im2, ax=axes[1::2], format='%1.0f', orientation='vertical')
  1071. #cb2 = canvas.fig.colorbar(im2, ax=axes[1::2], format='%1.0f', orientation='horizontal', shrink=.35, aspect=30, pad=.4)
  1072. cb2.ax.tick_params(axis='both', which='major', labelsize=fs)
  1073. if phase == 0: # Re Im
  1074. cb1.set_label(r"$\mathrm{Re} \left( \mathcal{V}_N \right)$ (nV)", fontsize=fs)
  1075. cb2.set_label(r"$\mathrm{Im} \left( \mathcal{V}_N \right)$ (nV)", fontsize=fs)
  1076. elif phase == 1: # Amp phase
  1077. cb1.set_label(r"$\left| \mathcal{V}_N \right|$ (nV)", fontsize=fs)
  1078. cb2.set_label(r"$\angle \mathcal{V}_N$", fontsize=fs)
  1079. else:
  1080. cb1.set_label(r"$\left| \mathcal{V}_N \right|$ (nV)", fontsize=fs)
  1081. cb2.set_label(r"noise (nV)", fontsize=fs)
  1082. cb2.locator = tick_locator2
  1083. cb2.update_ticks()
  1084. #canvas.fig.tight_layout()
  1085. canvas.draw()
  1086. self.doneTrigger.emit()
  1087. def RotateAmplitude(self, X, Y, zeta, df, t):
  1088. V = X + 1j*Y
  1089. return np.abs(V) * np.exp( 1j * ( np.angle(V) - zeta - 2.*np.pi*df*t ) )
  1090. def gateIntegrate( self, gpd, clip, canvas ):
  1091. """ Gate integrate the real, imaginary, phased, and noise residual channels
  1092. """
  1093. self.gated = True
  1094. self.GATED = {}
  1095. for pulse in self.DATADICT["PULSES"]:
  1096. QQ = np.average(self.DATADICT[pulse]["Q"], axis=1 )
  1097. iQ = np.argsort(QQ)
  1098. ichan = 0
  1099. for chan in self.DATADICT[pulse]["chan"]:
  1100. self.GATED[chan] = {}
  1101. for ipm in range(0, self.DATADICT["nPulseMoments"]):
  1102. #for ipm in iQ:
  1103. # Time since pulse end rather than since record starts...
  1104. #if clip > 0:
  1105. # time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"][clip:] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  1106. #else:
  1107. time_sp = 1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
  1108. #GT, GD, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  1109. #GT2, GP, GTT, sig_stack_err, isum = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
  1110. # err
  1111. GT, GCA, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 2 )
  1112. GT, GNR, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 2 )
  1113. GT, GRE, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["RE"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 2 )
  1114. GT, GIM, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["IM"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 2 )
  1115. GT, GIP, GTT, sig_stack, isum = rotate.gateIntegrate( self.DATADICT["IP"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 2 )
  1116. #if ipm == iQ[0]:
  1117. if ipm == 0:
  1118. # self.GATED[chan]["DATA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
  1119. # self.GATED[chan]["ERR"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
  1120. # self.GATED[chan]["SIGMA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
  1121. self.GATED[chan]["CA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  1122. self.GATED[chan]["NR"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  1123. self.GATED[chan]["BN"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  1124. self.GATED[chan]["RE"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  1125. self.GATED[chan]["IM"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  1126. self.GATED[chan]["IP"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
  1127. self.GATED[chan]["isum"] = isum
  1128. # Bootstrap noise
  1129. #self.GATED[chan]["isum"]
  1130. Means = rotate.bootstrapWindows( self.DATADICT["NR"][pulse][chan][ipm], 20000, isum[isum!=1], adapt=True)
  1131. # MAD, only for windows > 1
  1132. c = stats.norm.ppf(3./4.)
  1133. sig_stack[isum!=1] = np.ma.median(np.ma.abs(Means), axis=1) / c
  1134. self.GATED[chan]["BN"][ipm] = sig_stack[clip:]
  1135. #self.GATED[chan]["DATA"][ipm] = GD.real
  1136. self.GATEDABSCISSA = GT[clip:]
  1137. self.GATEDWINDOW = GTT[clip:]
  1138. #self.GATED[chan]["SIGMA"][ipm] = sig_stack #_err # GP.real
  1139. #self.GATED[chan]["ERR"][ipm] = GP.real
  1140. #self.GATED[chan]["CA"][iQ[ipm]] = GCA.real[clip:]
  1141. #self.GATED[chan]["NR"][iQ[ipm]] = GNR.real[clip:]
  1142. #self.GATED[chan]["RE"][iQ[ipm]] = GRE.real[clip:]
  1143. #self.GATED[chan]["IM"][iQ[ipm]] = GIM.real[clip:]
  1144. #self.GATED[chan]["IP"][iQ[ipm]] = GIP.real[clip:]
  1145. self.GATED[chan]["CA"][ipm] = GCA.real[clip:]
  1146. self.GATED[chan]["NR"][ipm] = GNR.real[clip:]
  1147. self.GATED[chan]["RE"][ipm] = GRE.real[clip:]
  1148. self.GATED[chan]["IM"][ipm] = GIM.real[clip:]
  1149. self.GATED[chan]["IP"][ipm] = GIP.real[clip:]
  1150. percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) /
  1151. (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
  1152. self.progressTrigger.emit(percent)
  1153. self.GATED[chan]["CA"] = self.GATED[chan]["CA"][iQ,:]
  1154. self.GATED[chan]["NR"] = self.GATED[chan]["NR"][iQ,:]
  1155. self.GATED[chan]["RE"] = self.GATED[chan]["RE"][iQ,:]
  1156. self.GATED[chan]["IM"] = self.GATED[chan]["IM"][iQ,:]
  1157. self.GATED[chan]["IP"] = self.GATED[chan]["IP"][iQ,:]
  1158. self.GATED[chan]["GTT"] = GTT[clip:]
  1159. self.GATED[chan]["GT"] = GT[clip:]
  1160. self.GATED[chan]["QQ"] = QQ[iQ]
  1161. ichan += 1
  1162. self.doneTrigger.emit()
  1163. def bootstrap_resample(self, X, n=None):
  1164. # from http://nbviewer.jupyter.org/gist/aflaxman/6871948
  1165. """ Bootstrap resample an array_like
  1166. Parameters
  1167. ----------
  1168. X : array_like
  1169. data to resample
  1170. n : int, optional
  1171. length of resampled array, equal to len(X) if n==None
  1172. Results
  1173. -------
  1174. returns X_resamples
  1175. """
  1176. if n == None:
  1177. n = len(X)
  1178. resample_i = np.floor(np.random.rand(n)*len(X)).astype(int)
  1179. return X[resample_i]
  1180. def bootstrap_sigma(self, pulse, chan):
  1181. # bootstrap resample
  1182. nt = len(self.GATED[chan]["GT"])
  1183. nb = 5000
  1184. XS = np.zeros( (nb, nt) )
  1185. for ii in range(nb):
  1186. for it in range(nt):
  1187. if self.GATED[chan]["isum"][it] < 8:
  1188. XS[ii, it] = self.sigma[pulse][chan]
  1189. else:
  1190. if it == 0:
  1191. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it], \
  1192. self.GATED[chan]["NR"][:,it+1], \
  1193. self.GATED[chan]["NR"][:,it+2], \
  1194. self.GATED[chan]["NR"][:,it+3] ) ), n=nt )
  1195. elif it == 1:
  1196. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it-1], self.GATED[chan]["NR"][:,it], \
  1197. self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it+2] ) ), n=nt )
  1198. elif it == nt-2:
  1199. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it], \
  1200. self.GATED[chan]["NR"][:,it-1], self.GATED[chan]["NR"][:,it-2] ) ), n=nt )
  1201. elif it == nt-1:
  1202. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it-1], \
  1203. self.GATED[chan]["NR"][:,it-2], self.GATED[chan]["NR"][:,it-3] ) ), n=nt )
  1204. else:
  1205. X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it-2] , self.GATED[chan]["NR"][:,it-1], \
  1206. self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it+2] )), n=nt )
  1207. XS[ii,it] = np.std(X)
  1208. return XS
  1209. def plotGateIntegrate( self, gpd, clip, phase, canvas ):
  1210. """ Plot the gate integration
  1211. """
  1212. fs = 10
  1213. canvas.reAxH2( len(self.DATADICT[ self.DATADICT["PULSES"][0] ]["chan"] ), False, False)
  1214. axes = canvas.fig.axes
  1215. #cmap = cmocean.cm.balance_r
  1216. dcmap = cmocean.cm.curl_r #"seismic_r" #cmocean.cm.balance_r #"RdBu" #YlGn" # "coolwarm_r" # diverging
  1217. # Calculate maximum for plotting...TODO move into loop above
  1218. vmax1 = 0
  1219. vmax2 = 0
  1220. for pulse in self.DATADICT["PULSES"]:
  1221. for chan in self.DATADICT[pulse]["chan"]:
  1222. if phase == 0:
  1223. vmax1 = max(vmax1, np.max(np.abs(self.GATED[chan]["RE"])))
  1224. vmax2 = max(vmax2, np.max(np.abs(self.GATED[chan]["IM"])))
  1225. elif phase == 1:
  1226. vmax1 = max(vmax1, np.max(np.abs(self.GATED[chan]["CA"])))
  1227. vmax2 = np.pi
  1228. elif phase == 2:
  1229. vmax1 = max(vmax1, np.max(np.abs(self.GATED[chan]["CA"])))
  1230. vmax2 = max(vmax2, np.max(np.abs(self.GATED[chan]["NR"])))
  1231. for pulse in self.DATADICT["PULSES"]:
  1232. ichan = 0
  1233. for chan in self.DATADICT[pulse]["chan"]:
  1234. ax1 = axes[2*ichan ]
  1235. ax2 = axes[2*ichan+1]
  1236. if phase == 0:
  1237. im1 = ax1.pcolormesh(self.GATED[chan]["GT"], self.GATED[chan]["QQ"], self.GATED[chan]["RE"], shading='nearest', cmap=dcmap, vmin=-vmax1, vmax=vmax1)
  1238. im2 = ax2.pcolormesh(self.GATED[chan]["GT"], self.GATED[chan]["QQ"], self.GATED[chan]["IM"], shading='nearest', cmap=dcmap, vmin=-vmax2, vmax=vmax2)
  1239. #im1 = ax1.matshow(self.GATED[chan]["RE"], cmap=dcmap, vmin=-vmax1, vmax=vmax1, aspect='auto')
  1240. #im2 = ax2.matshow(self.GATED[chan]["IM"], cmap=dcmap, vmin=-vmax2, vmax=vmax2, aspect='auto')
  1241. elif phase == 1:
  1242. im1 = ax1.pcolormesh(self.GATED[chan]["GT"], self.GATED[chan]["QQ"], self.GATED[chan]["CA"], shading='nearest', cmap=dcmap, vmin=-vmax1, vmax=vmax1)
  1243. im2 = ax2.pcolormesh(self.GATED[chan]["GT"], self.GATED[chan]["QQ"], self.GATED[chan]["IP"], shading='nearest', cmap=cmocean.cm.delta, vmin=-vmax2, vmax=vmax2)
  1244. #im2 = ax2.pcolormesh(self.GATED[chan]["GTT"], self.GATED[chan]["QQ"], self.GATED[chan]["IP"], cmap=cmocean.cm.phase, vmin=-vmax2, vmax=vmax2)
  1245. elif phase == 2:
  1246. im1 = ax1.pcolormesh(self.GATED[chan]["GT"], self.GATED[chan]["QQ"], self.GATED[chan]["CA"], shading='nearest', cmap=dcmap, vmin=-vmax1, vmax=vmax1)
  1247. #XS = self.bootstrap_sigma(pulse, chan)
  1248. #im2 = ax2.pcolormesh(self.GATED[chan]["GTT"], self.GATED[chan]["QQ"], self.GATED[chan]["NR"], cmap=cmap, vmin=-vmax2, vmax=vmax2)
  1249. # bootstrap resample
  1250. # nt = len(self.GATED[chan]["GT"])
  1251. # nb = 5000
  1252. # XS = np.zeros( (nb, nt) )
  1253. # for ii in range(nb):
  1254. # #XS = []
  1255. # for it in range(nt):
  1256. # if self.GATED[chan]["isum"][it] < 8:
  1257. # XS[ii, it] = self.sigma[pulse][chan]
  1258. # else:
  1259. # if it == 0:
  1260. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it+1], \
  1261. # self.GATED[chan]["NR"][:,it+2], self.GATED[chan]["NR"][:,it+3] ) ), n=nt )
  1262. # if it == 1:
  1263. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it-1], self.GATED[chan]["NR"][:,it], \
  1264. # self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it+2] ) ), n=nt )
  1265. # elif it == nt-2:
  1266. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it], \
  1267. # self.GATED[chan]["NR"][:,it-1], self.GATED[chan]["NR"][:,it-2] ) ), n=nt )
  1268. # elif it == nt-1:
  1269. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it-1], \
  1270. # self.GATED[chan]["NR"][:,it-2], self.GATED[chan]["NR"][:,it-3] ) ), n=nt )
  1271. # else:
  1272. # X = self.bootstrap_resample( np.concatenate( (self.GATED[chan]["NR"][:,it-2] , self.GATED[chan]["NR"][:,it-1], \
  1273. # self.GATED[chan]["NR"][:,it], self.GATED[chan]["NR"][:,it+1], self.GATED[chan]["NR"][:,it+2] )), n=nt )
  1274. # XS[ii,it] = np.std(X)
  1275. #if ii == 0:
  1276. # ax2.plot( self.GATED[chan]["GT"], XS[ii], '-', linewidth=1, markersize=4, alpha=.5, color='lightgrey', label = "bootstrap sim" )
  1277. #else:
  1278. # ax2.plot( self.GATED[chan]["GT"], XS[ii], '-', linewidth=1, markersize=4, alpha=.5, color='lightgrey' )
  1279. ax2.plot( self.GATED[chan]["GT"], np.std(self.GATED[chan]["NR"], axis=0), color='darkgrey', linewidth=2, label="gate std" )
  1280. ax2.plot( self.GATED[chan]["GT"], np.average(self.GATED[chan]["BN"], axis=0), color='black', linewidth=2, label="boot average" )
  1281. #ax2.plot( np.tile(self.GATED[chan]["GT"], (5000,1) ), XS, '.', color='lightgrey', linewidth=1, alpha=.5 )
  1282. #ax2.plot( self.GATED[chan]["GT"], np.average(XS, axis=0), color='black', linewidth=2, label="bootstrap avg." )
  1283. #ax2.plot( self.GATED[chan]["GT"], np.median(XS, axis=0), color='black', linewidth=2, label="bootstrap med." )
  1284. ax2.legend()
  1285. im1.set_edgecolor('face')
  1286. if phase != 2:
  1287. im2.set_edgecolor('face')
  1288. plt.setp(ax1.get_xticklabels(), visible=False)
  1289. ax1.set_ylim( np.min(self.GATED[chan]["QQ"]), np.max(self.GATED[chan]["QQ"]) )
  1290. if phase != 2:
  1291. ax2.set_ylim( np.min(self.GATED[chan]["QQ"]), np.max(self.GATED[chan]["QQ"]) )
  1292. ax1.set_xlim( np.min(self.GATED[chan]["GTT"]), np.max(self.GATED[chan]["GTT"]) )
  1293. ax2.set_xlim( np.min(self.GATED[chan]["GTT"]), np.max(self.GATED[chan]["GTT"]) )
  1294. ax1.set_yscale('log')
  1295. ax2.set_yscale('log')
  1296. qlabs = np.append(np.concatenate( ( self.GATED[chan]["QQ"][0:1], self.GATED[chan]["QQ"][9::10] )), self.GATED[chan]["QQ"][-1] )
  1297. ax1.yaxis.set_ticks( qlabs ) # np.append(np.concatenate( (QQ[0:1],QQ[9::10] )), QQ[-1] ) )
  1298. if phase != 2:
  1299. ax2.yaxis.set_ticks( qlabs ) #np.append(np.concatenate( (QQ[0:1],QQ[9::10] )), QQ[-1] ) )
  1300. #formatter = matplotlib.ticker.LogFormatter(10, labelOnlyBase=False)
  1301. formatter = matplotlib.ticker.FuncFormatter(lambda x, pos: str((round(x,1))))
  1302. ax1.set_xscale('log')
  1303. ax2.set_xscale('log')
  1304. ax1.yaxis.set_major_formatter(formatter) #matplotlib.ticker.FormatStrFormatter('%d.1'))
  1305. ax2.yaxis.set_major_formatter(formatter) #matplotlib.ticker.FormatStrFormatter('%d.1'))
  1306. ax1.xaxis.set_major_formatter(formatter) #matplotlib.ticker.FormatStrFormatter('%d.1'))
  1307. ax2.xaxis.set_major_formatter(formatter) #matplotlib.ticker.FormatStrFormatter('%d.1'))
  1308. if ichan == 0:
  1309. ax1.set_ylabel(r"$q$ ( $\mathrm{A}\cdot\mathrm{s}$)", fontsize=fs)
  1310. if phase == 2:
  1311. ax2.set_ylabel(r"noise est. (nV)", fontsize=fs)
  1312. else:
  1313. ax2.set_ylabel(r"$q$ ( $\mathrm{A}\cdot\mathrm{s}$)", fontsize=fs)
  1314. else:
  1315. plt.setp(ax1.get_yticklabels(), visible=False)
  1316. plt.setp(ax2.get_yticklabels(), visible=False)
  1317. ax2.set_xlabel(r"$t-\tau_p$ (ms)", fontsize=fs)
  1318. ichan += 1
  1319. #canvas.fig.subplots_adjust(hspace=.1, wspace=.05, left=.075, right=.925 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  1320. #canvas.fig.tight_layout()
  1321. #canvas.draw()
  1322. canvas.fig.subplots_adjust(hspace=.15, wspace=.05, left=.15, right=.9, bottom=.1, top=.9 )#left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  1323. tick_locator = MaxNLocator(nbins=5)
  1324. cb1 = canvas.fig.colorbar(im1, ax=axes[0::2], format='%1.0f', orientation='horizontal', shrink=.35, aspect=30)
  1325. cb1.ax.tick_params(axis='both', which='major', labelsize=fs)
  1326. cb1.set_label("$\mathcal{V}_N$ (nV)", fontsize=fs)
  1327. #cb1.locator = tick_locator
  1328. #cb1.update_ticks()
  1329. if phase != 2:
  1330. cb2 = canvas.fig.colorbar(im2, ax=axes[1::2], format='%1.0f', orientation='horizontal', shrink=.35, aspect=30, pad=.2)
  1331. cb2.ax.tick_params(axis='both', which='major', labelsize=fs)
  1332. cb2.set_label("$\mathcal{V}_N$ (nV)", fontsize=fs)
  1333. cb2.locator = tick_locator
  1334. cb2.update_ticks()
  1335. canvas.draw()
  1336. self.doneTrigger.emit()
  1337. def FDSmartStack(self, cv, canvas):
  1338. from matplotlib.colors import LogNorm
  1339. from matplotlib.ticker import MaxNLocator
  1340. """
  1341. Currently this stacks 4-phase second pulse data only, we need to generalise
  1342. """
  1343. try:
  1344. canvas.fig.clear()
  1345. except:
  1346. pass
  1347. self.dataCubeFFT( )
  1348. # canvas.ax1 = canvas.fig.add_axes([.1, .1, .8, .8])
  1349. canvas.ax1 = canvas.fig.add_axes([.1, .1, .2, .8])
  1350. canvas.ax2 = canvas.fig.add_axes([.325, .1, .2, .8])
  1351. canvas.ax3 = canvas.fig.add_axes([.55, .1, .2, .8])
  1352. canvas.ax4 = canvas.fig.add_axes([.815, .1, .05, .8]) #cb
  1353. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1354. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1355. canvas.ax3.tick_params(axis='both', which='major', labelsize=8)
  1356. canvas.ax4.tick_params(axis='both', which='major', labelsize=8)
  1357. canvas.ax1.set_ylabel("pulse index", fontsize=8)
  1358. canvas.ax1.set_xlabel(r"$\omega$ bin", fontsize=8)
  1359. canvas.ax2.set_xlabel(r"$\omega$ bin", fontsize=8)
  1360. canvas.ax3.set_xlabel(r"$\omega$ bin", fontsize=8)
  1361. canvas.ax2.yaxis.set_ticklabels([])
  1362. canvas.ax3.yaxis.set_ticklabels([])
  1363. #canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1364. # # Look at pulses
  1365. # for pulse in self.DATADICT["PULSES"]:
  1366. # for istack in self.DATADICT["stacks"]:
  1367. # for ipm in range(0,3):
  1368. # canvas.ax1.plot( self.DATADICT[pulse]["CURRENT"][ipm][istack] , label="istack "+str(istack) + " ipm=" + str(ipm) + pulse )
  1369. # canvas.draw()
  1370. # Create Container for stacks
  1371. # sandbox determine pulse sequence again
  1372. for pulse in self.DATADICT["PULSES"]:
  1373. for ichan in self.DATADICT[pulse]["chan"]:
  1374. #for ipm in range(10,11):
  1375. CONTAINER = {}
  1376. CONTAINER["Cycle 1"] = [] # These are actually subtracted cycles... v+ - v
  1377. CONTAINER["Cycle 2"] = []
  1378. for istack in self.DATADICT["stacks"]:
  1379. #canvas.ax1.clear()
  1380. ipm = 8
  1381. #for ipm in range(self.DATADICT["nPulseMoments"]):
  1382. #canvas.ax1.matshow( np.real(self.DATADICT[pulse][ichan]["FFT"][istack]), aspect='auto' )
  1383. #canvas.draw()
  1384. if not istack%4%4:
  1385. # phase cycle 4, aligned with 1 after sub
  1386. CONTAINER["Cycle 1"].append(-self.DATADICT[pulse][ichan]["FFT"][istack])
  1387. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], -self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1388. elif not istack%4%3:
  1389. # phase cycle 3, aligned with 2 after sub
  1390. CONTAINER["Cycle 2"].append(-self.DATADICT[pulse][ichan]["FFT"][istack])
  1391. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], -self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1392. elif not istack%4%2:
  1393. # phase cycle 2
  1394. CONTAINER["Cycle 2"].append( self.DATADICT[pulse][ichan]["FFT"][istack])
  1395. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1396. else:
  1397. # phase cycle 1
  1398. CONTAINER["Cycle 1"].append( self.DATADICT[pulse][ichan]["FFT"][istack])
  1399. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1400. #canvas.ax1.matshow(np.array(np.average(self.DATADICT[pulse][ichan]["FFT"]), axis=2), aspect='auto' )
  1401. #canvas.ax1.plot( self.DATADICT[pulse]["PULSE_TIMES"], self.DATADICT[pulse]["CURRENT"][ipm][istack] , color='black', label="istack "+str(istack) )
  1402. #canvas.ax1.plot( self.DATADICT[pulse]["CURRENT"][ipm][istack] , label="istack "+str(istack) + " iFID" + str(iFID) )
  1403. #canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], self.DATADICT[pulse][ichan][ipm][istack], label="istack "+str(istack)+ " " + pulse )
  1404. #canvas.ax1.legend(prop={'size':6})
  1405. #canvas.draw()
  1406. # Boostrap
  1407. # stack.
  1408. #scipy.random.shuffle(x)
  1409. # Stack and calculate the pooled variance (http://en.wikipedia.org/wiki/Pooled_variance)
  1410. """ All this phase cycling wreaks havoc on a normal calculation of std. and variance. Instead, we resort to calculating
  1411. a pooled variance. In this assumption is that the precision of the measurment is constant. This is a poor choice for
  1412. any type of moving sensor.
  1413. """
  1414. # if a window filter has been applied
  1415. #self.WINDOW
  1416. #self.IWindowStart
  1417. #self.iWindowEnd
  1418. #self.FFTtimes
  1419. CONTAINER = .5*(np.array(CONTAINER["Cycle 2"]) - np.array(CONTAINER["Cycle 1"]))
  1420. print ("container shape", np.shape( CONTAINER), self.iWindowStart+1, self.iWindowEnd-1)
  1421. dmin = np.min(np.abs(np.average(np.array(CONTAINER)[:,:,self.iWindowStart+1:self.iWindowEnd-1], axis=0)))
  1422. dmax = np.max(np.abs(np.average(np.array(CONTAINER)[:,:,self.iWindowStart+1:self.iWindowEnd-1], axis=0)))
  1423. 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)
  1424. #mn = canvas.ax1.matshow(20.*np.log10(XA[:,istart:iend+1]), aspect='auto', vmax=-40, vmin=-120) #, norm=LogNorm())
  1425. 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)
  1426. 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)
  1427. #canvas.ax1.legend(prop={'size':6})
  1428. cb1 = mpl.colorbar.Colorbar(canvas.ax4, mn)
  1429. cb1.ax.tick_params(labelsize=8)
  1430. cb1.set_label("power [dB]", fontsize=8)
  1431. canvas.ax1.xaxis.set_major_locator(MaxNLocator(4))
  1432. canvas.ax2.xaxis.set_major_locator(MaxNLocator(4))
  1433. canvas.ax3.xaxis.set_major_locator(MaxNLocator(4))
  1434. canvas.draw()
  1435. self.doneTrigger.emit()
  1436. def effectivePulseMoment(self, cv, canvas):
  1437. canvas.reAxH(2)
  1438. nstack = len(self.DATADICT["stacks"])
  1439. #canvas.ax1.set_yscale('log')
  1440. for pulse in self.DATADICT["PULSES"]:
  1441. self.DATADICT[pulse]["qeff"] = {}
  1442. self.DATADICT[pulse]["q_nu"] = {}
  1443. for ipm in range(self.DATADICT["nPulseMoments"]):
  1444. self.DATADICT[pulse]["qeff"][ipm] = {}
  1445. self.DATADICT[pulse]["q_nu"][ipm] = {}
  1446. #canvas.ax1.clear()
  1447. #scolours = np.array( ( np.linspace(0.8,0.4,len(self.DATADICT["stacks"])), \
  1448. # np.linspace(0.0,0.6,len(self.DATADICT["stacks"])), \
  1449. # np.linspace(0.6,0.0,len(self.DATADICT["stacks"])) )
  1450. # ).T
  1451. #scolours = plt.cm.Spectral(np.linspace(0,1,len(self.DATADICT["stacks"])))
  1452. #scolours = plt.cm.Blues(np.linspace(0,1,1.5*len(self.DATADICT["stacks"])))
  1453. scolours = cmocean.cm.ice(np.linspace(0,1,int(1.5*len(self.DATADICT["stacks"]))))
  1454. iistack = 0
  1455. for istack in self.DATADICT["stacks"]:
  1456. #self.DATADICT[pulse]["PULSE_TIMES"]
  1457. x = self.DATADICT[pulse]["CURRENT"][ipm][istack]
  1458. X = np.fft.rfft(x)
  1459. v = np.fft.fftfreq(len(x), self.dt)
  1460. v = v[0:len(X)]
  1461. v[-1] = np.abs(v[-1])
  1462. # calculate effective current/moment
  1463. I0 = np.abs(X)/len(X)
  1464. qeff = I0 * (self.DATADICT[pulse]["PULSE_TIMES"][-1]-self.DATADICT[pulse]["PULSE_TIMES"][0])
  1465. # frequency plot
  1466. #canvas.ax1.set_title(r"pulse moment index " +str(ipm), fontsize=10)
  1467. #canvas.ax1.set_xlabel(r"$\nu$ [Hz]", fontsize=8)
  1468. #canvas.ax1.set_ylabel(r"$q_{eff}$ [A$\cdot$sec]", fontsize=8)
  1469. #canvas.ax1.plot(v, qeff, color=scolours[iistack] ) # eff current
  1470. # time plot
  1471. canvas.ax1.plot(1e2*(self.DATADICT[pulse]["PULSE_TIMES"]-self.DATADICT[pulse]["PULSE_TIMES"][0]), x, color=scolours[iistack])
  1472. self.DATADICT[pulse]["qeff"][ipm][istack] = qeff
  1473. self.DATADICT[pulse]["q_nu"][ipm][istack] = v
  1474. iistack += 1
  1475. #canvas.draw()
  1476. percent = int(1e2* (float)((istack)+ipm*self.DATADICT["nPulseMoments"]) /
  1477. (float)(len(self.DATADICT["PULSES"])*self.DATADICT["nPulseMoments"]*nstack))
  1478. self.progressTrigger.emit(percent)
  1479. canvas.ax1.set_xlabel("time (ms)", fontsize=10)
  1480. canvas.ax1.set_ylabel("current (A)", fontsize=10)
  1481. #canvas.draw()
  1482. self.plotQeffNu(cv, canvas.ax2)
  1483. deSpine(canvas.ax1)
  1484. deSpine(canvas.ax2)
  1485. canvas.fig.tight_layout()
  1486. canvas.draw()
  1487. self.doneTrigger.emit()
  1488. def plotQeffNu(self, cv, ax):
  1489. ####################################
  1490. # TODO label fid1 and fid2, and make a legend, and colour by pulse
  1491. nstack = len(self.DATADICT["stacks"])
  1492. iFID = 0
  1493. for pulse in self.DATADICT["PULSES"]:
  1494. self.DATADICT[pulse]["Q"] = np.zeros( (self.DATADICT["nPulseMoments"], len(self.DATADICT["stacks"])) )
  1495. ilabel = True
  1496. for ipm in range(self.DATADICT["nPulseMoments"]):
  1497. #scolours = np.array([0.,0.,1.])
  1498. scolours = cmocean.cm.ice(np.linspace(0,1,int(1.5*len(self.DATADICT["stacks"]))))
  1499. #scolours = plt.cm.Spectral(np.linspace(0,1,len(self.DATADICT["stacks"])))
  1500. #scolours = plt.cm.Spectral(np.linspace(0,1,len(self.DATADICT["stacks"])))
  1501. istack = 0
  1502. for stack in self.DATADICT["stacks"]:
  1503. # find index
  1504. icv = int(round(cv / self.DATADICT[pulse]["q_nu"][ipm][stack][1]))
  1505. self.DATADICT[pulse]["Q"][ipm,istack] = self.DATADICT[pulse]["qeff"][ipm][stack][icv]
  1506. if ilabel:
  1507. ax.scatter(ipm, self.DATADICT[pulse]["qeff"][ipm][stack][icv], facecolors='none', edgecolors=scolours[istack], label=(str(pulse)))
  1508. ilabel = False
  1509. else:
  1510. ax.scatter(ipm, self.DATADICT[pulse]["qeff"][ipm][stack][icv], facecolors='none', edgecolors=scolours[istack])
  1511. #scolours += np.array((0,1./(nstack+1),-1/(nstack+1.)))
  1512. percent = int(1e2* (float)((istack)+ipm*self.DATADICT["nPulseMoments"]) /
  1513. (float)(len(self.DATADICT["PULSES"])*self.DATADICT["nPulseMoments"]*nstack))
  1514. self.progressTrigger.emit(percent)
  1515. istack += 1
  1516. iFID += 1
  1517. ax.set_xlabel(r"pulse moment index", fontsize=10)
  1518. ax.set_ylabel(r"$q_{eff}$ (A$\cdot$sec)", fontsize=10)
  1519. ax.set_yscale('log')
  1520. ax.set_xlim(0, ax.get_xlim()[1])
  1521. ax.legend(loc='upper right', scatterpoints = 1, prop={'size':10})
  1522. def enableDSP(self):
  1523. self.enableDSPTrigger.emit()
  1524. def adaptiveFilter(self, M, flambda, truncate, mu, PCA, plot, canvas):
  1525. #plot = False
  1526. canvas.reAx2(shx=False, shy=False)
  1527. # ax1 is top plot of filter taps
  1528. # ax2 is bottom plot of conditioned signal
  1529. if truncate:
  1530. itrunc =(int) ( round( 1e-3*truncate*self.samp ) )
  1531. print( "adaptive filter size", 1e3*self.dt*M, " [ms]" )
  1532. Filt = adapt.AdaptiveFilter(flambda)
  1533. H = {}
  1534. for pulse in self.DATADICT["PULSES"]:
  1535. H[pulse] = {}
  1536. for ichan in self.DATADICT[pulse]["chan"]:
  1537. H[pulse][ichan] = np.zeros(M*len( self.DATADICT[pulse]["rchan"] ))
  1538. iFID = 0
  1539. # original ordering...
  1540. #for pulse in self.DATADICT["PULSES"]:
  1541. # for ipm in range(self.DATADICT["nPulseMoments"]):
  1542. # for istack in self.DATADICT["stacks"]:
  1543. # This order makes more sense, same as data collection, verify
  1544. for istack in self.DATADICT["stacks"]:
  1545. for ipm in range(self.DATADICT["nPulseMoments"]):
  1546. for pulse in self.DATADICT["PULSES"]:
  1547. if plot:
  1548. canvas.softClear()
  1549. mmax = 0
  1550. for ichan in self.DATADICT[pulse]["chan"]:
  1551. canvas.ax2.plot( self.DATADICT[pulse]["TIMES"], 1e9* self.DATADICT[pulse][ichan][ipm][istack], alpha=.5)
  1552. mmax = max(mmax, np.max(1e9*self.DATADICT[pulse][ichan][ipm][istack]))
  1553. canvas.ax2.set_ylim(-mmax, mmax)
  1554. canvas.ax2.set_prop_cycle(None)
  1555. for ichan in self.DATADICT[pulse]["chan"]:
  1556. #H = np.zeros(M)
  1557. RX = []
  1558. for irchan in self.DATADICT[pulse]["rchan"]:
  1559. RX.append(self.DATADICT[pulse][irchan][ipm][istack][::-1])
  1560. # Reset each time?
  1561. #H[pulse][ichan] *= 0
  1562. #if all(H[pulse][ichan]) == 0:
  1563. if False:
  1564. ####################################################################################
  1565. # Padasip adaptive filter implimentations, do not allow for variable filter length
  1566. ####################################################################################
  1567. # identification #
  1568. #f = pa.filters.FilterRLS(n=len(self.DATADICT[pulse]["rchan"]), mu=0.99, w="zeros") #
  1569. #f = pa.filters.FilterGNGD(n=len(self.DATADICT[pulse]["rchan"]), mu=0.1) # # Nope
  1570. #f = pa.filters.FilterLMS(n=len(self.DATADICT[pulse]["rchan"]), mu=0.1) # # NOPE
  1571. #f = pa.filters.AdaptiveFilter(model="NLMS", n=len(self.DATADICT[pulse]["rchan"]), mu=0.1, w="random") # NOPE
  1572. #f = pa.filters.AdaptiveFilter(model="GNGD", n=len(self.DATADICT[pulse]["rchan"]), mu=0.1) # horrendous
  1573. #f = pa.filters.FilterNLMF(n=len(self.DATADICT[pulse]["rchan"]), mu=0.005, w="random") # BAD
  1574. #f = pa.filters.FilterSSLMS(n=len(self.DATADICT[pulse]["rchan"]), mu=0.01, w="zeros") # pretty good
  1575. f = pa.filters.FilterNSSLMS(n=len(self.DATADICT[pulse]["rchan"]), mu=0.1, w="zeros") # pretty good
  1576. y, e, H[pulse][ichan] = f.run(self.DATADICT[pulse][ichan][ipm][istack][::-1], np.array(RX).T) #
  1577. ####################################################################################
  1578. e = self.DATADICT[pulse][ichan][ipm][istack][::-1] - y
  1579. elif True:
  1580. # check for change in filter coefficients and rerun if things are changing too rapidly,
  1581. # this is especially true for the first run
  1582. hm1 = np.copy(H[pulse][ichan])
  1583. [e, H[pulse][ichan]] = Filt.adapt_filt_Ref( self.DATADICT[pulse][ichan][ipm][istack][::-1],\
  1584. RX,\
  1585. M, mu, PCA, flambda, H[pulse][ichan])
  1586. iloop = 0
  1587. #while False:
  1588. while (np.linalg.norm( H[pulse][ichan] - hm1) > .05): # threshold for recall
  1589. hm1 = np.copy(H[pulse][ichan])
  1590. [e, H[pulse][ichan]] = Filt.adapt_filt_Ref( self.DATADICT[pulse][ichan][ipm][istack][::-1],\
  1591. RX,\
  1592. M, mu, PCA, flambda, H[pulse][ichan])
  1593. iloop += 1
  1594. #print("Recalled ", iloop, "times with norm=", np.linalg.norm(hm1-H[pulse][ichan]))
  1595. else:
  1596. [e,H[pulse][ichan]] = Filt.adapt_filt_Ref( self.DATADICT[pulse][ichan][ipm][istack][::-1],\
  1597. RX,\
  1598. M, mu, PCA, flambda, H[pulse][ichan])
  1599. # replace
  1600. if truncate:
  1601. if plot:
  1602. canvas.ax2.plot( self.DATADICT[pulse]["TIMES"][0:itrunc], 1e9* e[::-1][0:itrunc],\
  1603. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1604. self.DATADICT[pulse][ichan][ipm][istack] = e[::-1][0:itrunc]
  1605. else:
  1606. if plot:
  1607. canvas.ax2.plot( self.DATADICT[pulse]["TIMES"], 1e9* e[::-1],\
  1608. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1609. self.DATADICT[pulse][ichan][ipm][istack] = e[::-1]
  1610. if plot:
  1611. #canvas.ax1.plot( H[pulse][ichan].reshape(-1, len(RX)) ) # , label="taps")
  1612. canvas.ax1.plot( H[pulse][ichan][::-1].reshape(M, len(RX), order='F' ) ) #.reshape(-1, len(RX)) ) # , label="taps")
  1613. canvas.ax2.legend(prop={'size':10}, loc='upper right')
  1614. #canvas.ax2.legend(prop={'size':6}, loc='upper right')
  1615. mh = np.max(np.abs( H[pulse][ichan] ))
  1616. canvas.ax1.set_ylim( -mh, mh )
  1617. canvas.ax2.set_xlabel(r"time (s)", fontsize=10)
  1618. canvas.ax2.set_ylabel(r"signal (nV)", fontsize=10)
  1619. canvas.ax1.set_xlabel(r"filter tap index", fontsize=10)
  1620. canvas.ax1.set_ylabel(r"tap amplitude", fontsize=10)
  1621. if plot:
  1622. canvas.fig.tight_layout()
  1623. deSpine(canvas.ax1)
  1624. deSpine(canvas.ax2)
  1625. canvas.draw()
  1626. # truncate the reference channels too, in case you still need them for something.
  1627. # Otherwise they are no longer aligned with the data
  1628. for rchan in self.DATADICT[pulse]["rchan"]:
  1629. if truncate:
  1630. self.DATADICT[pulse][rchan][ipm][istack] = self.DATADICT[pulse][rchan][ipm][istack][0:itrunc]
  1631. #percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1632. percent = (int)(1e2*((float)(istack*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments*(len(self.DATADICT["stacks"])+1) )))
  1633. self.progressTrigger.emit(percent)
  1634. # # why is this loop here, istack is not part of rest?
  1635. # for istack in self.DATADICT["stacks"]:
  1636. # if truncate:
  1637. # self.DATADICT[pulse]["TIMES"] = self.DATADICT[pulse]["TIMES"][0:itrunc]
  1638. # percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1639. # self.progressTrigger.emit(percent)
  1640. # iFID += 1
  1641. if truncate:
  1642. self.DATADICT[pulse]["TIMES"] = self.DATADICT[pulse]["TIMES"][0:itrunc]
  1643. self.doneTrigger.emit()
  1644. self.updateProcTrigger.emit()
  1645. #self.plotFT(canvas)
  1646. def plotFT(self, canvas, istart=0, iend=0):
  1647. try:
  1648. canvas.fig.clear()
  1649. except:
  1650. pass
  1651. canvas.ax1 = canvas.fig.add_axes([.1, .1, .65, .8])
  1652. canvas.ax1c = canvas.fig.add_axes([.8, .1, .05, .8])
  1653. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1654. for pulse in self.DATADICT["PULSES"]:
  1655. for istack in self.DATADICT["stacks"]:
  1656. for ichan in self.DATADICT[pulse]["chan"]:
  1657. # FFT of stack
  1658. XA = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])/2+1))
  1659. nu = np.fft.fftfreq(self.DATADICT[pulse][ichan][0][istack].size, d=self.dt)
  1660. nu[-1] *= -1
  1661. df = nu[1]
  1662. of = 0
  1663. if istart:
  1664. of = nu[istart]
  1665. def freqlabel(x, pos):
  1666. return '%1.0f' %(of + x*df)
  1667. formatter = FuncFormatter(freqlabel)
  1668. canvas.ax1.clear()
  1669. for ipm in range(self.DATADICT["nPulseMoments"]):
  1670. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1671. XA[ipm,:] = np.abs(X)
  1672. if istart:
  1673. mn = canvas.ax1.matshow(20.*np.log10(XA[:,istart:iend+1]), aspect='auto', vmax=-40, vmin=-120) #, norm=LogNorm())
  1674. else:
  1675. mn = canvas.ax1.matshow(20.*np.log10(XA), aspect='auto', vmax=-40, vmin=-120) #, norm=LogNorm())
  1676. smin = np.min(20.*np.log10(XA))
  1677. smax = np.max(20.*np.log10(XA))
  1678. canvas.ax1.xaxis.set_major_formatter(formatter)
  1679. cb1 = mpl.colorbar.Colorbar(canvas.ax1c, mn)
  1680. cb1.ax.tick_params(labelsize=8)
  1681. cb1.set_label("signal [dB]", fontsize=8)
  1682. canvas.ax1.set_xlabel(r"$\nu$ [Hz]", fontsize=10)
  1683. canvas.ax1.set_ylabel(r"$q_{index}$", fontsize=10)
  1684. canvas.draw()
  1685. def plotFT(self, canvas, istart=0, iend=0):
  1686. try:
  1687. canvas.fig.clear()
  1688. except:
  1689. pass
  1690. canvas.ax1 = canvas.fig.add_axes([.1, .1, .65, .8])
  1691. canvas.ax1c = canvas.fig.add_axes([.8, .1, .05, .8])
  1692. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1693. for pulse in self.DATADICT["PULSES"]:
  1694. for istack in self.DATADICT["stacks"]:
  1695. for ichan in self.DATADICT[pulse]["chan"]:
  1696. # FFT of stack
  1697. XA = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])//2+1))
  1698. nu = np.fft.fftfreq(self.DATADICT[pulse][ichan][0][istack].size, d=self.dt)
  1699. nu[-1] *= -1
  1700. df = nu[1]
  1701. of = 0
  1702. if istart:
  1703. of = nu[istart]
  1704. def freqlabel(x, pos):
  1705. return '%1.0f' %(of + x*df)
  1706. formatter = FuncFormatter(freqlabel)
  1707. canvas.ax1.clear()
  1708. for ipm in range(self.DATADICT["nPulseMoments"]):
  1709. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1710. XA[ipm,:] = np.abs(X)
  1711. if istart:
  1712. mn = canvas.ax1.matshow(20.*np.log10(XA[:,istart:iend+1]), aspect='auto', vmax=-40, vmin=-120, cmap='viridis') #, norm=LogNorm())
  1713. else:
  1714. mn = canvas.ax1.matshow(20.*np.log10(XA), aspect='auto', vmax=-40, vmin=-120, cmap='viridis') #, norm=LogNorm())
  1715. canvas.ax1.xaxis.set_major_formatter(formatter)
  1716. cb1 = mpl.colorbar.Colorbar(canvas.ax1c, mn)
  1717. cb1.ax.tick_params(labelsize=8)
  1718. cb1.set_label("signal [dB]", fontsize=8)
  1719. canvas.ax1.set_xlabel(r"$\nu$ [Hz]", fontsize=10)
  1720. canvas.ax1.set_ylabel(r"$q_{index}$", fontsize=10)
  1721. canvas.draw()
  1722. def dataCubeFFT(self):
  1723. """
  1724. Performs FFT on entire cube of DATA, and REFERENCE channels, but not pulse currents,
  1725. Results are saved to a new field in the data structure
  1726. The GMR varies phase as a function of pulse moment index, so that the first pusle moment is zero phase,
  1727. the second is pi/2 the third is zero. This method corrects for this, so that all pulse moments are in phase.
  1728. Technically we may not want to do this, if there is some system response that this cycles away, and we lose track of
  1729. 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
  1730. rest of the phase cycles. Holy phase cycling batman.
  1731. """
  1732. for pulse in self.DATADICT["PULSES"]:
  1733. for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  1734. # FFT of stack
  1735. self.DATADICT[pulse][ichan]["FFT"] = {}
  1736. self.DATADICT[pulse][ichan]["FFT"]["nu"] = np.fft.fftfreq(self.DATADICT[pulse][ichan][0][self.DATADICT["stacks"][0]].size, d=self.dt)
  1737. self.DATADICT[pulse][ichan]["FFT"]["nu"][-1] *= -1
  1738. for istack in self.DATADICT["stacks"]:
  1739. self.DATADICT[pulse][ichan]["FFT"][istack] = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])//2+1), dtype=complex)
  1740. for ipm in range(self.DATADICT["nPulseMoments"]):
  1741. # Mod works for FID pulse sequences, TODO generalize this for 4 phase T1, etc..
  1742. mod = (-1)**(ipm%2) * (-1)**(istack%2)
  1743. self.DATADICT[pulse][ichan]["FFT"][istack][ipm,:] = np.fft.rfft( self.DATADICT[pulse][ichan][ipm][istack] )
  1744. #if ipm%2:
  1745. # odd, phase cycled from previous
  1746. # self.DATADICT[pulse][ichan]["FFT"][istack][ipm,:] = np.fft.rfft(-self.DATADICT[pulse][ichan][ipm][istack])
  1747. #else:
  1748. # even, we define as zero phase, first pulse moment has this
  1749. # self.DATADICT[pulse][ichan]["FFT"][istack][ipm,:] = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1750. def adaptiveFilterFD(self, ftype, band, centre, canvas):
  1751. try:
  1752. canvas.fig.clear()
  1753. except:
  1754. pass
  1755. canvas.ax1 = canvas.fig.add_axes([.1, .5, .7, .4])
  1756. canvas.ax1c = canvas.fig.add_axes([.85, .5, .05, .4])
  1757. canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1758. #canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1759. canvas.ax2 = canvas.fig.add_axes([.1, .05, .7, .4])
  1760. canvas.ax2c = canvas.fig.add_axes([.85, .05, .05, .4])
  1761. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  1762. #canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1763. self.dataCubeFFT()
  1764. Filt = adapt.AdaptiveFilter(0.)
  1765. for pulse in self.DATADICT["PULSES"]:
  1766. # Compute window function and dimensions
  1767. [WINDOW, nd, wstart, wend, dead, idead] = self.computeWindow(pulse, band, centre, ftype)
  1768. for istack in self.DATADICT["stacks"]:
  1769. for ichan in self.DATADICT[pulse]["chan"]:
  1770. # FFT of stack
  1771. nd = len(self.DATADICT[pulse][ichan][0][istack])
  1772. XX = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])//2+1), dtype=complex)
  1773. nu = np.fft.fftfreq(self.DATADICT[pulse][ichan][0][istack].size, d=self.dt)
  1774. nu[-1] *= -1
  1775. #nu = self.DATADICT[pulse][ichan]["FFT"]["nu"]
  1776. def freqlabel(x, pos):
  1777. return '%1.0f' %((wstart)*nu[1] + x*nu[1])
  1778. formatter = FuncFormatter(freqlabel)
  1779. canvas.ax1.clear()
  1780. for ipm in range(self.DATADICT["nPulseMoments"]):
  1781. X = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1782. XX[ipm,:] = X
  1783. XX = XX*WINDOW
  1784. XX = XX[:,wstart:wend]
  1785. smin = np.min(20.*np.log10(np.abs(XX)))
  1786. smax = np.max(20.*np.log10(np.abs(XX)))
  1787. #if smin != smin:
  1788. smax = -40
  1789. smin = -120
  1790. mn = canvas.ax1.matshow(20.*np.log10(np.abs(XX)), aspect='auto', vmin=smin, vmax=smax) #, norm=LogNorm())
  1791. canvas.ax1.xaxis.set_major_formatter(formatter)
  1792. cb1 = mpl.colorbar.Colorbar(canvas.ax1c, mn)
  1793. RX = []
  1794. for ichan in self.DATADICT[pulse]["rchan"]:
  1795. R = np.zeros((self.DATADICT["nPulseMoments"] , len(self.DATADICT[pulse][ichan][0][istack])//2+1), dtype=complex)
  1796. for ipm in range(self.DATADICT["nPulseMoments"]):
  1797. R[ipm,:] = np.fft.rfft(self.DATADICT[pulse][ichan][ipm][istack])
  1798. RX.append(R[:,wstart:wend])
  1799. XC = Filt.transferFunctionFFT(XX, RX)
  1800. # TODO inverse FFT, but we need to map back to origional matrix size
  1801. #for ichan in self.DATADICT[pulse]["chan"]:
  1802. # for ipm in range(self.DATADICT["nPulseMoments"]):
  1803. # self.DATADICT[pulse][ichan][ipm][istack] = np.fft.irfft(XC[] , nd)
  1804. mc = canvas.ax2.matshow(20.*np.log10(np.abs(XC)), aspect='auto', vmin=smin, vmax=smax) #, norm=LogNorm())
  1805. cb2 = mpl.colorbar.Colorbar(canvas.ax2c, mc)
  1806. cmin = np.min(20.*np.log10(np.abs(XC)))
  1807. cmax = np.max(20.*np.log10(np.abs(XC)))
  1808. canvas.ax2.xaxis.set_major_formatter(formatter)
  1809. #canvas.ax2.colorbar(mn)
  1810. canvas.draw()
  1811. ##############################3
  1812. # TODO inverse FFT to get the damn data back!!!
  1813. # self.progressTrigger.emit(percent)
  1814. # #label = "iFID="+str(iFID) + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1815. self.doneTrigger.emit()
  1816. def findSpikes(self, x, width, threshold, rollOn):
  1817. import scipy.ndimage as im
  1818. spikes = np.zeros( len(x) )
  1819. med = im.median_filter(x, width,mode='nearest')
  1820. std = np.std(x)
  1821. spikes = (np.abs(x-med) > threshold * std)
  1822. return np.array(np.where(spikes[rollOn::])) + rollOn
  1823. # def despike(self, width, threshold, itype, rollOn, win, canvas):
  1824. # from scipy import interpolate
  1825. # """ This was a stab at a despike filter. Better results were achieved using the SmartStack approach
  1826. # """
  1827. # try:
  1828. # canvas.fig.clear()
  1829. # except:
  1830. # pass
  1831. #
  1832. # canvas.ax1 = canvas.fig.add_axes([.125,.1,.725,.8])
  1833. # canvas.ax1.tick_params(axis='both', which='major', labelsize=8)
  1834. # canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  1835. # iFID = 0
  1836. # for pulse in self.DATADICT["PULSES"]:
  1837. # for ipm in range(self.DATADICT["nPulseMoments"]):
  1838. # for istack in self.DATADICT["stacks"]:
  1839. # canvas.ax1.clear()
  1840. # for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  1841. # x = self.findSpikes(self.DATADICT[pulse][ichan][ipm][istack], width, threshold, rollOn)
  1842. # canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], self.DATADICT[pulse][ichan][ipm][istack],
  1843. # label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1844. # canvas.ax1.plot( self.DATADICT[pulse]["TIMES"][x], self.DATADICT[pulse][ichan][ipm][istack][x], '.', color='red' , markersize=6 )
  1845. #
  1846. # FIXED = np.zeros(len(x[0]))
  1847. # ii = 0
  1848. # for spike in np.array(x[0]).tolist():
  1849. # f = interpolate.interp1d(np.delete(self.DATADICT[pulse]["TIMES"][spike-win/2:spike+win/2], x[0]-(spike-win/2)), \
  1850. # np.delete(self.DATADICT[pulse][ichan][ipm][istack][spike-win/2:spike+win/2], x[0]-(spike-win/2)), itype)
  1851. # FIXED[ii] = f(self.DATADICT[pulse]["TIMES"][spike])
  1852. # ii += 1
  1853. # canvas.ax1.plot( self.DATADICT[pulse]["TIMES"][x[0]] , FIXED, '.', color='black' , markersize=4 )
  1854. # self.DATADICT[pulse][ichan][ipm][istack][x[0]] = FIXED
  1855. #
  1856. # canvas.ax1.legend(prop={'size':6})
  1857. # canvas.draw()
  1858. # percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1859. # self.progressTrigger.emit(percent)
  1860. # iFID += 1
  1861. # self.doneTrigger.emit()
  1862. def designFilter(self, cf, PB, SB, gpass, gstop, ftype, canvas):
  1863. ''' cf is central frequency
  1864. pb is pass band
  1865. sb is stop band
  1866. '''
  1867. TS = (cf) / (.5/self.dt)
  1868. PB = PB / (.5/self.dt) # 1/2 width pass band Muddy Creek
  1869. SB = SB / (.5/self.dt) # 1/2 width stop band Muddy Creek
  1870. # if butterworth
  1871. #[bord, wn] = signal.buttord([TS-PB,TS+PB], [TS-SB,TS+SB], 1e-1, 5.)
  1872. if ftype=="Butterworth":
  1873. [bord, wn] = signal.buttord([TS-PB,TS+PB], [TS-SB,TS+SB], gpass, gstop)
  1874. [self.filt_b, self.filt_a] = signal.butter(bord, wn, btype='bandpass', output='ba')
  1875. [self.filt_z, self.filt_p, self.filt_k] = signal.butter(bord, wn, btype='band', output='zpk')
  1876. elif ftype == "Chebychev Type II":
  1877. [bord, wn] = signal.cheb2ord([TS-PB,TS+PB], [TS-SB,TS+SB], gpass, gstop)
  1878. [self.filt_b, self.filt_a] = signal.cheby2(bord, gstop, wn, btype='bandpass', output='ba')
  1879. [self.filt_z, self.filt_p, self.filt_k] = signal.cheby2(bord, gstop, wn, btype='band', output='zpk')
  1880. elif ftype == "Elliptic":
  1881. [bord, wn] = signal.ellipord([TS-PB,TS+PB], [TS-SB,TS+SB], gpass, gstop)
  1882. [self.filt_b, self.filt_a] = signal.ellip(bord, gpass, gstop, wn, btype='bandpass', output='ba')
  1883. [self.filt_z, self.filt_p, self.filt_k] = signal.ellip(bord, gpass, gstop, wn, btype='band', output='zpk')
  1884. # if cheby2
  1885. impulse = self.mfreqz2(self.filt_b, self.filt_a, canvas)
  1886. self.fe = -5
  1887. for it in range(len(impulse[0])):
  1888. if abs(impulse[1][0][it][0]) >= .1 * gpass:# gpass:
  1889. self.fe = impulse[0][it]
  1890. canvas.draw()
  1891. return [bord, self.fe]
  1892. def downsample(self, truncate, dec, plot=False, canvas=None):
  1893. """ Downsamples and truncates the raw signal.
  1894. Args
  1895. truncate (float) : the length of the signal to truncate to
  1896. dec (int) : the decimation factor, 1 results in no downsampling
  1897. plot (bool) : perform plots
  1898. canvas : MPL axis for plotting
  1899. """
  1900. if plot:
  1901. fs = 10
  1902. canvas.reAx2()
  1903. canvas.ax1.set_ylabel(r"signal (nV)", fontsize=fs)
  1904. canvas.ax2.set_xlabel(r"time (s)", fontsize=fs)
  1905. canvas.ax2.set_ylabel(r"signal (nV)", fontsize=fs)
  1906. self.samp /= dec
  1907. self.dt = 1./self.samp
  1908. iFID = 0
  1909. for pulse in self.DATADICT["PULSES"]:
  1910. RSTIMES = self.DATADICT[pulse]["TIMES"][::dec]
  1911. if truncate:
  1912. itrunc = (int)( 1e-3*truncate*self.samp )
  1913. RSTIMES = RSTIMES[0:itrunc]
  1914. for ipm in range(self.DATADICT["nPulseMoments"]):
  1915. for istack in self.DATADICT["stacks"]:
  1916. if plot:
  1917. canvas.softClear()
  1918. for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  1919. # trim off indices that don't divide evenly
  1920. ndi = np.shape(self.DATADICT[pulse][ichan][ipm][istack])[0]%dec
  1921. if ndi:
  1922. #[self.DATADICT[pulse][ichan][ipm][istack], RSTIMES] = signal.resample(self.DATADICT[pulse][ichan][ipm][istack][0:-ndi],\
  1923. # len(self.DATADICT[pulse][ichan][ipm][istack][0:-ndi])//dec,\
  1924. # self.DATADICT[pulse]["TIMES"][0:-ndi], window='hamm')
  1925. self.DATADICT[pulse][ichan][ipm][istack] = signal.decimate(self.DATADICT[pulse][ichan][ipm][istack], dec, n=None, ftype='iir', zero_phase=True)
  1926. else:
  1927. #[self.DATADICT[pulse][ichan][ipm][istack], RSTIMES] = signal.resample(self.DATADICT[pulse][ichan][ipm][istack],\
  1928. # len(self.DATADICT[pulse][ichan][ipm][istack])//dec,\
  1929. # self.DATADICT[pulse]["TIMES"], window='hamm')
  1930. self.DATADICT[pulse][ichan][ipm][istack] = signal.decimate(self.DATADICT[pulse][ichan][ipm][istack], dec, n=None, ftype='iir', zero_phase=True)
  1931. if truncate:
  1932. self.DATADICT[pulse][ichan][ipm][istack] = self.DATADICT[pulse][ichan][ipm][istack][0:itrunc]
  1933. if plot:
  1934. for ichan in self.DATADICT[pulse]["chan"]:
  1935. canvas.ax2.plot( RSTIMES, 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  1936. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1937. for ichan in self.DATADICT[pulse]["rchan"]:
  1938. canvas.ax1.plot( RSTIMES, 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  1939. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " ichan=" + str(ichan))
  1940. canvas.ax1.legend(prop={'size':fs}, loc='upper right')
  1941. canvas.ax2.legend(prop={'size':fs}, loc='upper right')
  1942. deSpine( canvas.ax1 )
  1943. deSpine( canvas.ax2 )
  1944. plt.setp(canvas.ax1.get_xticklabels(), visible=False)
  1945. canvas.fig.tight_layout()
  1946. canvas.draw()
  1947. percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/( len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  1948. self.progressTrigger.emit(percent)
  1949. iFID += 1
  1950. self.DATADICT[pulse]["TIMES"] = RSTIMES
  1951. #####################################
  1952. # resample pulse data
  1953. for pulse in self.DATADICT["PULSES"]:
  1954. for ipm in range(self.DATADICT["nPulseMoments"]):
  1955. for istack in self.DATADICT["stacks"]:
  1956. ndi = np.shape(self.DATADICT[pulse]["CURRENT"][ipm][istack])[0]%dec
  1957. if ndi:
  1958. [self.DATADICT[pulse]["CURRENT"][ipm][istack], RSPTIMES] = signal.resample(self.DATADICT[pulse]["CURRENT"][ipm][istack][0:-ndi],\
  1959. len(self.DATADICT[pulse]["CURRENT"][ipm][istack][0:-ndi])//dec,\
  1960. self.DATADICT[pulse]["PULSE_TIMES"][0:-ndi], window='hamm')
  1961. else:
  1962. [self.DATADICT[pulse]["CURRENT"][ipm][istack], RSPTIMES] = signal.resample(self.DATADICT[pulse]["CURRENT"][ipm][istack],\
  1963. len(self.DATADICT[pulse]["CURRENT"][ipm][istack])//dec,\
  1964. self.DATADICT[pulse]["PULSE_TIMES"], window='hamm')
  1965. self.DATADICT[pulse]["PULSE_TIMES"] = RSPTIMES
  1966. self.doneTrigger.emit()
  1967. self.updateProcTrigger.emit()
  1968. def computeWindow(self, pulse, band, centre, ftype, canvas=None):
  1969. # Compute window
  1970. nd = len(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0]][0][self.DATADICT["stacks"][0]]) # num. data
  1971. fft1 = np.fft.rfft(self.DATADICT[pulse][self.DATADICT[pulse]["chan"][0]][0][self.DATADICT["stacks"][0]])
  1972. freqs = np.fft.fftfreq(nd, self.dt)
  1973. df = freqs[1] - freqs[0]
  1974. N = int((round)(band/df))
  1975. if ftype == "Hamming":
  1976. window = np.hamming(N)
  1977. elif ftype == "Hanning":
  1978. window = np.hanning(N)
  1979. elif ftype == "Rectangular":
  1980. window = np.ones(N)
  1981. elif ftype == "Flat top":
  1982. window = signal.flattop(N)
  1983. else:
  1984. print ("in windowFilter, window type undefined")
  1985. WINDOW = np.zeros(len(fft1))
  1986. ifreq = int(round(centre/df))
  1987. istart = ifreq-len(window)//2
  1988. iend = 0
  1989. if N%2:
  1990. WINDOW[ifreq-N//2:ifreq+N//2+1] = window
  1991. iend = ifreq+N//2+1
  1992. else:
  1993. WINDOW[ifreq-N//2:ifreq+N//2] = window
  1994. iend = ifreq+N//2
  1995. self.WINDOW = WINDOW
  1996. self.iWindowStart = istart
  1997. self.iWindowEnd = iend
  1998. self.FFTtimes = nd
  1999. fft1 = np.fft.irfft(WINDOW)
  2000. # calculate dead time
  2001. self.windead = 0.
  2002. for ift in np.arange(100,0,-1):
  2003. #print( ift, fft1[ift] )
  2004. if (abs(fft1[ift])/abs(fft1[0])) > 1e-2:
  2005. #print ("DEAD TIME", 1e3*self.DATADICT[pulse]["TIMES"][ift] - 1e3*self.DATADICT[pulse]["TIMES"][0] )
  2006. dead = 1e3*self.DATADICT[pulse]["TIMES"][ift] - 1e3*self.DATADICT[pulse]["TIMES"][0]
  2007. self.windead = self.DATADICT[pulse]["TIMES"][ift] - self.DATADICT[pulse]["TIMES"][0]
  2008. break
  2009. if canvas != None:
  2010. canvas.fig.clear()
  2011. canvas.ax1 = canvas.fig.add_axes([.1, .6, .75, .35])
  2012. canvas.ax2 = canvas.fig.add_axes([.1, .1, .75, .35])
  2013. canvas.ax1.plot(WINDOW)
  2014. canvas.ax2.plot( 1e3* self.DATADICT[pulse]["TIMES"][0:100] - 1e3*self.DATADICT[pulse]["TIMES"][0], fft1[0:100] )
  2015. canvas.ax2.set_xlabel("time (ms)")
  2016. canvas.ax2.set_title("IFFT")
  2017. canvas.draw()
  2018. return [WINDOW, nd, istart, iend, dead, ift]
  2019. def windowFilter(self, ftype, band, centre, trunc, canvas):
  2020. ###############################
  2021. # Window Filter (Ormsby filter http://www.xsgeo.com/course/filt.htm)
  2022. # apply window
  2023. iFID = 0
  2024. for pulse in self.DATADICT["PULSES"]:
  2025. [WINDOW, nd, istart, iend, dead, idead] = self.computeWindow(pulse, band, centre, ftype)
  2026. for istack in self.DATADICT["stacks"]:
  2027. for ipm in range(self.DATADICT["nPulseMoments"]):
  2028. for ichan in np.append(self.DATADICT[pulse]["chan"], self.DATADICT[pulse]["rchan"]):
  2029. fft = np.fft.rfft( self.DATADICT[pulse][ichan][ipm][istack] )
  2030. fft *= WINDOW
  2031. if trunc:
  2032. self.DATADICT[pulse][ichan][ipm][istack] = np.fft.irfft(fft, nd)[idead:-idead]
  2033. else:
  2034. self.DATADICT[pulse][ichan][ipm][istack] = np.fft.irfft(fft, nd)
  2035. percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/(len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  2036. self.progressTrigger.emit(percent)
  2037. iFID += 1
  2038. if trunc:
  2039. self.DATADICT[pulse]["TIMES"] = self.DATADICT[pulse]["TIMES"][idead:-idead]
  2040. [WINDOWxx, ndxx, istart, iend, deadxx, ideadxx] = self.computeWindow(pulse, band, centre, ftype)
  2041. self.plotFT(canvas, istart, iend)
  2042. self.doneTrigger.emit()
  2043. def bandpassFilter(self, canvas, blank, plot=True):
  2044. if plot:
  2045. canvas.reAx2()
  2046. canvas.ax1.set_ylabel(r"signal [nV]", fontsize=8)
  2047. canvas.ax2.set_xlabel(r"time [s]", fontsize=8)
  2048. canvas.ax2.set_ylabel(r"signal [nV]", fontsize=8)
  2049. ife = (int)( max(self.fe, self.windead) * self.samp )
  2050. # Data
  2051. iFID = 0
  2052. for pulse in self.DATADICT["PULSES"]:
  2053. self.DATADICT[pulse]["TIMES"] = self.DATADICT[pulse]["TIMES"][ife:-ife]
  2054. for ipm in range(self.DATADICT["nPulseMoments"]):
  2055. for istack in self.DATADICT["stacks"]:
  2056. if plot:
  2057. canvas.softClear()
  2058. mmax = 0
  2059. for ichan in self.DATADICT[pulse]["rchan"]:
  2060. canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack][ife:-ife], alpha=.5)
  2061. mmax = max( mmax, np.max(1e9*self.DATADICT[pulse][ichan][ipm][istack][ife:-ife]))
  2062. for ichan in self.DATADICT[pulse]["chan"]:
  2063. canvas.ax2.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack][ife:-ife], alpha=.5)
  2064. mmax = max( mmax, np.max(1e9*self.DATADICT[pulse][ichan][ipm][istack][ife:-ife]))
  2065. canvas.ax2.set_prop_cycle(None)
  2066. canvas.ax1.set_prop_cycle(None)
  2067. canvas.ax1.set_ylim(-mmax, mmax)
  2068. for ichan in self.DATADICT[pulse]["rchan"]:
  2069. # reflect signal back on itself to reduce gibbs effects on early times
  2070. #nr = len( self.DATADICT[pulse][ichan][ipm][istack] ) - 1 + ife
  2071. #refl = np.append( -1*self.DATADICT[pulse][ichan][ipm][istack][::-1][0:-1], self.DATADICT[pulse][ichan][ipm][istack] )
  2072. #reflfilt = signal.filtfilt( self.filt_b, self.filt_a, refl )
  2073. #self.DATADICT[pulse][ichan][ipm][istack] = reflfilt[nr:-ife]
  2074. # don't reflect
  2075. self.DATADICT[pulse][ichan][ipm][istack] = \
  2076. signal.filtfilt(self.filt_b, self.filt_a, self.DATADICT[pulse][ichan][ipm][istack])[ife:-ife]
  2077. # plot
  2078. if plot:
  2079. canvas.ax1.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  2080. label = pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " rchan=" + str(ichan))
  2081. for ichan in self.DATADICT[pulse]["chan"]:
  2082. # reflect signal back on itself to reduce gibbs effects on early times
  2083. #nr = len( self.DATADICT[pulse][ichan][ipm][istack] ) - 1 + ife
  2084. #refl = np.append( -1*self.DATADICT[pulse][ichan][ipm][istack][::-1][0:-1], self.DATADICT[pulse][ichan][ipm][istack] )
  2085. #reflfilt = signal.filtfilt( self.filt_b, self.filt_a, refl )
  2086. #self.DATADICT[pulse][ichan][ipm][istack] = reflfilt[nr:-ife]
  2087. # don't reflect
  2088. self.DATADICT[pulse][ichan][ipm][istack] = \
  2089. scipy.signal.filtfilt(self.filt_b, self.filt_a, self.DATADICT[pulse][ichan][ipm][istack])[ife:-ife]
  2090. # plot
  2091. if plot:
  2092. canvas.ax2.plot( self.DATADICT[pulse]["TIMES"], 1e9*self.DATADICT[pulse][ichan][ipm][istack], \
  2093. label = "data " + pulse + " ipm=" + str(ipm) + " istack=" + str(istack) + " chan=" + str(ichan))
  2094. if plot:
  2095. canvas.ax1.legend(prop={'size':6}, loc='upper right')
  2096. canvas.ax2.legend(prop={'size':6}, loc='upper right')
  2097. canvas.draw()
  2098. percent = (int)(1e2*((float)(iFID*self.DATADICT["nPulseMoments"]+(ipm))/(len(self.DATADICT["PULSES"])*self.nPulseMoments)))
  2099. self.progressTrigger.emit(percent)
  2100. iFID += 1
  2101. self.doneTrigger.emit()
  2102. self.updateProcTrigger.emit()
  2103. def loadGMRBinaryFID( self, rawfname, istack ):
  2104. """ Reads a single binary GMR file and fills into DATADICT
  2105. """
  2106. #################################################################################
  2107. # figure out key data indices
  2108. # Pulse
  2109. nps = (int)((self.prePulseDelay)*self.samp)
  2110. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  2111. # Data
  2112. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  2113. nd1 = (int)(1.*self.samp) # samples in first pulse
  2114. invGain = 1./self.RxGain
  2115. invCGain = self.CurrentGain
  2116. pulse = "Pulse 1"
  2117. chan = self.DATADICT[pulse]["chan"]
  2118. rchan = self.DATADICT[pulse]["rchan"]
  2119. rawFile = open( rawfname, 'rb')
  2120. for ipm in range(self.nPulseMoments):
  2121. buf1 = rawFile.read(4)
  2122. buf2 = rawFile.read(4)
  2123. N_chan = struct.unpack('>i', buf1 )[0]
  2124. N_samp = struct.unpack('>i', buf2 )[0]
  2125. T = N_samp * self.dt
  2126. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  2127. DATA = np.zeros([N_samp, N_chan+1])
  2128. for ichan in range(N_chan):
  2129. DATADUMP = rawFile.read(4*N_samp)
  2130. for irec in range(N_samp):
  2131. DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
  2132. # Save into Data Cube
  2133. for ichan in chan:
  2134. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain
  2135. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2136. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
  2137. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  2138. # reference channels?
  2139. for ichan in rchan:
  2140. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain
  2141. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2142. def loadGMRASCIIFID( self, rawfname, istack ):
  2143. """Based on the geoMRI instrument manufactured by VistaClara. Imports
  2144. a suite of raw .lvm files with the following format (on one line)
  2145. time(s) DC_Bus/100(V) Current+/75(A) Curr-/75(A) Voltage+/200(V) \
  2146. Ch1(V) Ch2(V) Ch3(V) Ch4(V)
  2147. Sampling rate is assumed at 50 kHz
  2148. """
  2149. import pandas as pd
  2150. #################################################################################
  2151. # figure out key data indices
  2152. # Pulse
  2153. nps = (int)((self.prePulseDelay)*self.samp)
  2154. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  2155. # Data
  2156. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  2157. nd1 = (int)(1.*self.samp) - nds # samples in first pulse
  2158. ndr = (int)(1.*self.samp) # samples in record
  2159. invGain = 1./self.RxGain
  2160. invCGain = self.CurrentGain
  2161. pulse = "Pulse 1"
  2162. chan = self.DATADICT[pulse]["chan"]
  2163. rchan = self.DATADICT[pulse]["rchan"]
  2164. T = 1.5 #N_samp * self.dt
  2165. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  2166. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2167. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  2168. # pandas is much faster than numpy for io
  2169. #DATA = np.loadtxt(rawfname)
  2170. DATA = pd.read_csv(rawfname, header=None, sep="\t").values
  2171. for ipm in range(self.nPulseMoments):
  2172. for ichan in np.append(chan,rchan):
  2173. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:, eval(ichan)+4][nds:(nds+nd1)] * invGain
  2174. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,2][nps:nps+npul] * invCGain
  2175. nds += ndr
  2176. nps += ndr
  2177. def readMIDI2Header(self, directory):
  2178. """ Reads the header of the FID_Q1_D0_R1.dat which should be in very MIDI directory
  2179. """
  2180. print("Searching ", directory, "for Q files")
  2181. self.QFiles = []
  2182. for file in glob.glob(directory+'/FID_Q*R1.dat'):
  2183. self.QFiles.append(file)
  2184. fidname = self.QFiles[0] # "/FID_Q1_D0_R1.dat"
  2185. HEADER = {}
  2186. with open(fidname, 'rb') as FID:
  2187. #print(FID.name)
  2188. headerLine = 0
  2189. for i in range(21):
  2190. tags = FID.readline().split(b']')
  2191. tags[0] = tags[0].strip(b'[')
  2192. tags[1] = tags[1].decode().strip( )
  2193. HEADER[ ''.join(map(chr, tags[0])) ] = tags[1]
  2194. #print(HEADER)
  2195. pulseTypeDict = {
  2196. 0 : lambda: "FID",
  2197. 2 : lambda: "T1",
  2198. 3 : lambda: "SPINECHO",
  2199. 4 : lambda: "4PhaseT1"
  2200. }
  2201. pulseLengthDict = {
  2202. 1 : lambda x: np.ones(1) * x,
  2203. 2 : lambda x: np.ones(2) * x,
  2204. 3 : lambda x: np.array([x, 2.*x]),
  2205. 4 : lambda x: np.ones(2) * x
  2206. }
  2207. if HEADER["P2"] == "0":
  2208. self.pulseType = "FID"
  2209. else:
  2210. self.pulseType = "T1"
  2211. self.transFreq = float(HEADER[ 'Excitation frequency /Hz' ])
  2212. self.maxBusV = '24 V'
  2213. self.pulseLength = [ float(HEADER['P1'])/self.transFreq, float(HEADER['P2'])/self.transFreq]
  2214. self.interpulseDelay = 1e-3*float(HEADER["Delay /ms"]) # for T2, Spin Echo
  2215. self.repetitionDelay = float(HEADER['Pause /s']) # delay between q pulses
  2216. self.nPulseMoments = len(self.QFiles) # Number of pulse moments per stack
  2217. self.TuneCapacitance = 0 # tuning capacitance in uF
  2218. self.nTransVersion = "MIDI 2" # Transmitter version
  2219. self.nDAQVersion = HEADER["Software Revision"] # DAQ software version
  2220. self.nInterleaves = 0 # num interleaves
  2221. self.Instrument = "MIDI 2"
  2222. self.datadir = directory
  2223. self.MIDIGain = HEADER["Gains"].split()
  2224. # default
  2225. self.samp = float(HEADER["Data Rate /Hz"]) # sampling frequency
  2226. self.dt = 1./self.samp # sampling rate
  2227. def loadMIDI2(self, directory, procStacks, chanin, rchanin, FIDProc, canvas, deadTime, pulseMoments, plot):
  2228. """Reads a MRS MIDI2 experiment.
  2229. """
  2230. print("Loading MIDI2 data")
  2231. canvas.reAx3(True,False)
  2232. chan = []
  2233. for ch in chanin:
  2234. chan.append(str(ch))
  2235. rchan = []
  2236. for ch in rchanin:
  2237. rchan.append(str(ch))
  2238. #print(self.QFiles)
  2239. # Set up the same structure as GMR
  2240. PULSES = [FIDProc]
  2241. PULSES = ["Pulse 1"]
  2242. if pulseMoments[0] == -1:
  2243. print("using default pulse moments, all of them")
  2244. self.pulseMoments = range(0, self.nPulseMoments) # use all of them, assume starting at 0
  2245. else:
  2246. self.pulseMoments = pulseMoments
  2247. self.nPulseMoments = len(self.pulseMoments)
  2248. print("nPulseMoments", self.nPulseMoments, self.pulseMoments)
  2249. self.DATADICT = {}
  2250. self.DATADICT["nPulseMoments"] = self.nPulseMoments
  2251. self.DATADICT["stacks"] = procStacks
  2252. self.DATADICT["PULSES"] = PULSES
  2253. for pulse in PULSES:
  2254. self.DATADICT[pulse] = {}
  2255. self.DATADICT[pulse]["chan"] = chan # TODO these should not be a subet of pulse! for GMR all
  2256. self.DATADICT[pulse]["rchan"] = rchan # data are consistent
  2257. self.DATADICT[pulse]["CURRENT"] = {}
  2258. for ichan in np.append(chan,rchan):
  2259. self.DATADICT[pulse][ichan] = {}
  2260. for ipm in range(self.nPulseMoments):
  2261. self.DATADICT[pulse][ichan][ipm] = {}
  2262. self.DATADICT[pulse]["CURRENT"][ipm] = {}
  2263. for istack in procStacks:
  2264. pass
  2265. #print("pulse", pulse, "ichan",type(ichan), ichan, "ipm", type(ipm), ipm, "istack",type(istack), istack)
  2266. #
  2267. #self.DATADICT[pulse][ichan][ipm][istack] = np.zeros(3)
  2268. #self.DATADICT[pulse]["CURRENT"][ipm][istack] = np.zeros(3)
  2269. iistack = 0
  2270. idead = int( (self.pulseLength[0]+deadTime) / self.dt)
  2271. for ipm, iq in enumerate(self.pulseMoments):
  2272. fidbase = self.datadir #+ self.QFiles[iq][0:-5]
  2273. for istack in procStacks:
  2274. #fidname = fidbase + str(iq).zfill(2) + ".dat"
  2275. fidname = fidbase + "/FID_Q" + str(iq) + "_D0_R" + str(istack) + ".dat"
  2276. with open(fidname, 'rb') as FID:
  2277. #print(FID.name)
  2278. headerLine = 0
  2279. for i in range(100):
  2280. line = FID.readline().strip()
  2281. headerLine += 1
  2282. if line == b'[Begin Data]':
  2283. break
  2284. # use numpy for now, consider pandas for faster read?
  2285. print("Loading", FID)
  2286. DATA = np.genfromtxt(FID, skip_header=0, skip_footer=1 )
  2287. #DATA = pd.read_csv(fidname, skiprows=headerLine, skipfooter=1, sep='\t', encoding='ascii')
  2288. for ichan in np.append(chan,rchan):
  2289. if int(ichan) <= 3:
  2290. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[idead:,int(ichan)] / float(self.MIDIGain[int(ichan)-1])
  2291. elif int(ichan) > 3:
  2292. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[idead:,int(ichan)+1] / float(self.MIDIGain[int(ichan)-1])
  2293. # truncate after dead time
  2294. self.DATADICT["Pulse 1"]["TIMES"] = DATA[idead:,0]
  2295. # truncate until dead time
  2296. ipulse = int(self.pulseLength[0] / self.dt)
  2297. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = DATA[0:ipulse,0]
  2298. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[0:ipulse,4]
  2299. if plot:
  2300. canvas.softClear()
  2301. for ichan in chan:
  2302. canvas.ax1.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
  2303. 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')
  2304. for ichan in rchan:
  2305. 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')
  2306. # reference axis
  2307. if len(rchan) > 0:
  2308. canvas.ax2.legend(prop={'size':10}, loc='upper right')
  2309. canvas.ax2.tick_params(axis='both', which='major', labelsize=10)
  2310. canvas.ax2.tick_params(axis='both', which='minor', labelsize=10)
  2311. #canvas.ax2.xaxis.set_ticklabels([])
  2312. plt.setp(canvas.ax2.get_xticklabels(), visible=False)
  2313. canvas.ax2.set_title("stack "+str(istack)+" pulse index " + str(iq), fontsize=10)
  2314. canvas.ax2.set_ylabel("RAW signal [V]", fontsize=10)
  2315. canvas.ax1.set_ylabel("Current (A)", fontsize=10)
  2316. canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2317. canvas.ax1.set_xlabel("time (s)", fontsize=10)
  2318. canvas.ax3.legend(prop={'size':10}, loc='upper right')
  2319. canvas.ax3.set_ylabel("RAW signal [V]", fontsize=10)
  2320. canvas.fig.tight_layout()
  2321. canvas.draw()
  2322. percent = (int) (1e2*((float)(iistack) / (len(procStacks)*self.nPulseMoments)))
  2323. self.progressTrigger.emit(percent)
  2324. iistack += 1
  2325. # percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  2326. # self.progressTrigger.emit(percent)
  2327. # iistack += 1
  2328. self.enableDSP()
  2329. self.doneTrigger.emit()
  2330. def loadGMRASCIIT1( self, rawfname, istack ):
  2331. """Based on the geoMRI instrument manufactured by VistaClara. Imports
  2332. a suite of raw .lvm files with the following format (on one line)
  2333. time(s) DC_Bus/100(V) Current+/75(A) Curr-/75(A) Voltage+/200(V) \
  2334. Ch1(V) Ch2(V) Ch3(V) Ch4(V)
  2335. Sampling rate is assumed at 50 kHz
  2336. """
  2337. import pandas as pd
  2338. #################################################################################
  2339. # figure out key data indices
  2340. # Pulse
  2341. nps = (int)((self.prePulseDelay)*self.samp)
  2342. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  2343. # phase cycling
  2344. # Older T1 GMR data had a curious phase cycling
  2345. npc = 2 #(int)( self.samp / self.transFreq / 6 )
  2346. #print("npc", npc)
  2347. # Data
  2348. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  2349. nd1 = (int)( (self.interpulseDelay) * self.samp) - nds # samples in first pulse
  2350. ndr = (int)( (self.interpulseDelay) * self.samp) # samples in record
  2351. invGain = 1./self.RxGain
  2352. invCGain = self.CurrentGain
  2353. pulse = "Pulse 1"
  2354. chan = self.DATADICT[pulse]["chan"]
  2355. rchan = self.DATADICT[pulse]["rchan"]
  2356. T = 1.5 #N_samp * self.dt
  2357. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  2358. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2359. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  2360. # pandas is much faster than numpy for io
  2361. #DATA = np.loadtxt(rawfname)
  2362. DATA = pd.read_csv(rawfname, header=None, sep="\t").values
  2363. for ipm in range(self.nPulseMoments):
  2364. for ichan in np.append(chan,rchan):
  2365. if ipm%2:
  2366. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:, eval(ichan)+4][(nds+npc):(nds+nd1+npc)] * invGain
  2367. #self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:, eval(ichan)+4][nds:(nds+nd1)] * invGain
  2368. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,2][nps+npc:nps+npul+npc] * invCGain
  2369. else:
  2370. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:, eval(ichan)+4][nds:(nds+nd1)] * invGain
  2371. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,2][nps:nps+npul] * invCGain
  2372. nds += ndr
  2373. nps += ndr
  2374. def loadFIDData(self, base, procStacks, chanin, rchanin, FIDProc, canvas, deadTime, plot):
  2375. '''
  2376. Loads a GMR FID dataset, reads binary and ASCII format files
  2377. '''
  2378. canvas.reAx3(True,False)
  2379. chan = []
  2380. for ch in chanin:
  2381. chan.append(str(ch))
  2382. rchan = []
  2383. for ch in rchanin:
  2384. rchan.append(str(ch))
  2385. self.deadTime = deadTime # instrument dead time before measurement
  2386. self.samp = 50000. # in case this is a reproc, these might have
  2387. self.dt = 1./self.samp # changed
  2388. #################################################################################
  2389. # Data structures
  2390. PULSES = [FIDProc]
  2391. PULSES = ["Pulse 1"]
  2392. self.DATADICT = {}
  2393. self.DATADICT["nPulseMoments"] = self.nPulseMoments
  2394. self.DATADICT["stacks"] = procStacks
  2395. self.DATADICT["PULSES"] = PULSES
  2396. for pulse in PULSES:
  2397. self.DATADICT[pulse] = {}
  2398. self.DATADICT[pulse]["chan"] = chan # TODO these should not be a subet of pulse! for GMR all
  2399. self.DATADICT[pulse]["rchan"] = rchan # data are consistent
  2400. self.DATADICT[pulse]["CURRENT"] = {}
  2401. for ichan in np.append(chan,rchan):
  2402. self.DATADICT[pulse][ichan] = {}
  2403. for ipm in range(self.nPulseMoments):
  2404. self.DATADICT[pulse][ichan][ipm] = {}
  2405. self.DATADICT[pulse]["CURRENT"][ipm] = {}
  2406. for istack in procStacks:
  2407. self.DATADICT[pulse][ichan][ipm][istack] = np.zeros(3)
  2408. self.DATADICT[pulse]["CURRENT"][ipm][istack] = np.zeros(3)
  2409. ##############################################
  2410. # Read in binary (.lvm) data
  2411. iistack = 0
  2412. for istack in procStacks:
  2413. if self.nDAQVersion <= 1.0:
  2414. try:
  2415. self.loadGMRASCIIFID( base + "_" + str(istack), istack )
  2416. except:
  2417. self.loadGMRASCIIFID( base + "_" + str(istack) + ".lvm", istack )
  2418. elif self.nDAQVersion < 2.3:
  2419. self.loadGMRASCIIFID( base + "_" + str(istack), istack )
  2420. else:
  2421. self.loadGMRBinaryFID( base + "_" + str(istack) + ".lvm", istack )
  2422. if plot:
  2423. for ipm in range(self.nPulseMoments):
  2424. canvas.softClear()
  2425. for ichan in chan:
  2426. canvas.ax1.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
  2427. 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')
  2428. for ichan in rchan:
  2429. 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')
  2430. # reference axis
  2431. canvas.ax2.tick_params(axis='both', which='major', labelsize=10)
  2432. canvas.ax2.tick_params(axis='both', which='minor', labelsize=10)
  2433. #canvas.ax2.xaxis.set_ticklabels([])
  2434. plt.setp(canvas.ax2.get_xticklabels(), visible=False)
  2435. canvas.ax2.legend(prop={'size':10}, loc='upper right')
  2436. canvas.ax2.set_title("stack "+str(istack)+" pulse index " + str(ipm), fontsize=10)
  2437. canvas.ax2.set_ylabel("RAW signal [V]", fontsize=10)
  2438. canvas.ax1.set_ylabel("Current (A)", fontsize=10)
  2439. canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2440. canvas.ax1.set_xlabel("time (s)", fontsize=10)
  2441. canvas.ax3.legend(prop={'size':10}, loc='upper right')
  2442. canvas.ax3.set_ylabel("RAW signal [V]", fontsize=10)
  2443. canvas.fig.tight_layout()
  2444. canvas.draw()
  2445. percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  2446. self.progressTrigger.emit(percent)
  2447. iistack += 1
  2448. # percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  2449. # self.progressTrigger.emit(percent)
  2450. # iistack += 1
  2451. self.enableDSP()
  2452. self.doneTrigger.emit()
  2453. def loadT1Data(self, base, procStacks, chanin, rchanin, FIDProc, canvas, deadTime, plot):
  2454. '''
  2455. Loads a GMR T1 dataset, reads binary and ASCII format files
  2456. '''
  2457. canvas.reAx3(True,False)
  2458. chan = []
  2459. for ch in chanin:
  2460. chan.append(str(ch))
  2461. rchan = []
  2462. for ch in rchanin:
  2463. rchan.append(str(ch))
  2464. # not in any headers but this has changed, NOT the place to do this. MOVE
  2465. #self.prePulseDelay = 0.01 # delay before pulse
  2466. self.deadTime = deadTime # instrument dead time before measurement
  2467. self.samp = 50000. # in case this is a reproc, these might have
  2468. self.dt = 1./self.samp # changed
  2469. #################################################################################
  2470. # Data structures
  2471. PULSES = [FIDProc]
  2472. self.DATADICT = {}
  2473. self.DATADICT["nPulseMoments"] = self.nPulseMoments
  2474. self.DATADICT["stacks"] = procStacks
  2475. self.DATADICT["PULSES"] = PULSES
  2476. for pulse in PULSES:
  2477. self.DATADICT[pulse] = {}
  2478. self.DATADICT[pulse]["chan"] = chan # TODO these should not be a subet of pulse! for GMR all
  2479. self.DATADICT[pulse]["rchan"] = rchan # data are consistent
  2480. self.DATADICT[pulse]["CURRENT"] = {}
  2481. for ichan in np.append(chan,rchan):
  2482. self.DATADICT[pulse][ichan] = {}
  2483. for ipm in range(self.nPulseMoments):
  2484. self.DATADICT[pulse][ichan][ipm] = {}
  2485. self.DATADICT[pulse]["CURRENT"][ipm] = {}
  2486. for istack in procStacks:
  2487. self.DATADICT[pulse][ichan][ipm][istack] = np.zeros(3)
  2488. self.DATADICT[pulse]["CURRENT"][ipm][istack] = np.zeros(3)
  2489. ##############################################
  2490. # Read in binary (.lvm) data
  2491. iistack = 0
  2492. fnames = []
  2493. for istack in procStacks:
  2494. if self.nDAQVersion < 2.3:
  2495. #rawfname = base + "_" + str(istack)
  2496. #self.loadGMRASCIIFID( base + "_" + str(istack), istack )
  2497. self.loadGMRASCIIT1( base + "_" + str(istack), istack )
  2498. else:
  2499. self.loadGMRBinaryFID( base + "_" + str(istack) + ".lvm", istack )
  2500. #fnames.append( base + "_" + str(istack) + ".lvm" )
  2501. percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  2502. self.progressTrigger.emit(percent)
  2503. iistack += 1
  2504. # multiprocessing load data
  2505. #info = {}
  2506. #info["prePulseDelay"] = self.prePulseDelay
  2507. #info["samp"] = self.samp
  2508. #with multiprocessing.Pool() as pool:
  2509. # results = pool.starmap( xxloadGMRBinaryFID, ( fnames, zip(itertools.repeat(info)) ) )
  2510. # Plotting
  2511. if plot:
  2512. iistack = 0
  2513. for istack in procStacks:
  2514. #for ipm in range(0,7,1):
  2515. for ipm in range(self.nPulseMoments):
  2516. canvas.ax1.clear()
  2517. canvas.ax2.clear()
  2518. canvas.ax3.clear()
  2519. #canvas.fig.patch.set_facecolor('blue')
  2520. for ichan in chan:
  2521. canvas.ax1.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
  2522. 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')
  2523. for ichan in rchan:
  2524. 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')
  2525. canvas.ax3.legend(prop={'size':6}, loc='upper right')
  2526. canvas.ax2.legend(prop={'size':6}, loc='upper right')
  2527. canvas.ax1.set_title("stack "+str(istack)+" pulse index " + str(ipm), fontsize=8)
  2528. canvas.ax1.set_xlabel("time [s]", fontsize=8)
  2529. canvas.ax1.set_ylabel("Current [A]", fontsize=8)
  2530. canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2531. canvas.ax2.set_ylabel("RAW signal [V]", fontsize=8)
  2532. canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  2533. canvas.ax2.tick_params(axis='both', which='minor', labelsize=6)
  2534. canvas.ax2.set_xlabel("time [s]", fontsize=8)
  2535. canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2536. canvas.ax3.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2537. canvas.draw()
  2538. #canvas.draw()
  2539. percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  2540. self.progressTrigger.emit(percent)
  2541. iistack += 1
  2542. self.enableDSP()
  2543. self.doneTrigger.emit()
  2544. def load4PhaseT1Data(self, base, procStacks, chan, rchan, FIDProc, canvas, deadTime, plot):
  2545. """
  2546. Designed to load GMR 4-phase data which use the following convention for phase cycles
  2547. P1 P2
  2548. Stack 1 -> 0 0 <-- <--
  2549. Stack 2 -> 0 pi/2 | <-- <--
  2550. Stack 3 -> pi/2 0 <-- | <--
  2551. Stack 4 -> pi/2 pi/2 <-- <--
  2552. The cycle is determined by stack indice. Walbrecker proposes for pulse2 data (Stack2 - Stack1) / 2
  2553. equivalently (Stack 4 - Stack3) will yield the same voltage response wrt. the second pulse.
  2554. 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
  2555. Then there are just the two phase cycles that can be stacked like normal.
  2556. Unfortunately, we need to stack each cycle first, then perform corrections for phase cycling. The reason for this is that otherwise,
  2557. 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,
  2558. if there is an uneven number of a certain phase cycle.
  2559. We could, I suppose impose this condition, but I think I would rather not?
  2560. + more samples for std. deviation calculation
  2561. + single spikes will have less residual effect
  2562. - can no longer do normality tests etc. and remove data that are suspect.
  2563. - requires a dumb stack, and may also require removal of entire stacks of data
  2564. 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 ...
  2565. This however, is altered by the above convention. It gets a little complicated...
  2566. """
  2567. import struct
  2568. canvas.reAx3()
  2569. # not in any headers but this has changed, NOT the place to do this. MOVE
  2570. self.prePulseDelay = 0.01 # delay before pulse
  2571. self.deadTime = deadTime # instrument dead time before measurement
  2572. self.samp = 50000. # in case this is a reproc, these might have
  2573. self.dt = 1./self.samp # changed
  2574. invGain = 1./self.RxGain
  2575. invCGain = self.CurrentGain
  2576. #################################################################################
  2577. # figure out key data indices
  2578. # Pulse
  2579. nps = (int)((self.prePulseDelay)*self.samp)
  2580. nps2 = (int)((self.prePulseDelay+self.interpulseDelay)*self.samp)
  2581. npul = (int)(self.pulseLength[0]*self.samp) #+ 100
  2582. np2 = (int)(self.pulseLength[1]*self.samp) #+ 100
  2583. # Data
  2584. nds = nps+npul+(int)((self.deadTime)*self.samp); # indice pulse 1 data starts
  2585. nd1 = (int)((self.interpulseDelay)*self.samp) # samples in first pulse
  2586. nd2s = nps+npul+nd1+(int)((self.deadTime)*self.samp); # indice pulse 2 data starts
  2587. nd2 = (int)((1.)*self.samp) # samples in first pulse
  2588. nd1 -= (int)((.028)*self.samp) + nps # some time to get ready for next pulse
  2589. #################################################################################
  2590. # Data structures
  2591. PULSES = [FIDProc]
  2592. if FIDProc == "Both":
  2593. PULSES = ["Pulse 1","Pulse 2"]
  2594. self.DATADICT = {}
  2595. self.DATADICT["nPulseMoments"] = self.nPulseMoments
  2596. self.DATADICT["stacks"] = procStacks
  2597. self.DATADICT["PULSES"] = PULSES
  2598. for pulse in PULSES:
  2599. self.DATADICT[pulse] = {}
  2600. self.DATADICT[pulse]["chan"] = chan
  2601. self.DATADICT[pulse]["rchan"] = rchan
  2602. self.DATADICT[pulse]["CURRENT"] = {}
  2603. for ichan in np.append(chan,rchan):
  2604. self.DATADICT[pulse][ichan] = {}
  2605. for ipm in range(self.nPulseMoments):
  2606. self.DATADICT[pulse][ichan][ipm] = {}
  2607. self.DATADICT[pulse]["CURRENT"][ipm] = {}
  2608. for istack in procStacks:
  2609. self.DATADICT[pulse][ichan][ipm][istack] = np.zeros(3)
  2610. self.DATADICT[pulse]["CURRENT"][ipm][istack] = np.zeros(3)
  2611. ##############################################
  2612. # Read in binary data
  2613. iistack = 0
  2614. for istack in procStacks:
  2615. rawFile = open(base + "_" + str(istack) + ".lvm", 'rb')
  2616. for ipm in range(self.nPulseMoments):
  2617. N_chan = struct.unpack('>i', rawFile.read(4))[0]
  2618. N_samp = struct.unpack('>i', rawFile.read(4))[0]
  2619. T = N_samp * self.dt
  2620. TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
  2621. DATA = np.zeros([N_samp, N_chan+1])
  2622. for ichan in range(N_chan):
  2623. DATADUMP = rawFile.read(4*N_samp)
  2624. for irec in range(N_samp):
  2625. DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
  2626. if plot:
  2627. #canvas.ax1.clear()
  2628. #canvas.ax2.clear()
  2629. canvas.softClear()
  2630. li = np.shape( DATA[:,4][nd2s:nd2s+nd2] )[0]
  2631. ######################################
  2632. # save into DATA cube
  2633. # TODO, changing iFID to 'Pulse 1' or 'Pulse 2'
  2634. for ichan in chan:
  2635. if FIDProc == "Pulse 1":
  2636. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,ichan+3][nds:nds+nd1] * invGain
  2637. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2638. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
  2639. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  2640. if plot:
  2641. 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')
  2642. canvas.ax1.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
  2643. elif FIDProc == "Pulse 2":
  2644. print("TODO fix y scale")
  2645. self.DATADICT["Pulse 2"][ichan][ipm][istack] = DATA[:,ichan+3][nd2s:nd2s+nd2] *invGain
  2646. self.DATADICT["Pulse 2"]["TIMES"] = TIMES[nd2s:nd2s+nd2]
  2647. self.DATADICT["Pulse 2"]["CURRENT"][ipm][istack] = DATA[:,1][nps2:nps2+np2] * invCGain
  2648. self.DATADICT["Pulse 2"]["PULSE_TIMES"] = TIMES[nps2:nps2+np2]
  2649. if plot:
  2650. canvas.ax3.plot(self.DATADICT["Pulse 2"]["TIMES"], self.DATADICT["Pulse 2"][ichan][ipm][istack], label="Pulse 2 FID data ch. "+str(ichan)) #, color='blue')
  2651. canvas.ax1.plot( self.DATADICT["Pulse 2"]["PULSE_TIMES"], self.DATADICT["Pulse 2"]["CURRENT"][ipm][istack], color='black' )
  2652. else:
  2653. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,ichan+3][nds:nds+nd1] * invGain
  2654. self.DATADICT["Pulse 2"][ichan][ipm][istack] = DATA[:,ichan+3][nd2s:nd2s+nd2] * invGain
  2655. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2656. self.DATADICT["Pulse 2"]["TIMES"] = TIMES[nd2s:nd2s+nd2]
  2657. self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
  2658. self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
  2659. self.DATADICT["Pulse 2"]["CURRENT"][ipm][istack] = DATA[:,1][nps2:nps2+np2] * invCGain
  2660. self.DATADICT["Pulse 2"]["PULSE_TIMES"] = TIMES[nps2:nps2+np2]
  2661. if plot:
  2662. 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')
  2663. canvas.ax3.plot(self.DATADICT["Pulse 2"]["TIMES"], self.DATADICT["Pulse 2"][ichan][ipm][istack], label="Pulse 2 FID data ch. "+str(ichan)) #, color='blue')
  2664. canvas.ax1.plot( self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black' )
  2665. canvas.ax1.plot( self.DATADICT["Pulse 2"]["PULSE_TIMES"], self.DATADICT["Pulse 2"]["CURRENT"][ipm][istack] , color='black')
  2666. for ichan in rchan:
  2667. if FIDProc == "Pulse 1":
  2668. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,ichan+3][nds:nds+nd1] * invGain
  2669. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2670. if plot:
  2671. 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')
  2672. elif FIDProc == "Pulse 2":
  2673. self.DATADICT["Pulse 2"][ichan][ipm][istack] = DATA[:,ichan+3][nd2s:nd2s+nd2] * invGain
  2674. self.DATADICT["Pulse 2"]["TIMES"] = TIMES[nd2s:nd2s+nd2]
  2675. if plot:
  2676. canvas.ax2.plot(self.DATADICT["Pulse 2"]["TIMES"], self.DATADICT["Pulse 2"][ichan][ipm][istack], label="Pulse 2 FID ref ch. "+str(ichan)) #, color='blue')
  2677. else:
  2678. self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,ichan+3][nds:nds+nd1] * invGain
  2679. self.DATADICT["Pulse 2"][ichan][ipm][istack] = DATA[:,ichan+3][nd2s:nd2s+nd2] * invGain
  2680. self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
  2681. self.DATADICT["Pulse 2"]["TIMES"] = TIMES[nd2s:nd2s+nd2]
  2682. if plot:
  2683. 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')
  2684. canvas.ax2.plot(self.DATADICT["Pulse 2"]["TIMES"], self.DATADICT["Pulse 2"][ichan][ipm][istack], label="Pulse 2 FID ref ch. "+str(ichan)) #, color='blue')
  2685. if plot:
  2686. canvas.ax3.legend(prop={'size':6}, loc='upper right')
  2687. canvas.ax2.legend(prop={'size':6}, loc='upper right')
  2688. canvas.ax1.set_title("stack "+str(istack)+" pulse index " + str(ipm), fontsize=8)
  2689. canvas.ax1.set_xlabel("time [s]", fontsize=8)
  2690. canvas.ax3.set_ylabel("RAW signal [V]", fontsize=8)
  2691. canvas.ax2.set_ylabel("RAW signal [V]", fontsize=8)
  2692. canvas.ax1.set_ylabel("Current [A]", fontsize=8)
  2693. #canvas.ax2.tick_params(axis='both', which='major', labelsize=8)
  2694. #canvas.ax2.tick_params(axis='both', which='minor', labelsize=6)
  2695. #canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2696. #canvas.ax1.ticklabel_format(style='sci', scilimits=(0,0), axis='y')
  2697. canvas.draw()
  2698. # update GUI of where we are
  2699. percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1)) / (len(procStacks)*self.nPulseMoments)))
  2700. self.progressTrigger.emit(percent)
  2701. iistack += 1
  2702. self.enableDSP()
  2703. self.doneTrigger.emit()
  2704. if __name__ == "__main__":
  2705. if len(sys.argv) < 4:
  2706. print( "mrsurvey path/to/header <stack1> <stackN> ")
  2707. exit()
  2708. GMR = GMRDataProcessor()
  2709. GMR.readHeaderFile(sys.argv[1])
  2710. GMR.Print()
  2711. if GMR.pulseType == "FID":
  2712. GMR.loadFIDData(sys.argv[1], sys.argv[2], sys.argv[3], 5)
  2713. if GMR.pulseType == "4PhaseT1":
  2714. GMR.load4PhaseT1Data(sys.argv[1], sys.argv[2], sys.argv[3], 5)
  2715. pylab.show()