diff --git a/Chimera.py b/Chimera.py index 120e83f..691c4a6 100644 --- a/Chimera.py +++ b/Chimera.py @@ -1,490 +1,495 @@ 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("C:\\ChimeraPython\\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.MaxV_IV = 0.4 self.StepIV = 0.05 self.timeIV = 4 # Event detection self.Threshold = 5 # standard deviations self.DwellTime = .5 # in ms self.upwardevents = False # 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) self.ShowSettings() def ShowSettings(self): print('Settings:') print('Saving variables:') print('savename = ' + str(self.savename)) print('saveDownsampled = ' + str(self.saveDownsampled)) print('') print('Plotting Variables:') print('displaysubsample = ' + str(self.displaysubsample)) print('lowpass (Hz) = ' + str(self.lowpass)) print('') print('IV variables:') print('MaxV_IV (V) = ' + str(self.MaxV_IV)) print('StepIV (V) = ' + str(self.StepIV)) print('Time (s) = ' + str(self.timeIV)) print('') print('Event Detection:') print('Threshold (* st.dev) = ' + str(self.Threshold)) print('DwellTime (ms) = ' + str(self.DwellTime)) print('upwardevents = ' + str(self.upwardevents)) def Initialize(self): self.xem = ok.FrontPanel() ChimeraControls.InitializeChimera(self.ChimeraSettings, self.xem) def ZeroCurrent(self): measI = np.mean(self.RecordTrace()) self.ChimeraSettings.SETUP_pAoffset = self.ChimeraSettings.SETUP_pAoffset - measI * 1E-9 print('New current offset: {} '.format(Amp.format_data(self.ChimeraSettings.SETUP_pAoffset))) self.ResetBuffer() self.ChimeraSettings.saveStoredValues() def EventDetection(self, useRaw=False, Threshold=None, DwellTime=None, upwardevents=None): if Threshold is None: Threshold = self.Threshold if DwellTime is None: DwellTime = self.DwellTime if upwardevents is None: upwardevents = self.upwardevents if useRaw: inputtrace = self.readvalues DwellTimedp = DwellTime * 1e-6 * self.ChimeraSettings.ADCSAMPLERATE else: inputtrace = self.displaybuffer DwellTimedp = DwellTime * 1e-6 * self.ChimeraSettings.ADCSAMPLERATE/self.displaysubsample ndown = functions.EventDetection(inputtrace, Threshold, DwellTimedp) if upwardevents: nup = functions.EventDetection(inputtrace*-1, Threshold, DwellTime) if ndown is False: totalevents = nup elif nup is False: totalevents = ndown else: totalevents = ndown + nup return totalevents else: return ndown 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('IV_', self.experimentName, nowstr) savingFolder = os.path.join(savingFolder, 'IV') + if not os.path.exists(savingFolder): + os.makedirs(savingFolder) else: savingName = '{}{}'.format(self.experimentName, nowstr) self.savename = os.path.join(savingFolder, savingName) print('Savename: ' + self.savename) def SetExperimentName(self, dataFolder=None, experimentName=None): if dataFolder is None: newDataFolder = self.DataFolder else: newDataFolder = os.path.join(self.DataFolder, dataFolder) if experimentName is not None: self.SetSavingFolder(newDataFolder, experimentName=experimentName) else: self.SetSavingFolder(newDataFolder) 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() self.RecordTrace(); print('done') 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 SetZeroVoltage(self, voltage): self.ChimeraSettings.voltageOffset = voltage self.ChimeraSettings.saveStoredValues() def ZeroVoltage(self): Vdelta = 0.02 print('Measuring at {} ...'.format(Volt.format_data(Vdelta)), end='') self.SetVoltage(Vdelta, write=False) time.sleep(1) self.ResetBuffer() measI1 = np.mean(self.RecordTrace()) print('{}'.format(Amp.format_data(measI1*1e-9))) print('Measuring at {} ...'.format(Volt.format_data(0)), end='') self.SetVoltage(0, write=False) time.sleep(1) self.ResetBuffer() measI2 = np.mean(self.RecordTrace()) print('{}'.format(Amp.format_data(measI2*1e-9))) 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))) self.SetVoltage(0) self.ChimeraSettings.saveStoredValues() def SetVoltage(self, sb, write=True): self.newbiasvalue = sb #self.ResetBuffer() if write: now = datetime.datetime.now() time_string = now.time().strftime("%H:%M:%S") print(time_string + ', {}'.format(Volt.format_data(sb))) self.SaveVoltage() self.file = None 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() self.displaybuffer, self.displaySamplerate = \ 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) if not os.path.isfile(fullpath): csv_headers = ['Date', 'Time', 'Voltage'] with open(fullpath, 'w', newline='') as new_data_file: datawriter = csv.writer(new_data_file) datawriter.writerow(csv_headers) # Create date and time string now = datetime.datetime.now() date_string = now.date().strftime("%Y-%m-%d") time_string = now.time().strftime("%H:%M:%S") # Convert to data data = [date_string, time_string, str(self.newbiasvalue)] with open(fullpath, 'a', newline='') as csvfile: writeline = csv.writer(csvfile) writeline.writerow(data) def WriteRawData(self, saveVoltages=False): if self.saveDownsampled: if saveVoltages: voltages = np.ones(len(self.displaybuffer)) * self.newbiasvalue * 1000 final = np.vstack((self.displaybuffer, voltages)) self.file.write((final.copy(order='C'))) else: 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.displaysubsample else: samplerate = self.ChimeraSettings.ADCSAMPLERATE mdict = {'DisplayBuffer': len(self.displaybuffer), 'ADCSAMPLERATE': samplerate, 'mytimestamp': str(datetime.datetime.today()), 'bias2value': self.newbiasvalue, 'SETUP_ADCVREF': self.ChimeraSettings.ADCVREF, 'SETUP_ADCSAMPLERATE': self.ChimeraSettings.ADCSAMPLERATE, 'SETUP_displaysubsample': self.displaysubsample, '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.voltageOffset, 'SETUP_biasvoltage' : self.newbiasvalue} io.savemat(self.savename + '.mat', mdict, True, '5', False, False, 'row') def SaveData(self, close=True, saveVoltages=False): if self.file is not None and self.ADClogsize < self.ChimeraSettings.MAXLOGBYTES: if not self.file.closed: self.WriteRawData(saveVoltages) else: self.file = open(self.savename + '.log', 'ab') self.WriteRawData(saveVoltages) if close: self.file.close() else: if self.file is not None and not self.file.closed: self.file.close() # Create new filename self.CreateSavename() # open file and reset logsize self.file = open(self.savename + '.log', 'ab') self.ADClogsize = 0 # Write summary .mat file and write raw .log file self.writeSummary() self.WriteRawData(saveVoltages) if close: self.file.close() -def MakeIV(Chimera): +def MakeIV(Chimera,plotting=True): MaxV_IV = Chimera.MaxV_IV StepIV = Chimera.StepIV timeIV = Chimera.timeIV allVoltages = functions.MakeAllVoltagesForIV(StepIV, MaxV_IV) # Set Chimera functions: oldlowpass = Chimera.lowpass olddisplaysubsample = Chimera.displaysubsample oldsaveDownsampled = Chimera.saveDownsampled Chimera.saveDownsampled = True Chimera.lowpass = 2e3 Chimera.displaysubsample = 100 Chimera.CreateSavename(IV=True) + if plotting: + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5)) + ax1.set_xlabel('time (s)') + ax1.set_ylabel('current (nA)') + ax1.set_xlim([0, timeIV]) - fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5)) - ax1.set_xlabel('time (s)') - ax1.set_ylabel('current (nA)') - ax1.set_xlim([0, timeIV]) - - ax2.set_xlabel('Voltage (V)') - ax2.set_ylabel('current (nA)') - ax2.set_title('IV-plot') - ax2.set_xlim([-MaxV_IV, MaxV_IV]) + ax2.set_xlabel('Voltage (V)') + ax2.set_ylabel('current (nA)') + ax2.set_title('IV-plot') + ax2.set_xlim([-MaxV_IV, MaxV_IV]) plt.tight_layout() currents = [] voltagesScanned = [] # Loop over all voltages for i in range(len(allVoltages)): # Set voltage value currentVoltage = allVoltages[i] voltagesScanned.append(currentVoltage) Chimera.SetVoltage(currentVoltage, write=False) - ax1.set_title('Live current at {}'.format(Volt.format_data(currentVoltage))) + if plotting: + ax1.set_title('Live current at {}'.format(Volt.format_data(currentVoltage))) t_end = time.time() + timeIV tVal = [] yVal = [] # Record trace for timeIV seconds while time.time() < t_end: trace = Chimera.RecordTrace() Chimera.SaveData(saveVoltages=True) tVal.append(time.time() - t_end + timeIV) yVal.append(np.mean(trace)) # Live plot recording data - if ax1.lines: - for line in ax1.lines: - line.set_xdata(tVal) - line.set_ydata(yVal) - else: - ax1.plot(tVal, yVal) - plt.show(block=True) - ax1.set_ylim([min(yVal) - 0.1 * abs(min(yVal)), max(yVal) + 0.1 * abs(max(yVal))]) - fig.canvas.draw() - fig.canvas.flush_events() + if plotting: + if ax1.lines: + for line in ax1.lines: + line.set_xdata(tVal) + line.set_ydata(yVal) + else: + ax1.plot(tVal, yVal) + plt.show(block=True) + ax1.set_ylim([min(yVal) - 0.1 * abs(min(yVal)), max(yVal) + 0.1 * abs(max(yVal))]) + fig.canvas.draw() + fig.canvas.flush_events() # Make list for plotting live plotting basic I-V currents.append(yVal[-1]) voltagesScanned, currents = (list(t) for t in zip(*sorted(zip(voltagesScanned, currents)))) # Live plotting I-V - if ax2.lines: - for line in ax2.lines: - line.set_xdata(voltagesScanned) - line.set_ydata(currents) - else: - ax2.plot(voltagesScanned, currents) - plt.show(block=True) - ax2.set_ylim([min(currents) - 0.1 * abs(min(currents)), max(currents) + 0.1 * abs(max(currents))]) - fig.canvas.draw() - fig.canvas.flush_events() + if plotting: + if ax2.lines: + for line in ax2.lines: + line.set_xdata(voltagesScanned) + line.set_ydata(currents) + else: + ax2.plot(voltagesScanned, currents) + plt.show(block=True) + ax2.set_ylim([min(currents) - 0.1 * abs(min(currents)), max(currents) + 0.1 * abs(max(currents))]) + fig.canvas.draw() + fig.canvas.flush_events() # Return filename filename = Chimera.savename + '.log' # Reset parameters Chimera.lowpass = oldlowpass Chimera.displaysubsample = olddisplaysubsample Chimera.saveDownsampled = oldsaveDownsampled return filename 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 + '\n{}'.format(Volt.format_data(self.Chimera.newbiasvalue))) self.buffertext = plt.figtext(0.95, 0.5, '') self.samplerate = Chimera.ChimeraSettings.ADCSAMPLERATE/Chimera.displaysubsample self.buffercheck = 5 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_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() def Writebuffer(self, out): 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)