Loading...

Stim

Optical Stimulation

Overview

Both reading data out and writing data into the brain are central for advanced applications in neural engineering. Synapse devices leverage the power and specificity of optogenetics to deliver targeted neural stimulation using Axon embedded thin film uLED technology.

The Axon 1N-1P family of multi-electrode arrays (MEAs) integrate the Nixel 512 and Pixel 16k chips into a single thin-film package for both recording neural activity and delivering spatially precise optical stimulation. This enables simultaneous stimulation and recording without the electrical artifacts typically seen with electrical stimulation approaches.

Optical stimulation is managed by the Pixel 16k chip, which received commands from the Synapse Optical Stimulation Node in the form of Optical Stimulation Frames. Each frame contains an array listing the activation state for each uLED, as well as frame ID, row dwell time, and other metadata.

The Pixel 16k uses a line scanning approach to uLED activation. This means that any number of uLEDs can be activated in a given row, but row scanning order is sequential and fixed. In other words, uLEDs in row 1 will always be activated before uLEDs in row 2, and all rows are scanned before executing the next frame. The frame period and dwell time for each row are controlled by frame_rate and duration_us.

  • frame_rate sets the maximum rate at which your Synapse device will receive frames, and is set using the kOpticalStimulation node settings in the Synapse JSON config file.
  • duration_us sets the row dwell time. By extension, this determines the total frame raster time, as well as the the LED activation duration and timing.

For example, if duration_us = 10_000 (10 ms), and frame array dimensions are 10x10, the overall frame raster time is 100 ms. Each row is scanned for 10ms, resulting in 10ms activation for all . Because each frame is processed row-by-row in descending order, the timing of individual uLED activations within the array is dictated by row. Any number of uLEDs can be activated simultaneously within a given row, but only one row can be active at a time. Given the parameters listed above, uLEDs in row one will be activated for 10 ms at frame onset. This is followed by activation of uLEDs in row two for the same duration, and so on until the frame has been fully rastered. The full frame raster take 100 ms in this example.

Below is an example configuration for the Optical Stimulation Node:

{
  "nodes": [
    {
      "type": "kOpticalStimulation", 
      "id": 1,
      "optical_stimulation": {
        "peripheral_id": 3, 
        "pixel_mask": [],
        "bit_width": 8,
        "frame_rate": 10,
        "gain": 1.0,
        "send_receipts": false
      }
    }
  ],
  "connections": []
}
  • peripheral_id identifies the peripheral optical stimulation device type.
  • pixel_mask is a software-applied mask that acts as an AND filter for the stim frame data. Any channel not included in the pixel mask will be set to zero.
  • frame_rate set the maximum frame receive rate for the Synapse device.
  • send_receipts are 1xN vectors sent by the FPGA in lock-step with neural data packets.

bit_width and gain are not currently used and can be left to their default values of 8 and 1.0, respectively.

Optical Stimulation Frame Format and Timing

Optical stimulation frames are protocol buffer (protobuf) generated objects with pre-defined fields and methods. Each frame contains information about the overall dimensions of the uLED array, frame ID, row dwell time in microseconds (duration_us), and the intensity value for each uLED. 8 LED intensity steps are available, and are scaled 0 - 1.0, with 0 being no illumination and 1 being maximum brightness.

Example Client Script

Typically, optical stim frames are generated using a python client script that also transmits each frame to your Synapse device over wifi using Taps. Each frame is then transmitted over USB to your Axon peripheral device, where it is executed row-by-row.

The example below generates and sends an optical stim frame to your Synapse device each time you press the enter button.

# =========================================
TARGET_ROW = 3      # Change this to any row (0-11)
TARGET_COL = 3      # Change this to any column (0-22)
DEVICE_IP = "$DEVICE_IP"  # Your device IP address
# =========================================

from synapse.client.taps import Tap
from synapse.api.nodes.optical_stimulation_pb2 import OpticalStimFrame
import time

# Tap Configuration
tap = Tap(DEVICE_IP)
res = tap.connect("optical_stimulation_1_input")
time.sleep(1)
if not res:
   print("Failed to connect to tap")
   exit(1)

# Frame Configuration
data = OpticalStimFrame()
data.frame_id = 1
data.rows = 12
data.columns = 23
data.duration_us = 50_000 # Row dwell time in microseconds.  

# Initialize all LEDs to OFF
data.intensity.extend([0.0] * (data.rows * data.columns))

# Calculate LED array index from row/column
led_index = TARGET_ROW * data.columns + TARGET_COL
data.intensity[led_index] = 1.0

# Send Optical Stim Command
while True:
   input(f"Press Enter to send frame with Row {TARGET_ROW}, Column {TARGET_COL} LED...")
   ret = tap.send(data.SerializeToString())

   # Increment frame_id & print result
   data.frame_id += 1 
   print(f"Sent frame {data.frame_id - 1}, LED at ({TARGET_ROW}, {TARGET_COL}) index {led_index} should be ON")