SSL ground data plane: telemetry sources, protocol bridges, and telecommand for simulated and real vehicles
Project description
ssl_link
The SSL ground data plane — one interface for getting robot data (from a log, a running simulation, or a real vehicle over telemetry) and one interface for sending commands back. Simulated and real vehicles look identical to everything downstream.
The link is the channel between your vehicles (simulated in
ssl_simulator, or flying over pprzlink/Ivy) and your ground applications
(the ssl_vista viewer, GCS tools, analysis notebooks) — telemetry
down, telecommand up. It owns the canonical log format, so every producer writes files that
every consumer reads.
Everything in ssl_link is built from two contracts. Learn these and the rest is adapters.
1. A frame is (time, {name: array}). A snapshot of the world: named, entity-indexed arrays
(p is (N, 3), R is (N, 3, 3), …) at one instant. This is exactly what
ssl_simulator.Engine hands to its sink, what goes on the wire, and what a telemetry parser
produces. It is the same vocabulary of components the simulator uses. So a plotter that reads
"p" doesn't care whether the number came from a solver an hour ago or a radio a millisecond ago.
2. Data flows through a DataSource; commands flow through a Commander.
┌─────────────── ssl_link ───────────────┐
producers │ in: DataSource out: Commander │ consumers
───────── │ ┌──────────────┐ ┌────────────────┐ │ ─────────
Engine(sink=…) ───────►│ │ StreamSource │ │ SimCommander │◄─┼── vista / GCS panels
udp datagrams ────────►│ │ UdpSource │ │ IvyCommander │ │ analysis notebooks
pprzlink/Ivy ─────────►│ │ IvyFeed │ │ │──┼──► real vehicles (Ivy)
a .csv/.npz/.h5 log ──►│ │ LoggedSource │ └────────────────┘ │
│ └──────────────┘ │
│ persistence: DataLogger / save_sim / load_sim (the log standard)
└────────────────────────────────────────┘
A DataSource is a plain Mapping (source["p"] gives (T, N, 3)) plus liveness
(is_live, revision) and frame access (frame(idx), time(idx), history(...)). A viewer
binds to a source and never knows which subclass it got. A Commander exposes the telecommand
surface (setting, move_waypoint, jump_to_block) — a GCS panel calls it and never knows
whether the target is simulated or flying.
Installation
pip install ssl_link
The core (DataSource, the wire format, persistence for csv/npz) needs only numpy + msgpack.
Extras add capability:
| Extra | Adds | Needed for |
|---|---|---|
ssl_link[hdf5] |
h5py |
reading/writing .h5 log files |
ssl_link[pprz] |
ivy-python |
the Ivy bridges & IvyCommander (real Paparazzi vehicles) |
ssl_link[all] |
both of the above |
Note.
ssl_linkdoes not depend onssl_simulator. the data plane is producer-agnostic. The relationship is the other way round:ssl_simulatordepends onssl_linkfor the log format, so simulated and real runs share one on-disk standard. Installssl_simulatorseparately when you want the simulator as a data producer.
Tutorial
1. A source is a live, seekable view of frames
StreamSource is the base live buffer: push frames in, read them back like a finished run.
import numpy as np
from ssl_link import StreamSource
source = StreamSource(capacity=1000) # bounded ring: keeps the last 1000 frames
for k in range(5):
source.push(k * 0.1, {"p": np.full((3, 2), float(k))}) # (time, frame)
source["p"].shape # (5, 3, 2) — stacked like a logged run
source.n_frames # 5
source.frame(-1)["p"] # the latest snapshot: (3, 2)
source.is_live # True
It's bounded on purpose, a telemetry link runs for hours, so an unbounded buffer isn't an option. It rejects a changing set of component names mid-stream, so ragged data can't creep in.
2. Live-stream a running simulation (needs ssl_simulator)
StreamSource.push has exactly the signature of ssl_simulator.Engine's streaming sink — so
connecting a running sim to a live source is one argument, no glue:
from ssl_simulator import Engine
# ... build a `world` (see ssl_simulator) ...
stream = StreamSource(capacity=2000)
Engine(time_step=0.01, log_time_step=0.1, sink=stream.push).run(world, duration=60.0)
# `stream` fills as the engine runs; hand it to a viewer to watch live.
3. Cross a process boundary with UDP
udp_sink is a sink that publishes each frame as one msgpack datagram; UdpSource receives them
on a background thread and pushes them into itself. Producer and consumer can be different
terminals or machines.
# producer process:
from ssl_link.transports import udp_sink
Engine(sink=udp_sink(host="127.0.0.1", port=4700)).run(world, 60.0)
# consumer process (a viewer, a recorder, …):
from ssl_link.sources import UdpSource
with UdpSource(port=4700, capacity=2000).start() as source:
... # `source` is a DataSource that fills as datagrams arrive
A UDP-streamed run is byte-for-byte the same as the logged replay of the same run — the wire adds nothing but distance.
4. Persist to the canonical log format
The formats (.csv with a # SETTINGS: header, .npz, .h5) are the shared standard. Any
producer's output opens in any consumer:
from ssl_link import save_sim, load_sim
save_sim("run.csv", {"time": t, "p": p, "R": R}, settings={"gain": 0.5})
data, settings = load_sim("run.csv") # data["p"] -> (T, N, 3)
from ssl_link.sources import LoggedSource
source = LoggedSource.from_file("run.csv") # replay a log through the same DataSource interface
DataLogger is the streaming writer the simulator's engine uses (writes a row per tick); reach
for save_sim when you already have the whole run in memory.
5. WIP: Send commands — the same code for sim and reality
A Commander is the telecommand surface. Point a GCS panel at one and swap the implementation
underneath:
from ssl_link.command import SimCommander, IngestCommands
commander = SimCommander() # queues commands into a running world
world.add_system(IngestCommands(commander, { # the world declares what each command does
"setting": lambda w, ac_id, cmd: w["gain"].__setitem__(ac_id, cmd["value"]),
}))
# ... run the engine in a thread ...
commander.setting(ac_id=0, index=0, value=4.0) # mid-run: drone 0's gain becomes 4.0
Swap SimCommander for IvyCommander (needs ssl_link[pprz]) and the identical
commander.setting(...) call radios a real Paparazzi aircraft instead. That symmetry — one
command API, sim or reality — is the point of the layer.
6. WIP: Bridge a real Paparazzi swarm (needs ssl_link[pprz])
IvyTelemetrySink publishes a simulation onto the Ivy bus as per-aircraft pprzlink telemetry, so
existing ground tools see a simulated swarm exactly as they see real drones. IvyFeed does the
reverse — assembles incoming telemetry back into component frames for a viewer. Both are driven by
one mapping table (bridges.DEFAULT_TELEMETRY_MAP) so the two directions can't drift apart.
Scope.
IvyTelemetrySinkemulates the telemetry surface, not the firmware. Full PprzGCS visibility additionally requiresserver.mlwith the aircraft inconf.xml(the ALIVE/CONFIG handshake). Lab tools that subscribe telemetry directly byac_idneed none of that.
Package map
ssl_link/
sources/ DataSource (the contract) · LoggedSource · StreamSource · UdpSource
transports/ encode_frame/decode_frame (msgpack wire) · udp_sink (an Engine sink)
persistence/ DataLogger · save_sim · load_sim — the canonical log standard
bridges/ IvyTelemetrySink · IvyFeed · the message↔component mapping table [pprz]
command/ Commander (protocol) · SimCommander/IngestCommands · IvyCommander
Top-level shortcuts: from ssl_link import DataSource, LoggedSource, StreamSource, DataLogger, load_sim, save_sim.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ssl_link-0.1.0.tar.gz.
File metadata
- Download URL: ssl_link-0.1.0.tar.gz
- Upload date:
- Size: 26.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48acbd131db3289a1141268ae4daec806f7af6679c19f5f7bc7d9eda689c9ffa
|
|
| MD5 |
fa68d00280e9eebffb758f0115ffec2d
|
|
| BLAKE2b-256 |
474ffddda36271076eef241b6227bf077a766ca3e2cbb3016d051cf3b080589f
|
File details
Details for the file ssl_link-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ssl_link-0.1.0-py3-none-any.whl
- Upload date:
- Size: 33.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
128b95d57e9e4a13b042ea7ea2b83f1f692779564b93653779a0158fb1a71bf4
|
|
| MD5 |
6603f116f5e006ed9f30535485b6c205
|
|
| BLAKE2b-256 |
5a896fa3e507190b9a3ed4e03f60d8da474266c96f0ced5269627315b33b3443
|