diff --git a/Chimera.py b/Chimera.py index da9f88b..2c7caf0 100644 --- a/Chimera.py +++ b/Chimera.py @@ -1,284 +1,289 @@ import ChimeraSettings import ok import ChimeraControls import os import pickle import datetime import numpy as np import time import csv import matplotlib.pyplot as plt import functions from scipy import io from matplotlib.ticker import EngFormatter Amp = EngFormatter(unit='A', places=2) Time = EngFormatter(unit='s', places=2) Volt = EngFormatter(unit='V', places=2) Cond = EngFormatter(unit='S', places=2) # Main Chimera Launcher class Launcher: def __init__(self, loadedInstance=None): if loadedInstance is None: self.ChimeraSettings = ChimeraSettings.ChimeraSettings() if os.path.isfile("store.pckl"): self.ChimeraSettings.setStoredValues() else: self.ChimeraSettings = loadedInstance # Initialize Chimera self.xem = ok.FrontPanel() ChimeraControls.InitializeChimera(self.ChimeraSettings, self.xem) # Control variables self.newbiasvalue = 0 # Set saving variables self.DataFolder = None self.todaysfolder = None self.experimentName = None self.file = None self.savename = None self.SetSavingFolder(os.getcwd()) self.ADClogsize = 0 self.saveDownsampled = False # Plotting Variables + self.displaysubsample = 50 self.lowpass = 10e3 self.displaySamplerate = self.lowpass*2 # IV variables self.IVUseAlternatingV = True self.LowerIV = 0.0 self.HigherIV = 0.0 self.StepIV = 0.0 self.timeIV = 2 # Data variables self.blockvalues = np.empty((1048576,), dtype=np.uint16) self.readvalues = ChimeraControls.ConvertBlockvalues(self.ChimeraSettings, self.blockvalues) self.displaybuffer = np.empty((65536,), dtype=np.uint16) def Initialize(self): ChimeraControls.InitializeChimera(self.ChimeraSettings, self.xem) def SetSavingFolder(self, savingFolder=None, experimentName='Untitled'): if savingFolder is not None: self.DataFolder = savingFolder self.todaysfolder = str(datetime.date.today()) self.experimentName = experimentName if not os.path.exists(self.DataFolder): os.makedirs(self.DataFolder) if not os.path.exists(os.path.join(self.DataFolder, self.todaysfolder)): os.makedirs(os.path.join(self.DataFolder, self.todaysfolder)) self.savename = os.path.join(self.DataFolder, self.todaysfolder, self.experimentName) print('Files are saved in ' + self.savename) def CreateSavename(self, IV=False): savingFolder = os.path.join(self.DataFolder, self.todaysfolder) now = datetime.datetime.now() nowstr = datetime.datetime.strftime(now, '%Y%m%d_%H%M%S') if IV: savingName = '{}'.format(self.experimentName), '{}{}{}'.format('IV_', self.experimentName, nowstr) else: savingName = '{}'.format(self.experimentName), '{}{}'.format(self.experimentName, nowstr) self.savename = os.path.join(savingFolder, savingName) print('Savename: ' + self.savename) def SetExperimentName(self, experimentName): self.SetSavingFolder(self.DataFolder, experimentName=experimentName) def SaveSettings(self, fileName): with open(fileName, 'rb') as file: pickle.dump(self.ChimeraSettings, file) print('Saved as ' + fileName) def LoadSettings(self, fileName): with open(fileName, 'rb') as file: self.ChimeraSettings = pickle.load(file) print('Loaded from ' + fileName + '. Re-initializing...') ChimeraControls.InitializeChimera(self.ChimeraSettings, self.xem) def updateBW(self, printtext=True): out = ChimeraControls.CHIMERA_bandwidthtimer(self.ChimeraSettings, self.xem) if printtext: text1 = "Time\n{0}\nUSB Read Rate\n{1:} kHz\nUSB Write Rate\n{2}".format(out['text_time'], out['USB_readrate'], out['USB_writerate']) text2 = "Buffer\n{0} kB\nBuffer Size\n{1} %\nSeconds buffered\n{2:.3f} sec".format(out['buffer'], out['bufferPC'], out['bufferseconds']) print(text1) print(text2) return out def ResetBuffer(self): print("Buffer is resetting...", end='') self.xem.SetWireInValue(self.ChimeraSettings.EP_WIREIN_TEST1, self.ChimeraSettings.EPBIT_GLOBALRESET, self.ChimeraSettings.EPBIT_GLOBALRESET) self.xem.UpdateWireIns() time.sleep(0.1) self.xem.SetWireInValue(self.ChimeraSettings.EP_WIREIN_TEST1, 0, self.ChimeraSettings.EPBIT_GLOBALRESET) self.xem.UpdateWireIns() print('done') def ZeroVolt(self): Vdelta = 0.02 print('Measuring at {} ...'.format(Volt.format_data(Vdelta)), end='') self.SetVoltage(Vdelta, write=False) self.ResetBuffer() time.sleep(1) measI1 = np.mean(self.RecordTrace()) print('{}'.format(Amp.format_data(measI1))) print('Measuring at {} ...'.format(Volt.format_data(0)), end='') self.SetVoltage(0, write=False) self.ResetBuffer() time.sleep(1) measI2 = np.mean(self.RecordTrace()) print('{}'.format(Amp.format_data(measI2))) deltaI = measI1-measI2 if deltaI == 0: print('Error, cannot zero voltage: deltaI = 0') else: measR = Vdelta / deltaI self.ResetBuffer() self.ChimeraSettings.voltageOffset += -measI2 * measR print('New voltage offset: {}'.format(Volt.format_data(self.ChimeraSettings.voltageOffset))) ChimeraControls.CHIMERA_updateDACvalues1(self.ChimeraSettings, self.xem, 0) self.SetVoltage(self.ChimeraSettings.voltageOffset) def SetVoltage(self, sb, write=True): self.newbiasvalue = sb # self.ChimeraSettings.RestartBuffer = True if write: self.SaveVoltage() ChimeraControls.CHIMERA_updateDACvalues1(self.ChimeraSettings, self.xem, self.newbiasvalue) def RecordTraceRaw(self): self.blockvalues = ChimeraControls.CHIMERA_process_triggers(self.ChimeraSettings, self.xem) self.readvalues = ChimeraControls.ConvertBlockvalues(self.ChimeraSettings, self.blockvalues) return self.readvalues def RecordTrace(self): self.RecordTraceRaw() # Samplerate = self.ChimeraSettings.ADCSAMPLERATE # displaybuffer, self.displaySamplerate = functions.LowPass(readvalues, self.ChimeraSettings, self.lowpass) # displaybuffer = ChimeraControls.lowpass(self.ChimeraSettings, readvalues, cutoff=self.lowpass) self.displaybuffer, self.displaySamplerate = \ - functions.LowPassFast(self.readvalues, self.ChimeraSettings, self.lowpass) + functions.LowPassFast(self.readvalues, self.ChimeraSettings, self.displaysubsample, self.lowpass) return self.displaybuffer def SaveVoltage(self): savingFolder = os.path.join(self.DataFolder, self.todaysfolder) savingName = self.experimentName + 'Vdata.csv' fullpath = os.path.join(savingFolder, savingName) now = datetime.datetime.now() csvstr = datetime.datetime.strftime(now, '%Y, %m, %d, %H, %M, %S') data = [csvstr, self.newbiasvalue] with open(fullpath, 'w') as csvfile: writeline = csv.writer(csvfile, delimiter=',') writeline.writerows(data) def WriteRawData(self): if self.saveDownsampled: self.file.write(self.displaybuffer.copy(order='C')) self.ADClogsize += 2 * len(self.displaybuffer) else: self.file.write(np.uint16(self.blockvalues)) self.ADClogsize += 2 * len(self.blockvalues) def writeSummary(self): if self.saveDownsampled: samplerate = self.ChimeraSettings.ADCSAMPLERATE/self.ChimeraSettings.displaysubsample else: samplerate = self.ChimeraSettings.ADCSAMPLERATE mdict = {'DisplayBuffer': len(self.displaybuffer), 'ADCSAMPLERATE': samplerate, 'mytimestamp': str(datetime.datetime.today()), 'bias2value': self.ChimeraSettings.newbiasvalue, 'SETUP_biasvoltage': self.ChimeraSettings.newbiasvalue, 'SETUP_ADCVREF': self.ChimeraSettings.ADCVREF, 'SETUP_ADCSAMPLERATE': self.ChimeraSettings.ADCSAMPLERATE, 'SETUP_ADCBITS': self.ChimeraSettings.ADCBITS, 'SETUP_TIAgain': self.ChimeraSettings.SETUP_TIAgain, 'SETUP_preADCgain': self.ChimeraSettings.SETUP_preADCgain, 'SETUP_pAoffset': self.ChimeraSettings.SETUP_pAoffset, 'SETUP_mVoffset': self.ChimeraSettings.myvoltageoffset} io.savemat(self.savename + '.mat', mdict, True, '5', False, False, 'row') def SaveData(self): if self.file is not None and not self.file.closed and self.ADClogsize < self.ChimeraSettings.MAXLOGBYTES: self.WriteRawData() else: # Create new filename self.CreateSavename() # open file and reset logsize self.file = open(self.savename + '.log') self.ADClogsize = 0 # Write summary .mat file and write raw .log file self.writeSummary() self.WriteRawData() class ChimeraPlot: def __init__(self, Chimera): self.Chimera = Chimera self.fig, self.ax = plt.subplots(figsize=(9.5, 4.5)) self.ax.set_xlabel('Time [s]') self.ax.set_ylabel('Current [nA]') self.ax.set_title(self.Chimera.savename) self.buffertext = plt.figtext(0.95, 0.5, '') - self.samplerate = Chimera.displaySamplerate + self.samplerate = Chimera.ChimeraSettings.ADCSAMPLERATE/Chimera.ChimeraSettings.displaysubsample def Update(self, trace): x = np.arange(len(trace)) / self.samplerate currentIDC = np.mean(trace) currentRMS = np.std(trace) if self.ax.lines: for line in self.ax.lines: - line.set_xdata(x) + #line.set_xdata(x) line.set_ydata(trace) else: self.ax.plot(x, trace) + plt.show(block=False) + self.fig.canvas.draw() self.ax.set_ylim([currentIDC - 10 * currentRMS, currentIDC + 10 * currentRMS]) + self.ax.set_xlim([0, len(trace)/self.samplerate]) self.fig.canvas.draw() + self.fig.canvas.flush_events() # write buffer if needed now = datetime.datetime.now() if round(now.second) == 10: out = self.Chimera.updateBW(printtext=False) textstr = "Buffer\n{0} kB\nBuffer Size\n{1} %\nSeconds buffered\n{2:.3f} sec".format(out['buffer'], out['bufferPC'], out['bufferseconds']) self.buffertext.set_text(textstr) diff --git a/ChimeraSettings.py b/ChimeraSettings.py index af6dc28..fc02dec 100644 --- a/ChimeraSettings.py +++ b/ChimeraSettings.py @@ -1,159 +1,158 @@ import datetime import numpy as np import pickle import os class ChimeraSettings: def __init__(self): # Make sure this corresponds to your chimera calibration self.freqComp = 19 self.SETUP_pAoffset = 1365e-12 self.SETUP_TIAgain = 100e6 self.SETUP_preADCgain = 1.235 self.k = 1.3806503e-23 self.T = 298 self.IVOn = 0 self.currentIDC = 0 self.currentRMS = 0 self.newbiasvalue = 0 self.myCfast = 11E-12 self.voltageOffset = -274E-3 # self.AllVoltages = 0 # self.IVCurrentV = 0.0 self.Threshold = 8 self.EventLimit = 0 self.currentRMSRaw = 0 # self.file = 0 # self.writtenBlocks = 0 # self.blockvalues = np.empty((1048576,), dtype=np.uint16) self.StockTime = 4 self.StockData = np.empty((0,), dtype=np.uint16) # self.RestartBuffer = 0 # Zaps self.ZapVoltage = -0.3 self.ZapTime = 0.5 self.ZapFrequency = 2 self.VoltageBeforeZap = 0.0 self.ZapOnce = 0 # VoltageBeforeZap = 0.0 self.ZappingInputChoice = 0 self.SAVECONFIGFILE = "startupconfig.mat" self.EVENTLOGFILE = "EVENT_LOG.txt" self.LOGFILE_DIR = "logfiles" self.FPGAMODEL = "spartan6_usb3" # CLK1FREQDEFAULT = 100e6; # CLK2FREQDEFAULT = 4e6; self.ADCSAMPLERATE = 100e6 / 24 self.HWBUFFERSIZE_KB = 128 * 1024 self.ADCMUXDEFAULT = 2 self.RAWDATAFORMAT = 'uint32' self.MAXLOGBYTES = 20e6 self.CFAST_gain = 2 self.CFAST_CAP0 = 1e-12 self.CFAST_CAP1 = 10e-12 self.ADC_xfer_len = 4 * 1024 * 1024 ##Todo delete? self.ADC_pkt_len = self.ADC_xfer_len self.ADC_xfer_blocksize = 512 self.ADCcaptureenable = 1 self.ADCnumsignals = 1 self.ADCBITS = 14 # this may get overwritten by startupconfig.mat self.ADCVREF = 2.5 # this may get overwritten by startupconfig.mat self.ADClogenable = 0 self.ADClogsize = 0 self.ADClogreset = 0 self.logfid = -1 self.DACBITS = 16 self.DACFULLSCALE = 4.5 self.DACcurrent = 0 self.DACprevious = 0 self.ADC_xfer_time = self.ADC_xfer_len / self.ADCSAMPLERATE self.EP_TRIGGERIN_TEST1 = int('40', 16) self.EP_TRIGGEROUT_TEST1 = int('60', 16) self.EP_WIREIN_TEST1 = int('00', 16) self.EP_WIREIN_ADCMUX = int('01', 16) self.EP_WIREOUT_TEST1 = int('20', 16) self.EP_WIREOUT_ADCFIFOCOUNT = int('21', 16) self.EP_PIPEIN_SCANCHAINS = int('80', 16) self.EP_PIPEOUT_ADC = int('A0', 16) BITMASK_ALL = int("ffff", 16) BITMASK_0 = int("0001", 16) BITMASK_1 = int("0002", 16) BITMASK_2 = int("0004", 16) BITMASK_3 = int("0008", 16) BITMASK_4 = int("0010", 16) BITMASK_5 = int("0020", 16) BITMASK_6 = int("0040", 16) BITMASK_7 = int("0080", 16) BITMASK_8 = int("0100", 16) BITMASK_9 = int("0200", 16) BITMASK_10 = int("0400", 16) BITMASK_11 = int("0800", 16) BITMASK_12 = int("1000", 16) BITMASK_13 = int("2000", 16) BITMASK_14 = int("4000", 16) BITMASK_15 = int("8000", 16) self.EPBIT_ENABLEADCFIFO = BITMASK_4 self.EPBIT_GLOBALRESET = BITMASK_15 self.EPBIT_DACSDI = BITMASK_0 self.EPBIT_DACCLK = BITMASK_1 self.EPBIT_DACNCS = BITMASK_2 self.EPBIT_DACSDI_FASTC = BITMASK_8 self.EPBIT_DAC_NLOAD = BITMASK_10 self.EPBIT_DPOT_CLK = BITMASK_4 self.EPBIT_DPOT_SDI = BITMASK_5 self.EPBIT_DPOT_NCS = BITMASK_6 self.EPBIT_FASTC_CAPMUX = BITMASK_9 self.EPBIT_DAQTRIGGER = BITMASK_15 self.EPBIT_ADCDATAREADY = BITMASK_0 self.EPBIT_ADC1MUX0 = BITMASK_0 self.EPBIT_ADC1MUX1 = BITMASK_1 self.EPBIT_HEADSTAGE_PWR_EN = BITMASK_0 self.EPBIT_HEADSTAGE_PWR_LED = BITMASK_1 self.EP_ACTIVELOWBITS = self.EPBIT_DACNCS + self.EPBIT_DAC_NLOAD + self.EPBIT_DPOT_NCS self.DAC_SCANCHAIN_LENGTH = 16 self.DAC_MSB = 16 self.DAC_LSB = 1 self.DPOT_SCANCHAIN_LENGTH = 9 self.DPOT_A = 9 self.DPOT_MSB = 8 self.DPOT_LSB = 1 self.read_monitor = 0 self.write_monitor = 0 self.timezero = datetime.datetime.today() # self.displaybuffertime = np.floor(self.ADC_xfer_time * 10) / 50 # self.displaycount = 1 # self.displayupdaterate = 2 - self.displaysubsample = 16 # self.displaybuffersize = int(np.ceil(self.ADCnumsignals * self.ADCSAMPLERATE * self.displaybuffertime / # self.displaysubsample)) # self.displaybuffer = np.zeros(self.displaybuffersize, dtype=np.float) # self.flag = 0 # self.ChimeraIsOn = 1 def setStoredValues(self): f = open('store.pckl', 'rb') variables = pickle.load(f) print('Loaded Variables: {}'.format(variables)) self.voltageOffset = variables['Voffset'] self.SETUP_pAoffset = variables['pAOffset'] f.close() def Loadettings(self, fileName): f = open(fileName, 'rb') self = pickle.load(f) f.close() diff --git a/functions.py b/functions.py index 6fca9d4..38ac35e 100644 --- a/functions.py +++ b/functions.py @@ -1,51 +1,51 @@ import scipy from scipy import signal import numpy as np def LowPass(data, ChimeraSettings, lowPass): """ Function used to filter data with a digital Bessel filter of the 4th order. Specially useful for high bandwidth recordings. Parameters ---------- data : Data to be filtered. samplerate : float Sampling frequency of the data aquisition. lowPass : float Cutoff frequency of the filter. Returns ------- output: a list of the currents filtered newsamplerate : float of new sampling frequency corresponding to 2*Cutoff frequency (Nyquist-Shannon sampling theorem). """ samplerate = ChimeraSettings.ADCSAMPLERATE Wn = round(2 * lowPass / samplerate, 4) # [0,1] nyquist frequency b, a = signal.bessel(4, Wn, btype='low', analog=False) # 4-th order digital filter z, p, k = signal.tf2zpk(b, a) eps = 1e-9 r = np.max(np.abs(p)) approx_impulse_len = int(np.ceil(np.log(eps) / np.log(r))) Filt_sig = (signal.filtfilt(b, a, data, method='gust', irlen=approx_impulse_len)) ds_factor = np.ceil(samplerate / (2 * lowPass)) output = {} output = scipy.signal.resample(Filt_sig, int(len(data) / ds_factor)) output = output[100:len(output)-100] newsamplerate = samplerate / ds_factor return output, newsamplerate -def LowPassFast(data, ChimeraSettings, lowPass): - downsampled = signal.resample(data, int(1 / ChimeraSettings.displaysubsample * len(data))) - effsamplerate = (ChimeraSettings.ADCSAMPLERATE / ChimeraSettings.displaysubsample) +def LowPassFast(data, ChimeraSettings, displaysubsample, lowPass): + downsampled = signal.resample(data, int(1 / displaysubsample * len(data))) + effsamplerate = (ChimeraSettings.ADCSAMPLERATE / displaysubsample) Wn = round(2 * lowPass / effsamplerate, 4) b, a = signal.bessel(4, Wn, btype='low', analog=False) output = signal.filtfilt(b, a, downsampled) output= output[100:len(output) - 100] return output, ChimeraSettings.displaysubsample \ No newline at end of file