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
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 DeviceSize: 72 bytes
Purpose: Send neural stimulation parameters
<Q + ffffffff + ffffffff (little-endian)
Spike Data Packet
Direction: CL1 Device → Training SystemSize: 40 bytes
Purpose: Send spike counts from neural hardware
<Q + ffffffff (little-endian)
Feedback Packet Format
Direction: Training System → CL1 DeviceSize: 120 bytes
Purpose: Send reward/event-based stimulation commands
<QBB64BIfIB32sx (little-endian)
Event Metadata Packet
Direction: Training System → CL1 DeviceSize: 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 Hzamplitudes(np.ndarray): Shape(8,), amplitude values in μA
bytes: 72-byte binary packet ready to send via UDP
ValueError: If arrays have incorrect shape
unpack_stimulation_command(packet)
Unpack a stimulation command packet.
Parameters:
packet(bytes): 72-byte binary packet from UDP
tuple:(timestamp, frequencies, amplitudes)timestamp(int): Microseconds since epochfrequencies(np.ndarray): Shape(8,), Hz valuesamplitudes(np.ndarray): Shape(8,), μA values
ValueError: If packet has incorrect size
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
bytes: 40-byte binary packet ready to send via UDP
ValueError: If array has incorrect shape
unpack_spike_data(packet)
Unpack a spike data packet.
Parameters:
packet(bytes): 40-byte binary packet from UDP
tuple:(timestamp, spike_counts)timestamp(int): Microseconds since epochspike_counts(np.ndarray): Shape(8,), spike counts
ValueError: If packet has incorrect size
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 Hzamplitude(float): Stimulation amplitude in μApulses(int): Number of pulses/burstsunpredictable(bool): Whether this is unpredictable stimulation (default: False)event_name(str): Name of the event for logging (default: "")
bytes: 120-byte binary packet ready to send via UDP
ValueError: If parameters are invalid (too many channels, invalid type, etc.)
unpack_feedback_command(packet)
Unpack a feedback command packet.
Parameters:
packet(bytes): 120-byte binary packet from UDP
tuple:(timestamp, feedback_type, channels, frequency, amplitude, pulses, unpredictable, event_name)timestamp(int): Microseconds since epochfeedback_type(str):"interrupt","event", or"reward"channels(List[int]): Channel numbers (0xFF padding removed)frequency(int): Hzamplitude(float): μApulses(int): Number of pulsesunpredictable(bool): Unpredictable flagevent_name(str): Event name (null padding removed)
ValueError: If packet has incorrect size
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
bytes: Variable-length binary packet with JSON payload
unpack_event_metadata(packet)
Unpack event metadata packet.
Parameters:
packet(bytes): Binary packet from UDP
tuple:(timestamp, event_type, data)timestamp(int): Microseconds since epochevent_type(str): Event typedata(dict): Event data
ValueError: If packet is too small or JSON is invalid
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)
float: Latency in milliseconds
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
- ✅ Human-readable
- ✅ Flexible schema
- ✅ Easy debugging
- ❌ Variable size
- ❌ Slower serialization (~50-100 μs)
- ❌ Higher network overhead
Optimization Tips
- Reuse numpy arrays instead of creating new ones every packet
- Cache struct.pack format strings (already done in module)
- Use non-blocking sockets to prevent loop stalling
- Batch event metadata when possible (send multiple episodes in one packet)
- Monitor latency with
get_latency_ms()to detect network issues
Testing
The module includes a test suite when run directly:See Also
- training_server.py - Uses UDP protocol for remote training
- cl1_neural_interface.py - Implements protocol on CL1 device
- ppo_doom.py - Direct CL1 training (no UDP)