Metadata-Version: 2.5
Name: poseproxy
Version: 0.0.2
Summary: Simple Pose Forwarder For Motion Capturing Systems
Author-email: Jonas Eschmann <jonas.eschmann@gmail.com>
License: MIT
Requires-Python: >=3.10
Requires-Dist: websockets<16,>=15.0.1
Provides-Extra: vicon
Requires-Dist: pyvicon-datastream==0.2.4; extra == 'vicon'
Description-Content-Type: text/markdown

# poseproxy

Streams one rigid body from any mocap system to a browser. Requires Python 3.10+.

Install from PyPI:

```sh
python3 -m venv .venv
. .venv/bin/activate
pip install poseproxy
poseproxy --demo
```

For Vicon:

```sh
pip install 'poseproxy[vicon]'
poseproxy.vicon --host 192.154.4.124 --list  # List current object names
poseproxy.vicon --host 192.154.4.124 --subject crazyflie | poseproxy
```

Vicon uses the root segment; override with `--segment NAME`. Server defaults:
`127.0.0.1:8765`; change with `--host`/`--port`. Stop with Ctrl-C.

## Pose API

Connect to `ws://127.0.0.1:8765/pose`. Each text message contains:

```json
{"timestamp":1789600000.125,"position":[0.1,-0.2,0.5],"quaternion":[0,0,0,1]}
```

- `timestamp`: Unix seconds when the producer received the sample.
- `position`: meters, right-handed world frame: X forward, Y left, Z up.
- `quaternion`: normalized `[x,y,z,w]`, rotating body-local vectors into world coordinates.

All values must be finite numbers. The Vicon adapter converts millimeters to meters
and reorders its SDK quaternion to this shared convention.

The stream is read-only. Invalid/occluded poses are skipped; tracking loss means
silence. Clients should detect stale data. Slow clients skip pending samples;
new connections receive only new samples. Input EOF stops the server.

Other adapters must emit this format as flushed, newline-delimited JSON to
stdout, with diagnostics on stderr:

```sh
python -u my_mocap_adapter.py | poseproxy
```

## Browser

Run Python on the browser's computer. From a Connect button handler:

```js
const response = await fetch("http://127.0.0.1:8765/health");
if (!response.ok) throw new Error("Pose server unavailable");
const socket = new WebSocket("ws://127.0.0.1:8765/pose");
socket.onmessage = ({data}) => console.log(JSON.parse(data));
```

Grant Chrome's local/loopback permission. Allowed origins: `https://rc.rl.tools`,
`http://localhost:8000`, `http://127.0.0.1:8000`; extend with `--allow-origin URL`.
`/health` returns `last_pose_age_s` (`null` before any sample).

From the repository root: `pip install ./poseproxy`.
Tests: `python -B -m unittest discover -s poseproxy/tests -v`.
