def __init__(self, sync_event, spike_listener, position_estimator, place_field_handler): """TODO: to be defined1. """ ThreadExtension.StoppableProcess.__init__(self) self._sync_event = sync_event self._spike_buffer = collections.deque(maxlen=self._SPIKE_BUFFER_SIZE) self._spike_histogram = collections.Counter() self._spike_buffer_connection = spike_listener.get_spike_buffer_connection( ) self._position_buffer_connection = position_estimator.get_position_buffer_connection( ) self._place_field_handler = place_field_handler self._position_access = Lock() self._spike_access = Lock() # TODO: This functionality should be moved to the parent class self._enable_synchrnoizer = Lock() self._is_disabled = Value("b", True) # Position data at the time ripple is triggered self._pos_x = -1 self._pos_y = -1 self._most_recent_speed = 0 self._most_recent_pos_timestamp = 0 self._serial_port = None try: self._serial_port = SerialPort.BiphasicPort() except Exception as err: logging.warning("Unable to open Serial port.") print(err) logging.info(self.CLASS_IDENTIFIER + "Started Ripple Synchronization thread.")
def getRippleStatistics(tetrodes, analysis_time=4, show_ripples=False, \ ripple_statistics=None, interrupt_ripples=False): """ Get ripple data statistics for a particular tetrode and a user defined time period. Added: 2019/02/19 Archit Gupta :tetrodes: Indices of tetrodes that should be used for collecting the statistics. :analysis_time: Amount of time (specified in seconds) for which the data should be analyzed to get ripple statistics. :show_ripple: Show ripple as they happen in real time. :ripple_statistics: Mean and STD for declaring something a sharp-wave ripple. :returns: Distribution of ripple power, ripple amplitude and frequency """ if show_ripples: plt.ion() if interrupt_ripples: ser = SerialPort.BiphasicPort() n_tetrodes = len(tetrodes) report_ripples = (ripple_statistics is not None) # Create a ripple filter (discrete butterworth filter with cutoff # frequencies set at Ripple LO and HI cutoffs.) ripple_filter = signal.butter(RiD.LFP_FILTER_ORDER, \ (RiD.RIPPLE_LO_FREQ, RiD.RIPPLE_HI_FREQ), \ btype='bandpass', analog=False, output='sos', \ fs=RiD.LFP_FREQUENCY) # Filter the contents of the signal frame by frame ripple_frame_filter = signal.sosfilt_zi(ripple_filter) # Tile it to take in all the tetrodes at once ripple_frame_filter = np.tile(np.reshape(ripple_frame_filter, \ (RiD.LFP_FILTER_ORDER, 1, 2)), (1, n_tetrodes, 1)) # Initialize a new client client = TrodesInterface.SGClient("RippleAnalyst") if (client.initialize() != 0): del client raise Exception("Could not initialize connection! Aborting.") # Access the LFP stream and create a buffer for trodes to fill LFP data into lfp_stream = client.subscribeLFPData( TrodesInterface.LFP_SUBSCRIPTION_ATTRIBUTE, tetrodes) lfp_stream.initialize() # LFP Sampling frequency TIMES desired analysis time period N_DATA_SAMPLES = int(analysis_time * RiD.LFP_FREQUENCY) # Each LFP frame (I think it is just a single time point) is returned in # lfp_frame_buffer. The entire timeseries is stored in raw_lfp_buffer. lfp_frame_buffer = lfp_stream.create_numpy_array() ripple_filtered_lfp = np.zeros((n_tetrodes, N_DATA_SAMPLES), dtype='float') raw_lfp_buffer = np.zeros((n_tetrodes, N_DATA_SAMPLES), dtype='float') ripple_power = np.zeros((n_tetrodes, N_DATA_SAMPLES), dtype='float') # Create a plot to look at the raw lfp data timestamps = np.linspace(0, analysis_time, N_DATA_SAMPLES) iter_idx = 0 prev_ripple = -1.0 prev_interrupt = -1.0 # Data to be logged for later use ripple_events = [] trodes_timestamps = [] wall_ripple_times = [] interrupt_events = [] if report_ripples: print('Using pre-recorded ripple statistics') print('Mean: %.2f' % ripple_statistics[0]) print('Std: %.2f' % ripple_statistics[1]) if show_ripples: interruption_fig = plt.figure() interruption_axes = plt.axes() plt.plot([], []) plt.grid(True) plt.ion() plt.show() wait_for_user_input = input("Press Enter to start!") start_time = 0.0 start_wall_time = time.perf_counter() interruption_iter = -1 is_first_ripple = True while (iter_idx < N_DATA_SAMPLES): n_lfp_frames = lfp_stream.available(0) for frame_idx in range(n_lfp_frames): # print("t__%.2f"%(float(iter_idx)/float(RiD.LFP_FREQUENCY))) t_stamp = lfp_stream.getData() trodes_time_stamp = client.latestTrodesTimestamp() raw_lfp_buffer[:, iter_idx] = lfp_frame_buffer[:] # If we have enough data to fill in a new filter buffer, filter the # new data if (iter_idx > RiD.RIPPLE_SMOOTHING_WINDOW) and ( iter_idx % RiD.LFP_FILTER_ORDER == 0): lfp_frame = raw_lfp_buffer[:, iter_idx - RiD.LFP_FILTER_ORDER:iter_idx] # print(lfp_frame) filtered_frame, ripple_frame_filter = signal.sosfilt(ripple_filter, \ lfp_frame, axis=1, zi=ripple_frame_filter) # print(filtered_frame) ripple_filtered_lfp[:, iter_idx - RiD. LFP_FILTER_ORDER:iter_idx] = filtered_frame # Averaging over a longer window to be able to pick out ripples effectively. # TODO: Ripple power is only being reported for each frame # right now: Filling out the same value for the entire frame. frame_ripple_power = np.sqrt(np.mean(np.power( \ ripple_filtered_lfp[:,iter_idx-RiD.RIPPLE_SMOOTHING_WINDOW:iter_idx], 2), axis=1)) ripple_power[:,iter_idx-RiD.LFP_FILTER_ORDER:iter_idx] = \ np.tile(np.reshape(frame_ripple_power, (n_tetrodes, 1)), (1, RiD.LFP_FILTER_ORDER)) if report_ripples: if is_first_ripple: is_first_ripple = False else: # Show the previous interruption after a sufficient time has elapsed if show_ripples: if (iter_idx == int( (prev_ripple + RiD.INTERRUPTION_WINDOW) * RiD.LFP_FREQUENCY)): data_begin_idx = int( max(0, iter_idx - 2 * RiD.INTERRUPTION_TPTS)) interruption_axes.clear() interruption_axes.plot(timestamps[data_begin_idx:iter_idx], raw_lfp_buffer[0, \ data_begin_idx:iter_idx]) interruption_axes.scatter(prev_ripple, 0, c="r") interruption_axes.set_ylim(-3000, 3000) plt.grid(True) plt.draw() plt.pause(0.001) # print(raw_lfp_buffer[0, data_begin_idx:iter_idx]) # If any of the tetrodes has a ripple, let's call it a ripple for now ripple_to_baseline_ratio = (frame_ripple_power[0] - ripple_statistics[0])/ \ ripple_statistics[1] if (ripple_to_baseline_ratio > RiD.RIPPLE_POWER_THRESHOLD): current_time = float(iter_idx) / float( RiD.LFP_FREQUENCY) if ((current_time - prev_ripple) > RiD.RIPPLE_REFRACTORY_PERIOD): prev_ripple = current_time current_wall_time = time.perf_counter( ) - start_wall_time time_lag = (current_wall_time - current_time) if interrupt_ripples: ser.sendBiphasicPulse() print( "Ripple @ %.2f, Real Time %.2f [Lag: %.2f], strength: %.1f" % (current_time, current_wall_time, time_lag, ripple_to_baseline_ratio)) trodes_timestamps.append(trodes_time_stamp) ripple_events.append(current_time) wall_ripple_times.append(current_wall_time) iter_idx += 1 if (iter_idx >= N_DATA_SAMPLES): break if client is not None: client.closeConnections() print("Collected raw LFP Data. Visualizing.") power_mean, power_std = Visualization.visualizeLFP(timestamps, raw_lfp_buffer, ripple_power, \ ripple_filtered_lfp, ripple_events, do_animation=False) if report_ripples: writeLogFile(trodes_timestamps, ripple_events, wall_ripple_times, interrupt_events) # Program exits with a segmentation fault! Can't help this. wait_for_user_input = input('Press ENTER to quit') return (power_mean, power_std)