# Data Conventions, Threading and Errors (C# API)

The rules that hold across all of **AR51.Net**: units, axes, time, threading and errors.

## Units and axes

- **Positions** are in **meters**.
- The AR 51 world is **left-handed: X right, Y up, Z forward** — the same axes as Unity.
- **Rotations** are `System.Numerics.Quaternion`; vectors and matrices are `System.Numerics` types.
- **Camera calibration** follows the OpenCV convention: focal lengths and principal point in pixels, and `WorldToCamera` maps a world point into the camera's space (X right, Y down, Z forward). See [Cameras](/docs/csharp-api/cameras/#calibration-values).

## Time

- `CaptureTime` — when the cameras captured the data, on the **server's** clock, in UTC. It's the Unix epoch (1970-01-01) when the server didn't send a valid time.
- `ReceivedTime` — when your app received it, on **your** clock.

Their difference is the true delay only when both clocks are synchronized, for example with NTP.

## Undetected joints

A body joint the cameras couldn't see is reported at **(0, 0, 0) with confidence 0**. Use `Skeleton.TryGetPosition` to tell it apart from a real position.

## Threading

- **Every member is safe to call from any thread.** Create **one** client and keep it for the life of your application.
- **`SkeletonReceived` and `ObjectReceived`** are raised on the stream's own background thread, one event per skeleton or object, in arrival order. **Keep these handlers short:** for example, 20 people at 120 frames per second leaves all handlers together about **0.4 ms per skeleton**. Copy what you need and do heavy work elsewhere — see [hand-off patterns](/docs/csharp-api/app-integration/#hand-data-off-the-receiving-thread).
- **All other events** — connection state, servers, devices, calibration, auto-tune, recording — are raised **in order, one at a time, on one background thread** per client.
- A handler that throws is **logged** and doesn't stop the other handlers or the stream. Handlers may call `StopAsync` or `DisposeAsync` on the client.
- To update a UI, marshal to your UI thread (for example with `SynchronizationContext` or your framework's dispatcher).

## Errors

Failed calls throw [`AR51Exception`](/docs/sdk-api/csharp-api/classes/ar51exception/); its `ErrorCode` says why:

<div className="apiTable">

| `ErrorCode` | Meaning | What to do |
|---|---|---|
| `ServiceUnavailable` | The server, or the service needed, isn't reachable — for example the client isn't started yet | Call `StartAsync` first; check the AR 51 machines are running |
| `ConnectionLost` | The connection dropped during the call | Retry once the client has reconnected |
| `Timeout` | The server didn't answer in time | Retry |
| `Rejected` | The server refused the request and says why — for example "a calibration is already running" | Read `exception.Message`; fix the request or try again later |
| `ServerError` | The server failed while handling the request | Retrying is unlikely to help until the server is fixed or restarted |
| `Unknown` | Anything else | See the inner exception |

</div>

Cancelling throws `OperationCanceledException`; using a disposed client throws `ObjectDisposedException` — as usual in .NET.

```csharp
try
{
    await ar51.Calibration.StartCalibrationAsync();
}
catch (AR51Exception exception) when (exception.ErrorCode == AR51ErrorCode.Rejected)
{
    Console.WriteLine($"Refused by the server: {exception.Message}");
}
```

## Reference

[`AR51Exception`](/docs/sdk-api/csharp-api/classes/ar51exception/) · [`AR51ErrorCode`](/docs/sdk-api/csharp-api/classes/ar51exception/#ar51errorcode) · [API reference](/docs/sdk-api/csharp-api/)
