Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Receiving measurements

Every rig publishes every frame’s measurement, as soon as it is tracked, in two formats at once:

OutputTransportContentUse it for
Kinefly-compatible ZMQZMQ PUB on tcp://*:9871Left, right and L − R in radians, frame number, timeExisting Kinefly subscribers; closed-loop control
Full measurementsUDP JSON to 127.0.0.1:5601, and optionally a JSONL fileEverything: every part, every edge, strengths, timingAnalysis, logging, custom controllers

Neither output ever slows tracking down. Both can be turned off, and their addresses changed, in Settings → Instance / output or the config’s [output] table.

Kinefly-compatible ZMQ

Each rig binds a ZMQ PUB socket (output.zmq_bind, default tcp://*:9871; the second rig 9872). Subscribe to everything (b"") and receive one UTF-8 JSON frame per camera frame, byte for byte as the Kinefly Docker bridge sends it:

{"timestamp_unix": 1700000000.125, "pitch": 0.0, "y": -0.5, "x": 0.25, "yaw": 0.75, "z": 0.0, "roll": 0.0, "frame_number": 77}
FieldMeaning
xLeft wing angle, radians
yRight wing angle, radians
yawx − y: L − R, the steering signal
z, pitch, rollAlways 0.0, as in the bridge
frame_numberThe source’s frame sequence number
timestamp_unixWhen the frame arrived, seconds since 1970

A missing angle is sent as 0.0, as the bridge did. Use the full measurements when you need to tell a missing angle from a real zero.

Rekkefly replaces the bridge: don’t run both on the same port. The publisher speaks the ZeroMQ wire protocol the way libzmq does, ZMTP 3.0, 2.0 or 1.0 as the subscriber needs, so every subscriber that worked with the bridge works unchanged: pyzmq, libzmq, PlotJuggler, and NetMQ in Unity, including the older NetMQ releases (3.x and 4.0.0.x) that only speak ZMTP 2.0. ZMQ PUB/SUB drops messages sent before a subscriber connects, and never blocks tracking: like libzmq, a subscriber more than 1,000 messages behind misses messages rather than slowing the rig down.

A receiver in Python (python3 -m pip install pyzmq):

import json, zmq

socket = zmq.Context().socket(zmq.SUB)
socket.connect("tcp://127.0.0.1:9871")      # the rig's computer and port
socket.setsockopt(zmq.SUBSCRIBE, b"")
while True:
    m = json.loads(socket.recv_string())
    print(m["frame_number"], m["x"], m["y"], m["yaw"])

Ready-made versions ship in examples/:

python3 examples/receive_zmq.py --port 9871
python3 examples/live_plot.py --port 9871      # live plot at http://127.0.0.1:8765

Any language with a ZMQ library works the same way: connect a SUB socket, subscribe to everything and parse each message as JSON.

To receive on another computer, connect to tcp://<rig computer>:9871 and allow that port through the rig computer’s firewall. To keep the publisher private to one computer, set zmq_bind = "tcp://127.0.0.1:9871".

Full measurements over UDP

Every frame is also sent as one JSON datagram from output.bind (this rig’s own sending port, 127.0.0.1:5501) to output.target (your receiver, 127.0.0.1:5601). UDP needs no library and no connection, and a receiver can start and stop at any time. A receiver using only Python’s standard library:

import json, socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("127.0.0.1", 5601))
while True:
    m = json.loads(sock.recvfrom(65535)[0])
    print(m["sequence"], m["left"]["angle_rad"], m["right"]["angle_rad"], m["left_minus_right_rad"])

or python3 examples/receive_udp.py --port 5601.

Each datagram is a full measurement:

{"schema_version":1,"instance":"rig-a","sequence":0,"timestamp_ns":1791859983969667394,
 "elapsed_ns":0,"width":640,"height":480,"capture_fps":30.0,"processing_us":342.7,
 "background_subtracted":false,
 "left":{"enabled":true,"angle_rad":-0.3708,"image_angle_rad":-0.3819,
         "angles_rad":[-0.3708,0.2864],"gradients":[-0.0337,0.0051],"radius_px":null,
         "intensity":0.342,"strength":0.0337,"peak_gradient":0.0337,"coverage":1.0},
 "right":{…}, "head":{…}, "abdomen":{…},
 "left_minus_right_rad":-0.0964,"left_plus_right_rad":-0.6452}

Missing or disabled angles are null, never zero or a stale value. Every field is described in Measurement format.

JSONL files

Set output.jsonl_path to also write every measurement to a file, one JSON object per line, in the UDP format. The path is relative to the config; an existing file is never overwritten. rekkefly track --jsonl writes the same format offline.

Timing

timestamp_ns and timestamp_unix are when the frame arrived at the computer (host time), not a hardware exposure timestamp. processing_us is how long tracking took for that frame; measurements are published as soon as it finishes.