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

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