class Light(object): OFF,ON = 0,1 def __init__(self, port='ao0', saver=None): self.port = port self.saver = saver self.daq = DAQ(DAQ.ANALOG_OUT, ports=[self.port]) self.trig = Trigger(5.0, dtype=np.float64) self.end_trig = Trigger(0.0, dtype=np.float64) self.is_on = False def on(self): if self.is_on: return self.daq.trigger(self.trig, clear=False) if self.saver: self.saver.write('light', [self.ON]) self.is_on = True def off(self): if not self.is_on: return self.daq.trigger(self.end_trig, clear=False) if self.saver: self.saver.write('light', [self.OFF]) self.is_on = False def end(self): self.daq.release()
class AnalogReader(object): READ_BUF_SIZE = 10 ACCUM_SIZE = 1000 def __init__( self, ports=["ai0", "ai1", "ai5", "ai6"], runtime_ports=[0, 1], saver=None, lick_thresh=6.0, holding_thresh=1.0 ): # IMPORTANT: NUMBER OF PORTS MUST MATCH VALUE IN EXPTS/DTYPES.PY self.ports = ports self.runtime_ports = runtime_ports # to be used in accumulator and lick/holding variable self.saver = saver self.thresh = lick_thresh self.daq = DAQ(DAQ.ANALOG_IN, ports=self.ports, read_buffer_size=self.READ_BUF_SIZE) self.holding_thresh = int(holding_thresh * self.daq.sample_rate) # runtime vars self.licked_ = np.zeros(len(self.runtime_ports)) self.accum = np.zeros((len(self.runtime_ports), self.ACCUM_SIZE)) self.holding = np.array([False, False]) self.lock = threading.Lock() self.accum_lock = threading.Lock() self.SAVING = False self.start() def start(self): # begin reading self.on = True th = threading.Thread(target=self.continuous_read) th.start() def get_accum(self): with self.accum_lock: return self.accum.copy() def continuous_read(self): while self.on: # read new data with self.accum_lock: self.accum = np.roll(self.accum, -self.READ_BUF_SIZE, axis=1) newdat_ts, newdat = ( self.daq.get() ) # important that "get" function locks thread, otherwise update line below would repeat logic on identical data assert newdat.shape == (len(self.ports), self.READ_BUF_SIZE) self.accum[:, -self.READ_BUF_SIZE :] = newdat[self.runtime_ports, :] # update logic with self.lock: self.licked_ += np.any( newdat[self.runtime_ports, :] >= self.thresh, axis=1 ) # is this really picking up the number of licks? no, it really represents the number of times this class queried and received a "at least 1 lick" response self.holding = np.all(self.accum[:, -self.holding_thresh :] > self.thresh, axis=1) if not self.on: # b/c session can end b/t start of this loop iteration and this point break if self.saver and self.SAVING: self.saver.write("analogreader", newdat, ts=newdat_ts) self.release() def begin_saving(self): self.SAVING = True def licked(self): with self.lock: ret = self.licked_.copy() self.licked_ = np.zeros(len(self.runtime_ports)) return ret def end(self): self.SAVING = False self.on = False def release(self): self.daq.release()