def prepareTaskStart(self): ## check power before starting task. if self.ui.checkPowerCheck.isChecked(): power = self.dev.outputPower() ## request current power from laser valid = self.dev.checkPowerValidity(power) if power is None: raise HelpfulException( "The current laser power for '%s' is unknown." % self.dev.name) if not valid: powerStr = siFormat(power, suffix='W') raise HelpfulException( "The current laser power for '%s' (%s) is outside the expected range." % (self.dev.name(), powerStr))
def saveMA(self, fileName=None): if self.imgData is None: raise HelpfulException("There is no processed data to save.") if fileName is None: dh = self.getElement("File Loader").baseDir().name() self.fileDialog = FileDialog(None, "Save image data", dh, '*.ma') self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.fileDialog.show() self.fileDialog.fileSelected.connect(self.saveMA) return table = self.dbquery.table() x = table['xPos'].min() y = table['yPos'].min() #print "params:", self.imgData.dtype.names #print "shape:", self.imgData.shape #arr = MetaArray(self.currentData) ### need to format this with axes and info arr = MetaArray([self.imgData[p] for p in self.imgData.dtype.names], info=[ {'name':'vals', 'cols':[{'name':p} for p in self.imgData.dtype.names]}, {'name':'xPos', 'units':'m', 'values':np.arange(self.imgData.shape[0])*self.spacing+x}, {'name':'yPos', 'units':'m', 'values':np.arange(self.imgData.shape[1])*self.spacing+y}, {'spacing':self.spacing} ]) arr.write(fileName)
def generateTask(self, dh, params=None, progressDlg=None): #prof = Profiler("Generate Task: %s" % str(params)) ## Never put {} in the function signature if params is None: params = {} prot = {'protocol': self.protoStateGroup.state()} # Disable timeouts for these tasks because we don't know how long to wait # for external triggers. TODO: use the default timeout, but also allow devices # in the task to modify the default. prot['protocol']['timeout'] = None #prof.mark('protocol state') store = (dh is not None) prot['protocol']['storeData'] = store if store: if params != {}: name = '_'.join(map(lambda i: '%03d'%i, params.values())) #print "mkdir", name info = params.copy() info['dirType'] = 'Protocol' dh1 = dh.mkdir(name, info=info) #prof.mark('create storage dir') else: dh1 = dh prot['protocol']['storageDir'] = dh1 #prof.mark('selected storage dir.') prot['protocol']['name'] = self.currentTask.fileName for d in self.currentTask.devices: if self.currentTask.deviceEnabled(d): ## select out just the parameters needed for this device p = dict([(i[1], params[i]) for i in params.keys() if i[0] == d]) ## Ask the device to generate its task command if d not in self.docks: raise HelpfulException("The device '%s' currently has no dock loaded." % d, reasons=[ "This device name does not exist in the system's configuration", "There was an error when creating the device at program startup", ], tags={}, importance=8, docSections=['userGuide/modules/TaskRunner/loadingNonexistentDevices'] ) prot[d] = self.docks[d].widget().generateTask(p) #prof.mark("get task from %s" % d) #print prot['protocol']['storageDir'].name() if progressDlg is not None: progressDlg.setValue(progressDlg.value()+1) ## only do UI updates every 1 sec. now = ptime.time() if now - self.lastQtProcessTime > 1.0: self.lastQtProcessTime = now QtGui.QApplication.processEvents() if progressDlg.wasCanceled(): raise Exception("Target sequence computation canceled by user.") #prof.mark('done') return prot
def mapToScanner(self, x, y, laser, opticState=None): """Convert global coordinates to voltages required to set scan mirrors *laser* and *opticState* are used to look up the correct calibration data. If *opticState* is not given, then the current optical state is used instead. """ if opticState is None: opticState = self.getDeviceStateKey( ) ## this tells us about objectives, filters, etc cal = self.getCalibration(laser, opticState) if cal is None: raise HelpfulException( "The scanner device '%s' is not calibrated for this combination of laser and objective (%s, %s)" % (self.name(), laser, str(opticState))) ## map from global coordinates to parent parentPos = self.mapGlobalToParent((x, y)) if isinstance(parentPos, Qt.QPointF): x = parentPos.x() y = parentPos.y() else: x = parentPos[0] y = parentPos[1] ## map to voltages using calibration cal = cal['params'] x2 = x**2 y2 = y**2 x1 = cal[0][0] + cal[0][1] * x + cal[0][2] * y + cal[0][3] * x2 + cal[ 0][4] * y2 y1 = cal[1][0] + cal[1][1] * x + cal[1][2] * y + cal[1][3] * x2 + cal[ 1][4] * y2 #print "voltage:", x1, y1 return [x1, y1]
def pointSize(self): ## returns (calibrated spot size, requested display size) try: #camMod = self.cameraModule() #if camMod is None: #return (1,1) #cam = camMod.config['camDev'] laser = self.getLaser() cal = self.dev.getCalibration(laser) ss = cal['spot'][1] except: #logMsg("Could not find spot size from calibration.", msgType='error') ### This should turn into a HelpfulException. exc = sys.exc_info() raise HelpfulException("Could not find spot size from calibration. ", exc=exc, reasons=["Correct camera and/or laser device are not selected.", "There is no calibration file for selected camera and laser."], errId=1) if self.ui.sizeFromCalibrationRadio.isChecked(): displaySize = ss ## reconnecting before this to get around reload errors, breaks the disconnect #try: #self.ui.sizeSpin.valueChanged.disconnect(self.sizeSpinEdited) #except TypeError: #logExc("A TypeError was caught in ScannerTaskGui.pointSize(). It was probably caused by a reload.", msgType='status', importance=0) self.stateGroup.setState({'spotSize':ss}) #self.ui.sizeSpin.valueChanged.connect(self.sizeSpinEdited) self.displaySize[(laser, self.currentOpticState)] = None elif self.ui.sizeCustomRadio.isChecked(): displaySize = self.ui.sizeSpin.value() self.displaySize[(laser, self.currentOpticState)] = displaySize return (ss, displaySize)
def makeError2(self): try: print(y) except: t, exc, tb = sys.exc_info() raise HelpfulException(message='msg from makeError', exc=(t, exc, tb), reasons=["reason one", "reason 2"], docs=['what, you expect documentation?'])
def setWavelength(self, wl): """Set the laser's wavelength (if tunable). Arguments: wl: """ raise HelpfulException( "%s device does not support wavelength tuning." % str(self.name()), reasons=[ "Hardware doesn't support tuning.", "setWavelenth function is not reimplemented in subclass." ])
def addItem(self, itemType, state=None): if state is None: state = {} if state.get('name', None) is None: state['name'] = self.getNextItemName(itemType) try: ptSize, dispSize = self.pointSize() except HelpfulException as ex: exc = sys.exc_info() if ex.kwargs.get('errId', None) == 1: raise HelpfulException('Cannot add items: %s is not calibrated for %s.' %(str(self.ui.laserCombo.currentText()), self.currentOpticState), exc=exc) else: raise state['ptSize'] = dispSize cls = {'Grid': Grid, 'Point': TargetPoint, 'Occlusion': TargetOcclusion}[itemType] item = cls(**state) camMod = self.cameraModule() if camMod is None: raise HelpfulException("Cannot add control items until a camera module is available to display them.") return False item.opticState = self.currentOpticState self.items[item.name] = item pos = state.get('pos', None) ## if no position is given, the camera will automatically place the item in the middle fo the view camMod.ui.addItem(item, pos, [1, 1], 1000) self.positionCtrlGroup.addChild(item.parameters()) item.sigStateChanged.connect(self.itemChanged) item.parameters().sigValueChanged.connect(self.itemActivationChanged) self.itemChanged(item) self.storeConfiguration()
def storeDBScan(self, scan, storeEvents=True): """Store all data for a scan, using cached values if possible""" p = debug.Profiler("Photostim.storeDBScan", disabled=True) if storeEvents: self.clearDBScan(scan) with pg.BusyCursor(): #dh = scan.source() #print "Store scan:", scan.source().name() events = [] stats = [] spots = scan.spots() with pg.ProgressDialog("Preparing data for %s" % scan.name(), 0, len(spots) + 1) as dlg: ## collect events and stats from all spots in the scan for i in xrange(len(spots)): s = spots[i] fh = self.dataModel.getClampFile(s.data()) try: ev = scan.getEvents(fh)['events'] events.append(ev) except: print fh, scan.getEvents(fh) raise st = scan.getStats(s.data()) stats.append(st) dlg.setValue(i) if dlg.wasCanceled(): raise HelpfulException("Scan store canceled by user.", msgType='status') p.mark("Prepared data") dbui = self.getElement('Database') db = dbui.getDb() with db.transaction(): ## Store all events for this scan if storeEvents: events = [x for x in events if len(x) > 0] if len(events) > 0: ev = np.concatenate(events) p.mark("concatenate events") self.detector.storeToDB(ev) p.mark("stored all events") ## Store spot data self.storeStats(stats) p.mark("stored all stats") p.finish()
def storeStats(self, data): ## Store a list of dict records, one per spot. ## data: {'SourceFile': clamp file handle, 'xPos':, 'yPos':, ...other fields from stats flowchart...} ## parentDir: protocolSequence dir handle (or protocol for single spots) #print "Store stats:", fh ## If only one record was given, make it into a list of one record if isinstance(data, dict): data = [data] dbui = self.getElement('Database') identity = self.dbIdentity + '.sites' table = dbui.getTableName(identity) db = dbui.getDb() if db is None: raise Exception("No DB selected") ## determine the set of fields we expect to find in the table fields = db.describeData(data) ## override directory fields since describeData can't guess these for us fields['ProtocolDir'] = 'directory:Protocol' fields['ProtocolSequenceDir'] = 'directory:ProtocolSequence' with db.transaction(): ## Make sure target table exists and has correct columns, links to input file db.checkTable(table, owner=identity, columns=fields, create=True, addUnknownColumns=True, indexes=[['ProtocolDir'], ['ProtocolSequenceDir']]) # delete old for source in set([d['ProtocolDir'] for d in data]): #name = rec['SourceFile'] db.delete(table, where={'ProtocolDir': source}) # write new with pg.ProgressDialog("Storing spot stats...", 0, 100) as dlg: for n, nmax in db.iterInsert(table, data, chunkSize=30): dlg.setMaximum(nmax) dlg.setValue(n) if dlg.wasCanceled(): raise HelpfulException("Scan store canceled by user.", msgType='status')
def runSingle(self, store=True): if self.protoStateGroup.state()['loop']: self.loopEnabled = True # good time to collect garbage gc.collect() self.lastProtoTime = ptime.time() ## Disable all start buttons self.enableStartBtns(False) ## Set storage dir try: if store: currentDir = self.manager.getCurrentDir() name = self.currentTask.name() if name is None: name = 'protocol' info = self.taskInfo() info['dirType'] = 'Protocol' ## Create storage directory with all information about the task to be executed dh = currentDir.mkdir(name, autoIncrement=True, info=info) else: dh = None ## Tell devices to prepare for task start. for d in self.currentTask.devices: if self.currentTask.deviceEnabled(d): self.docks[d].widget().prepareTaskStart() ## Generate executable conf from task object prot = self.generateTask(dh) #print prot self.sigTaskSequenceStarted.emit({}) #print "runSingle: Starting taskThread.." self.taskThread.startTask(prot) #print "runSingle: taskThreadStarted" except: exc = sys.exc_info() self.enableStartBtns(True) self.loopEnabled = False #print "Error starting task. " raise HelpfulException("Error occurred while starting task", exc=exc)
def makeError1(self): try: self.makeError2() #print x except: t, exc, tb = sys.exc_info() #logExc(message="This button doesn't work", reasons='reason a, reason b', docs='documentation') #if isinstance(exc, HelpfulException): #exc.prependErr("Button doesn't work", (t,exc,tb), reasons = ["It's supposed to raise an error for testing purposes", "You're doing it wrong."]) #raise #else: raise HelpfulException( message='This button does not work.', exc=(t, exc, tb), reasons=[ "It's supposed to raise an error for testing purposes", "You're doing it wrong." ])
def setData(self, arr=None, xPos=None, yPos=None, numOfPostEvents=None): if arr is not None: self.checkArrayInput(arr) self.populateEventsCombo(arr) fields = arr.dtype.names if 'xPos' not in fields or 'yPos' not in fields: raise HelpfulException( "Array input to Spatial correlator needs to have the following fields: 'xPos', 'yPos'" ) elif arr is None: self.data = None return self.data = np.zeros(len(arr), dtype=arr.dtype.descr + [('prob', float)]) self.data[:] = arr if 'numOfPreEvents' in fields and 'PreRegionLen' in fields: self.calculateSpontRate() if 'PostRegionLen' in fields: self.ctrl.deltaTSpin.setValue(self.data['PostRegionLen'][0]) self.process()
def scan(self): """Scan over x and y ranges in a nPts x nPts grid, return the image recorded at each location.""" camera = str(self.ui.cameraCombo.currentText()) laser = str(self.ui.laserCombo.currentText()) ## Camera settings to use during scan camParams = self.dev.getCameraConfig(camera) duration = self.ui.scanDurationSpin.value() rate = 10000 nPts = int(rate * duration) sweeps = 20 #cameraTrigger = ones(nPts, dtype=byte) ##(cmdMin, cmdMax) = self.dev.config['commandLimits'] xRange = (self.ui.xMinSpin.value(), self.ui.xMaxSpin.value()) yRange = (self.ui.yMinSpin.value(), self.ui.yMaxSpin.value()) xDiff = xRange[1] - xRange[0] yDiff = yRange[1] - yRange[0] xCommand = np.fromfunction(lambda i: xRange[0] + ((xDiff * i * float(sweeps) / nPts) % xDiff), (nPts,), dtype=float) xCommand[-1] = 0.0 yCommand = np.empty((nPts,), dtype=float) start = 0 for i in range(sweeps): stop = start + (nPts / sweeps) yCommand[start:stop] = yRange[0] + yDiff * (float(i)/(sweeps-1)) start = stop yCommand[-1] = 0.0 daqName = self.dev.config['XAxis']['device'] ## Record 10 camera frames with the shutter closed #print "parameters:", camParams cmd = { 'protocol': {'duration': 0.0, 'timeout': 5.0}, camera: {'record': True, 'minFrames': 10, 'params': camParams, 'pushState': 'scanProt'}, #laser: {'Shutter': {'preset': 0, 'holding': 0}} } #print "\n\n====> Record background\n" task = acq4.Manager.getManager().createTask(cmd) task.execute() result = task.getResult() ## pull result, convert to ndarray float, take average over all frames background = result[camera].asArray().astype(float).mean(axis=0) #print "Background shape:", result[camera]['frames'].shape ## Record full scan. cmd = { 'protocol': {'duration': duration, 'timeout': duration+5.0}, camera: {'record': True, 'triggerProtocol': True, 'params': camParams, 'channels': { 'exposure': {'record': True}, }, 'popState': 'scanProt'}, #laser: {'shutter': {'preset': 0, 'holding': 0, 'command': np.ones(len(xCommand), dtype=byte)}}, laser: {'alignMode': True}, self.dev.name(): {'xCommand': xCommand, 'yCommand': yCommand}, daqName: {'numPts': nPts, 'rate': rate, 'triggerDevice': camera} } #print "\n\n====> Scan\n" task = acq4.Manager.getManager().createTask(cmd) task.execute(block=False) with pg.ProgressDialog("Calibrating scanner: Running scan protocol..", 0, 100) as dlg: while not task.isDone(): dlg.setValue(100.*task.runTime()/task.duration()) if dlg.wasCanceled(): task.abort() raise HelpfulException('Calibration canceled by user.', msgType='warning') time.sleep(0.2) result = task.getResult() frames = result[camera].asMetaArray() if frames._info[-1]['preciseTiming'] is not True: raise HelpfulException("Calibration could not accurately measure camera frame timing.", reasons=["The exposure signal from the camera was not recorded by the DAQ."]) #print "scan shape:", frames.shape #print "parameters:", camParams ## Generate a list of the scanner command values for each frame positions = [] for i in range(frames.shape[0]): t = frames.xvals('Time')[i] ind = int((t/duration) * nPts) if ind >= len(xCommand): break positions.append([xCommand[ind], yCommand[ind]]) if frames.ndim != 3 or frames.shape[0] < 5: raise Exception("Camera did not collect enough frames (data shape is %s)" % str(frames.shape)) if background.shape != frames.shape[1:]: raise Exception("Background measurement frame has different shape %s from scan frames %s" % (str(background.shape), str(frames.shape[1:]))) return (background, result[camera], positions)
def runCalibration(self): """The scanner calibration routine: 1) Measure background frame, then scan mirrors while collecting frames as fast as possible (self.scan()) 2) Locate spot in every frame using gaussian fit 3) Map image spot locations to coordinate system of Scanner device's parent 3) Do parabolic fit to determine mapping between voltage and position """ camera = str(self.ui.cameraCombo.currentText()) laser = str(self.ui.laserCombo.currentText()) blurRadius = 5 ## Do fast scan of entire allowed command range (background, cameraResult, positions) = self.scan() #self.calibrationResult = {'bg': background, 'frames': cameraResult, 'pos': positions} with pg.ProgressDialog("Calibrating scanner: Computing spot positions...", 0, 100) as dlg: dlg.show() dlg.raise_() # Not sure why this is needed here.. ## Forget first 2 frames since some cameras can't seem to get these right. frames = cameraResult.asArray() frames = frames[2:] positions = positions[2:] ## Do background subtraction ## take out half the data until it can do the calculation without having a MemoryError. finished = False gc.collect() while not finished: try: frames = frames.astype(np.float32) frames -= background.astype(np.float32) finished=True except MemoryError: frames = frames[::2,:,:] positions = positions[::2] finished = False ## Find a frame with a spot close to the center (within center 1/3) cx = frames.shape[1] / 3 cy = frames.shape[2] / 3 centerSlice = blur(frames[:, cx:cx*2, cy:cy*2], (0, 5, 5)).max(axis=1).max(axis=1) maxIndex = argmax(centerSlice) maxFrame = frames[maxIndex] dlg.setValue(5) ## Determine spot intensity and width mfBlur = blur(maxFrame, blurRadius) amp = mfBlur.max() - median(mfBlur) ## guess intensity of spot (x, y) = argwhere(mfBlur == mfBlur.max())[0] ## guess location of spot fit = fitGaussian2D(maxFrame, [amp, x, y, maxFrame.shape[0] / 10, 0.])[0] ## gaussian fit to locate spot exactly # convert sigma to full width at 1/e fit[3] = abs(2 * (2 ** 0.5) * fit[3]) ## sometimes the fit for width comes out negative. *shrug* someFrame = cameraResult.frames()[0] frameTransform = pg.SRTTransform(someFrame.globalTransform()) pixelSize = someFrame.info()['pixelSize'][0] spotAmplitude = fit[0] spotWidth = abs(fit[3] * pixelSize) size = self.spotSize(mfBlur) dlg.setValue(50) ## Determine location of spot within each frame, ## ignoring frames where the spot is too dim or too close to the frame edge spotLocations = [] globalSpotLocations = [] spotCommands = [] spotFrames = [] margin = fit[3] for i in range(len(positions)): dlg.setValue(50. + 50. * i / frames.shape[0]) if dlg.wasCanceled(): raise HelpfulException('Calibration canceled by user.', msgType='warning') frame = frames[i] fBlur = blur(frame.astype(np.float32), blurRadius) mx = fBlur.max() diff = mx - fBlur.min() ss = self.spotSize(fBlur) if ss < size * 0.6: #print "Ignoring spot:", ss continue #else: #print "Keeping spot:", ss (x, y) = argwhere(fBlur == mx)[0] # guess location of spot if x < margin or x > frame.shape[0] - margin: #print " ..skipping; too close to edge", x, y continue if y < margin or y > frame.shape[1] - margin: #print " ..skipping; too close to edge", x, y continue frame[x,y] = -1 ## mark location of peak in image ## convert pixel location to coordinate system of scanner's parent globalPos = frameTransform.map(pg.Point(x, y)) ## Map from frame pixel location to global coordinates localPos = self.dev.mapGlobalToParent(globalPos) ## map from global to parent coordinate system. This is the position we calibrate to. #print (x, y), (globalPos.x(), globalPos.y()), (localPos.x(), localPos.y()) spotLocations.append([localPos.x(), localPos.y()]) globalSpotLocations.append([globalPos.x(), globalPos.y()]) spotCommands.append(positions[i]) spotFrames.append(frame[newaxis]) ## sanity check on spot frame if len(spotFrames) == 0: self.ui.view.setImage(frames) raise HelpfulException('Calibration never detected laser spot! Looking for spots that are %f pixels wide.'% fit[3], reasons=['shutter is disabled', 'mirrors are disabled', 'objective is not clean', 'spot is not visible or not bright enough when shutter is open']) spotFrameMax = concatenate(spotFrames).max(axis=0) self.ui.view.setImage(spotFrameMax, transform=frameTransform) self.clearSpots() for sl in globalSpotLocations: #self.addSpot(sl, fit[3]*binning[0]) self.addSpot(sl, spotWidth) self.ui.view.autoRange() if len(spotFrames) < 10: raise HelpfulException('Calibration detected only %d frames with laser spot; need minimum of 10.' % len(spotFrames), reasons=['spot is too dim for camera sensitivity', 'objective is not clean', 'mirrors are scanning too quickly', 'mirror scanning region is not within the camera\'s view']) ## Fit all data to a map function mapParams = self.generateMap(array(spotLocations), array(spotCommands)) #print #print "Map parameters:", mapParams if spotWidth < 0: raise Exception() return (mapParams, (spotAmplitude, spotWidth))
def runOnce(self, params=None): # good time to collect garbage gc.collect() prof = Profiler("TaskRunner.TaskThread.runOnce", disabled=True, delayed=False) startTime = ptime.time() if params is None: params = {} ## Select correct command to execute cmd = self.task if params is not None: for p in params: cmd = cmd[p:params[p]] prof.mark('select command') ## Wait before starting if we've already run too recently while (self.lastRunTime is not None) and (ptime.time() < self.lastRunTime + cmd['protocol']['cycleTime']): with self.lock: if self.abortThread or self.stopThread: #print "Task run aborted by user" return time.sleep(1e-3) prof.mark('sleep') emitSig = True while True: with self.lock: if self.abortThread or self.stopThread: return pause = self.paused if not pause: break if emitSig: emitSig = False self.sigPaused.emit() time.sleep(10e-3) prof.mark('pause') if type(cmd) is not dict: print("========= TaskRunner.runOnce cmd: ==================") print(cmd) print("========= TaskRunner.runOnce params: ==================") print("Params:", params) print("===========================") raise Exception( "TaskRunner.runOnce failed to generate a proper command structure. Object type was '%s', should have been 'dict'." % type(cmd)) task = self.dm.createTask(cmd) prof.mark('create task') self.lastRunTime = ptime.time() try: with self.lock: self._currentTask = task task.execute(block=False) # record estimated end time endTime = time.time() + cmd['protocol']['duration'] self.sigTaskStarted.emit(params) prof.mark('execute') except: with self.lock: self._currentTask = None try: task.stop(abort=True) except: pass printExc("\nError starting task:") exc = sys.exc_info() raise HelpfulException("\nError starting task:", exc) prof.mark('start task') ### Do not put code outside of these try: blocks; may cause device lockup try: ## wait for finish, watch for abort requests while True: if task.isDone(): prof.mark('task done') break with self.lock: if self.abortThread: # should be taken care of in TaskThread.abort() # NO -- task.stop() is not thread-safe. task.stop(abort=True) return # adjust sleep time based on estimated time remaining in the task. sleep = np.clip((endTime - time.time()) * 0.5, 1e-3, 20e-3) time.sleep(sleep) result = task.getResult() except: ## Make sure the task is fully stopped if there was a failure at any point. #printExc("\nError during task execution:") print("\nStopping task..") task.stop(abort=True) print("") raise HelpfulException("\nError during task execution:", sys.exc_info()) finally: with self.lock: self._currentTask = None prof.mark('getResult') frame = {'params': params, 'cmd': cmd, 'result': result} self.sigNewFrame.emit(frame) prof.mark('emit newFrame') if self.stopThread: raise Exception('stop', result) ## Give everyone else a chance to catch up Qt.QThread.yieldCurrentThread() prof.mark('yield') prof.finish()
def getChannelCmds(self, cmd, rate): ### cmd is a dict and can contain 'powerWaveform' or 'switchWaveform' keys with the array as a value if 'switchWaveform' in cmd: cmdWaveform = cmd['switchWaveform'] vals = np.unique(cmd['switchWaveform']) if not self.hasPCell and len(vals) == 2 and ( 1 or 1.0 ) not in vals: ## check to make sure we can give the specified power. raise Exception( 'An analog power modulator is neccessary to get power values other than zero and one (full power). The following values (as percentages of full power) were requested: %s. This %s device does not have an analog power modulator.' % (str(vals), self.name())) elif 'powerWaveform' in cmd: cmdWaveform = cmd['powerWaveform'] else: raise Exception( 'Not sure how to generate channel commands for %s' % str(cmd)) nPts = len(cmdWaveform) daqCmd = {} #if self.dev.config.get('pCell', None) is not None: if self.hasPCell: ## convert power values using calibration data if 'switchWaveform' in cmd: with self.variableLock: if self.params['useExpectedPower']: power = self.params['expectedPower'] else: power = self.params['currentPower'] transmission = self.params['scopeTransmission'] if transmission is None: raise Exception( 'Power transmission has not been calibrated for "%s" with the current optics and wavelength.' % self.name()) #transmission = 0.1 powerCmd = cmd['switchWaveform'] * power * transmission else: powerCmd = cmd['powerWaveform'] daqCmd['pCell'] = self.getPCellWaveform(powerCmd, cmd) else: if len( np.unique(cmdWaveform) ) > 2: ## check to make sure command doesn't specify powers we can't do raise Exception( "%s device does not have an analog power modulator, so can only have a binary power command." % str(self.name())) if self.hasQSwitch: #if self.dev.config.get('qSwitch', None) is not None: qswitchCmd = np.zeros(nPts, dtype=np.byte) qswitchCmd[cmdWaveform > 1e-5] = 1 daqCmd['qSwitch'] = qswitchCmd if self.hasTriggerableShutter: shutterCmd = np.zeros(nPts, dtype=np.byte) delay = self.config['shutter'].get('delay', 0.0) shutterCmd[ cmdWaveform != 0] = 1 ## open shutter when we expect power ## open shutter a little before we expect power because it has a delay delayPts = int(delay * rate) a = np.argwhere(shutterCmd[1:] - shutterCmd[:-1] == 1) + 1 for i in a: start = i - delayPts if start < 0: print start, delayPts, i raise HelpfulException( "Shutter takes %g seconds to open. Power pulse cannot be started before then." % delay) shutterCmd[start:i + 1] = 1 daqCmd['shutter'] = shutterCmd return daqCmd
def checkArrayInput(self, arr): fields = arr.dtype.names if 'xPos' not in fields or 'yPos' not in fields or 'numOfPostEvents' not in fields: raise HelpfulException( "Array input needs to have the following fields: 'xPos', 'yPos', 'numOfPostEvents'. Current fields are: %s" % str(fields)) else: return True #@staticmethod #def bendelsSpatialCorrelationAlgorithm(data, radius, spontRate, timeWindow): #SpatialCorrelator.checkArrayInput(data) ## check that data has 'xPos', 'yPos' and 'numOfPostEvents' ### add 'prob' field to data array #if 'prob' not in data.dtype.names: #arr = np.zeros(len(data), dtype=data.dtype.descr + [('prob', float)]) #arr[:] = data #data = arr #else: #data['prob']=0 ### spatial correlation algorithm from : ### Bendels, MHK; Beed, P; Schmitz, D; Johenning, FW; and Leibold C. Etection of input sites in ### scanning photostimulation data based on spatial correlations. 2010. Journal of Neuroscience Methods. ### calculate probability of seeing a spontaneous event in time window #p = 1-np.exp(-spontRate*timeWindow) ### for each spot, calculate the probability of having the events in nearby spots occur randomly #for x in data: #spots = data[(np.sqrt((data['xPos']-x['xPos'])**2+(data['yPos']-x['yPos'])**2)) < radius] #nSpots = len(spots) #nEventSpots = len(spots[spots['numOfPostEvents'] > 0]) #prob = 0 #for j in range(nEventSpots, nSpots+1): #prob += ((p**j)*((1-p)**(nSpots-j))*math.factorial(nEventSpots))/(math.factorial(j)*math.factorial(nSpots-j)) ##j = arange(nEventSponts, nSpots+1) ##prob = (((p**j)*((1-p)**(nSpots-j))*np.factorial(nEventSpots))/(np.factorial(j)*np.factorial(nSpots-j))).sum() ## need a factorial function that works on arrays #x['prob'] = prob #return data #class SpatialOutline(pg.GraphicsObject): #def __init__(self, parent=None, pen=None, spots=None, radius=25e-6): #pg.GraphicsObject.__init__(self, parent) #if pen is None: #pen = (255, 255, 255) #self.setPen(pen) #self.path = Qt.QPainterPath() #self.spots = spots #self.radius = radius #if spots is not None: #self.makePath() #def setData(self, spots): #self.spots = spots #self.makePath() #self.update(self.boundingRect()) #def setRadius(self, radius): #self.radius = radius #self.makePath() #self.update(self.boundingRect()) #def setPen(self, pen): #self.pen = pg.mkPen(pen) #self.currentPen = self.pen #self.update() #def makePath(self): #if self.spots is None: #return #path = Qt.QPainterPath() #for s in self.spots: #path.addEllipse(s['xPos'], s['yPos'], self.radius, self.radius) ##pps = Qt.QPainterPathStroker() ##self.path = pps.createStroke(path) #self.path=path #def boundingRect(self): #if self.spots is None: #return Qt.QRectF() ##x = self.spots['xPos'].min() ##y = self.spots['yPos'].min() ##return Qt.QRectF(x,y , self.spots['xPos'].max()-x, self.spots['yPos'].max()-y) ##print "outline.boundingRect: ", self.path.boundingRect() #return self.path.boundingRect() #def paint(self, p, *args): #p.setRenderHint(Qt.QPainter.Antialiasing) ##path = self.shape() #p.setPen(self.currentPen) #p.drawPath(self.path) ##p.setPen(Qt.QPen(Qt.QColor(255,0,0))) ##p.drawPath(self.shape()) #p.setPen(Qt.QPen(Qt.QColor(0,0,255))) #p.drawRect(self.boundingRect())
def __init__(self, manager, name, config): Module.__init__(self, manager, name, config) self.lastProtoTime = None self.loopEnabled = False self.devListItems = {} self.docks = {} self.firstDock = None # all new docks should stack here self.analysisDocks = {} self.deleteState = 0 self.ui = Ui_MainWindow() self.win = Window(self) g = self.win.geometry() self.ui.setupUi(self.win) self.win.setGeometry(g) self.win.setStatusBar(StatusBar()) self.ui.protoDurationSpin.setOpts(dec=True, bounds=[1e-3, None], step=1, minStep=1e-3, suffix='s', siPrefix=True) self.ui.protoLeadTimeSpin.setOpts(dec=True, bounds=[0, None], step=1, minStep=10e-3, suffix='s', siPrefix=True) self.ui.protoCycleTimeSpin.setOpts(dec=True, bounds=[0, None], step=1, minStep=1e-3, suffix='s', siPrefix=True) self.ui.seqCycleTimeSpin.setOpts(dec=True, bounds=[0, None], step=1, minStep=1e-3, suffix='s', siPrefix=True) self.protoStateGroup = pg.WidgetGroup([ (self.ui.protoContinuousCheck, 'continuous'), (self.ui.protoDurationSpin, 'duration'), (self.ui.protoLeadTimeSpin, 'leadTime'), (self.ui.protoLoopCheck, 'loop'), (self.ui.protoCycleTimeSpin, 'loopCycleTime'), (self.ui.seqCycleTimeSpin, 'cycleTime'), (self.ui.seqRepetitionSpin, 'repetitions', 1), ]) try: try: taskDir = config['taskDir'] except KeyError: taskDir = self.manager.config[ 'protocolDir'] # for backward compatibility self.taskList = Loader(self, taskDir) except KeyError: raise HelpfulException( "Config is missing 'taskDir'; cannot load task list.") self.ui.LoaderDock.setWidget(self.taskList) self.currentTask = None ## pointer to current task object for m in analysisModules.MODULES: item = Qt.QListWidgetItem(m, self.ui.analysisList) item.setFlags(Qt.Qt.ItemIsSelectable | Qt.Qt.ItemIsEnabled | Qt.Qt.ItemIsUserCheckable) item.setCheckState(Qt.Qt.Unchecked) self.taskThread = TaskThread(self) self.newTask() self.ui.testSingleBtn.clicked.connect(self.testSingleClicked) self.ui.runTaskBtn.clicked.connect(self.runSingleClicked) self.ui.testSequenceBtn.clicked.connect(self.testSequence) self.ui.runSequenceBtn.clicked.connect(self.runSequenceClicked) self.ui.stopSingleBtn.clicked.connect(self.stopSingle) self.ui.stopSequenceBtn.clicked.connect(self.stopSequence) self.ui.pauseSequenceBtn.toggled.connect(self.pauseSequence) self.ui.deviceList.itemClicked.connect(self.deviceItemClicked) self.taskList.sigCurrentFileChanged.connect( self.fileChanged ) ## called if loaded task file is renamed or moved self.taskThread.finished.connect(self.taskThreadStopped) self.taskThread.sigNewFrame.connect(self.handleFrame) self.taskThread.sigPaused.connect(self.taskThreadPaused) self.taskThread.sigTaskStarted.connect(self.taskStarted) self.taskThread.sigExitFromError.connect(self.taskErrored) self.protoStateGroup.sigChanged.connect(self.protoGroupChanged) self.win.show() self.ui.sequenceParamList.itemChanged.connect(self.updateSeqReport) self.ui.analysisList.itemClicked.connect(self.analysisItemClicked)