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

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