Skip to main content

Overview

udp_protocol.py defines binary packet formats for low-latency communication between the training system and CL1 neural interface. It uses struct-packed binary data for efficient serialization. Key Features:
  • Fixed-size binary packets for predictable performance
  • Little-endian encoding for consistency
  • Microsecond timestamps for latency measurement
  • Numpy integration for ML pipeline compatibility
Location: source/udp_protocol.py

Constants

Packet Sizes

integer
default:"8"
Number of neural channel groups used in the systemCorresponds to: encoding, move_forward, move_backward, move_left, move_right, turn_left, turn_right, attack
integer
default:"72"
Size of stimulation command packets in bytesStructure: 8 bytes timestamp + 32 bytes frequencies + 32 bytes amplitudes
integer
default:"40"
Size of spike data packets in bytesStructure: 8 bytes timestamp + 32 bytes spike counts
integer
default:"120"
Size of feedback command packets in bytesSee Feedback Packet Format for structure details.
integer
default:"64"
Maximum number of channels that can be specified in a feedback command

Feedback Types

integer
default:"0"
Feedback type code for interrupt commands (stop ongoing stimulation)
integer
default:"1"
Feedback type code for event-based feedback (kills, damage, pickups)
integer
default:"2"
Feedback type code for reward-based feedback (positive/negative reinforcement)

Packet Formats

Stimulation Command Packet

Direction: Training System → CL1 Device
Size: 72 bytes
Purpose: Send neural stimulation parameters
Struct Format: <Q + ffffffff + ffffffff (little-endian)

Spike Data Packet

Direction: CL1 Device → Training System
Size: 40 bytes
Purpose: Send spike counts from neural hardware
Struct Format: <Q + ffffffff (little-endian)

Feedback Packet Format

Direction: Training System → CL1 Device
Size: 120 bytes
Purpose: Send reward/event-based stimulation commands
Struct Format: <QBB64BIfIB32sx (little-endian)

Event Metadata Packet

Direction: Training System → CL1 Device
Size: Variable (JSON)
Purpose: Send training events for logging

Functions

Stimulation Command Functions

pack_stimulation_command(frequencies, amplitudes)

Pack stimulation parameters into a binary UDP packet. Parameters:
  • frequencies (np.ndarray): Shape (8,), frequency values in Hz
  • amplitudes (np.ndarray): Shape (8,), amplitude values in μA
Returns:
  • bytes: 72-byte binary packet ready to send via UDP
Raises:
  • ValueError: If arrays have incorrect shape
Example:

unpack_stimulation_command(packet)

Unpack a stimulation command packet. Parameters:
  • packet (bytes): 72-byte binary packet from UDP
Returns:
  • tuple: (timestamp, frequencies, amplitudes)
    • timestamp (int): Microseconds since epoch
    • frequencies (np.ndarray): Shape (8,), Hz values
    • amplitudes (np.ndarray): Shape (8,), μA values
Raises:
  • ValueError: If packet has incorrect size
Example:

Spike Data Functions

pack_spike_data(spike_counts)

Pack spike count data into a binary UDP packet. Parameters:
  • spike_counts (np.ndarray): Shape (8,), spike counts per channel group
Returns:
  • bytes: 40-byte binary packet ready to send via UDP
Raises:
  • ValueError: If array has incorrect shape
Example:

unpack_spike_data(packet)

Unpack a spike data packet. Parameters:
  • packet (bytes): 40-byte binary packet from UDP
Returns:
  • tuple: (timestamp, spike_counts)
    • timestamp (int): Microseconds since epoch
    • spike_counts (np.ndarray): Shape (8,), spike counts
Raises:
  • ValueError: If packet has incorrect size
Example:

Feedback Command Functions

pack_feedback_command(feedback_type, channels, frequency, amplitude, pulses, unpredictable=False, event_name="")

Pack feedback stimulation command into a binary UDP packet. Parameters:
  • feedback_type (str): Type of feedback - "interrupt", "event", or "reward"
  • channels (List[int]): List of channel numbers to stimulate (0-63)
  • frequency (int): Stimulation frequency in Hz
  • amplitude (float): Stimulation amplitude in μA
  • pulses (int): Number of pulses/bursts
  • unpredictable (bool): Whether this is unpredictable stimulation (default: False)
  • event_name (str): Name of the event for logging (default: "")
Returns:
  • bytes: 120-byte binary packet ready to send via UDP
Raises:
  • ValueError: If parameters are invalid (too many channels, invalid type, etc.)
Example:

unpack_feedback_command(packet)

Unpack a feedback command packet. Parameters:
  • packet (bytes): 120-byte binary packet from UDP
Returns:
  • tuple: (timestamp, feedback_type, channels, frequency, amplitude, pulses, unpredictable, event_name)
    • timestamp (int): Microseconds since epoch
    • feedback_type (str): "interrupt", "event", or "reward"
    • channels (List[int]): Channel numbers (0xFF padding removed)
    • frequency (int): Hz
    • amplitude (float): μA
    • pulses (int): Number of pulses
    • unpredictable (bool): Unpredictable flag
    • event_name (str): Event name (null padding removed)
Raises:
  • ValueError: If packet has incorrect size
Example:

Event Metadata Functions

pack_event_metadata(event_type, data)

Pack event metadata into a UDP packet. Parameters:
  • event_type (str): Type of event - "episode_end", "checkpoint", "training_complete", etc.
  • data (dict): Dictionary of event data
Returns:
  • bytes: Variable-length binary packet with JSON payload
Example:

unpack_event_metadata(packet)

Unpack event metadata packet. Parameters:
  • packet (bytes): Binary packet from UDP
Returns:
  • tuple: (timestamp, event_type, data)
    • timestamp (int): Microseconds since epoch
    • event_type (str): Event type
    • data (dict): Event data
Raises:
  • ValueError: If packet is too small or JSON is invalid
Example:

Utility Functions

get_latency_ms(packet_timestamp)

Calculate network latency from packet timestamp to now. Parameters:
  • packet_timestamp (int): Timestamp from packet (microseconds since epoch)
Returns:
  • float: Latency in milliseconds
Example:

Usage Examples

Training System - Send Stimulation

CL1 Device - Receive Stimulation

CL1 Device - Send Spikes

Training System - Receive Spikes

Send Event-Based Feedback

Performance Considerations

Packet Size vs Latency

Binary vs JSON Trade-offs

Binary (Stimulation/Spike/Feedback):
  • ✅ Fixed size (predictable)
  • ✅ Fast serialization (~1-5 μs)
  • ✅ Low network overhead
  • ❌ Less human-readable
  • ❌ Harder to debug
JSON (Event Metadata):
  • ✅ Human-readable
  • ✅ Flexible schema
  • ✅ Easy debugging
  • ❌ Variable size
  • ❌ Slower serialization (~50-100 μs)
  • ❌ Higher network overhead

Optimization Tips

  1. Reuse numpy arrays instead of creating new ones every packet
  2. Cache struct.pack format strings (already done in module)
  3. Use non-blocking sockets to prevent loop stalling
  4. Batch event metadata when possible (send multiple episodes in one packet)
  5. Monitor latency with get_latency_ms() to detect network issues

Testing

The module includes a test suite when run directly:
Output:

See Also