# ICalibrationClient

<Badge tone="type">interface</Badge> <Badge>thread-safe</Badge>

```csharp
namespace AR51.Net;

public interface ICalibrationClient
```

Camera calibration and the tracked area (capture bounds). Use it through [`IAR51Client.Calibration`](/docs/sdk-api/csharp-api/classes/iar51client/).

A calibration ends by itself once it has collected enough data, or when someone stops it. This client reports every calibration, whoever started it (this app, Mocap Studio or another client). All members are safe to use from any thread.

:::tip[How-to]
This page is **reference** (facts only). For a walkthrough with a complete sample, see the [Calibration guide](/docs/csharp-api/calibration/).
:::

## Properties

<div className="apiPropTable apiTable">

| Property | Type | Access | Description |
|---|---|---|---|
| `Status` | [`CalibrationStatus`](#calibrationstatus) | `get` | The latest calibration status reported by the server, whoever started the calibration. |

</div>

## Method summary

<div className="apiTable">

| Method | Returns | |
|---|---|---|
| [`StartCalibrationAsync`](#startcalibrationasync) | [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) |  |
| [`StopCalibrationAsync`](#stopcalibrationasync) | [`Task<CalibrationResult>`](#calibrationresult) |  |
| [`GetCaptureBoundsAsync`](#getcaptureboundsasync) | [`Task<CaptureBounds>`](/docs/sdk-api/csharp-api/classes/capturebounds/) |  |
| [`SetCaptureBoundsAsync`](#setcaptureboundsasync) | [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) |  |

</div>

→ Full descriptions in [Method details](#method-details) below.

## Events

Standard .NET events (`EventHandler<T>`): subscribe with `+=`, unsubscribe with `-=`. They are raised on a background thread.

<div className="apiTable">

| Event | Args | Raised when |
|---|---|---|
| `CalibrationStarted` | [`CalibrationStatusEventArgs`](#calibrationstatuseventargs) | Raised when a calibration starts, whoever started it. Raised on a background thread. |
| `CalibrationProgressChanged` | [`CalibrationStatusEventArgs`](#calibrationstatuseventargs) | Raised when a running calibration's progress or error changes. Raised on a background thread. |
| `CalibrationEnded` | [`CalibrationEndedEventArgs`](#calibrationendedeventargs) | Raised when a calibration ends: it collected enough data, or someone stopped it. Raised on a background thread. The outcome is known only for a calibration this client started; for one started elsewhere it is [`CalibrationOutcome.Unknown`](#calibrationoutcome). |

</div>

## Method details

### StartCalibrationAsync

<ApiBody kind="method">

```csharp
Task StartCalibrationAsync(CancellationToken cancellationToken = default);
```

Starts a camera calibration. It ends by itself once it has enough data, or call [`StopCalibrationAsync`](#stopcalibrationasync).

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Cancels the request. Optional.</Param>
</Params>

<Returns type="Task" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task">A task that completes once the calibration is running.</Returns>

<Throws>
<Throw type="AR51Exception" typeHref="/docs/sdk-api/csharp-api/classes/ar51exception/">A calibration is already running ([`AR51ErrorCode.Rejected`](/docs/sdk-api/csharp-api/classes/ar51exception/#ar51errorcode)), or the calibration service is not available.</Throw>
</Throws>

:::caution[Affects every app using the system]
Tracking keeps running on the current calibration meanwhile. A successful calibration **replaces every camera's calibration** for every app connected to the AR 51 system; a failed or cancelled one leaves the current calibration in place.
:::

:::note
This client keeps the calibration running: if it is stopped or disposed (for example the app shuts down) before the calibration ends, the calibration is cancelled.
:::

<Example>

```csharp
ar51.Calibration.CalibrationProgressChanged += (_, e) =>
    Console.WriteLine($"{e.Status.Progress:P0}, error {e.Status.ReprojectionErrorPixels:F2} px");
ar51.Calibration.CalibrationEnded += (_, e) =>
    Console.WriteLine($"calibration {e.Result.Outcome}");
await ar51.Calibration.StartCalibrationAsync();
```

</Example>

</ApiBody>

### StopCalibrationAsync

<ApiBody kind="method">

```csharp
Task<CalibrationResult> StopCalibrationAsync(CancellationToken cancellationToken = default);
```

Finishes the running calibration now, whoever started it, and waits for its result. Does nothing if none is running.

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Stops waiting for the result; the calibration still stops. Optional.</Param>
</Params>

<Returns type="Task<CalibrationResult>" typeHref="#calibrationresult">The result: [`CalibrationOutcome.Succeeded`](#calibrationoutcome) or [`CalibrationOutcome.Failed`](#calibrationoutcome) when enough data was collected, [`CalibrationOutcome.Cancelled`](#calibrationoutcome) when stopped too early, and [`CalibrationOutcome.Unknown`](#calibrationoutcome) for a calibration started elsewhere or when none was running.</Returns>

<Throws>
<Throw type="AR51Exception" typeHref="/docs/sdk-api/csharp-api/classes/ar51exception/">The calibration service is not available.</Throw>
</Throws>

:::caution[Affects every app using the system]
Ends the running calibration now, **whoever started it** — including one an operator started in Mocap Studio. Ended before it has enough data, it is `Cancelled`.
:::

</ApiBody>

### GetCaptureBoundsAsync

<ApiBody kind="method">

```csharp
Task<CaptureBounds> GetCaptureBoundsAsync(CancellationToken cancellationToken = default);
```

Gets the tracked area: people outside it are not tracked.

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Cancels the request. Optional.</Param>
</Params>

<Returns type="Task<CaptureBounds>" typeHref="/docs/sdk-api/csharp-api/classes/capturebounds/">The tracked area.</Returns>

<Throws>
<Throw type="AR51Exception" typeHref="/docs/sdk-api/csharp-api/classes/ar51exception/">The calibration service is not available.</Throw>
</Throws>

</ApiBody>

### SetCaptureBoundsAsync

<ApiBody kind="method">

```csharp
Task SetCaptureBoundsAsync(CaptureBounds bounds, CancellationToken cancellationToken = default);
```

Sets the tracked area. **People outside it are no longer tracked**, so a wrong value makes people disappear.

<Params>
<Param name="bounds" type="CaptureBounds" typeHref="/docs/sdk-api/csharp-api/classes/capturebounds/">The new area.</Param>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Cancels the request. Optional.</Param>
</Params>

<Returns type="Task" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task">A task that completes when the server has applied the area.</Returns>

<Throws>
<Throw type="ArgumentException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception">`bounds` has no volume: its size is 0 or less on some axis.</Throw>
<Throw type="AR51Exception" typeHref="/docs/sdk-api/csharp-api/classes/ar51exception/">The calibration service is not available.</Throw>
</Throws>

:::caution[Affects every app using the system]
Applies immediately for **every app using the system**: anyone outside the new area stops being tracked. Double-check the values first.
:::

<Example>

```csharp
CaptureBounds bounds = await ar51.Calibration.GetCaptureBoundsAsync();
// People outside the area are no longer tracked: double-check before applying.
await ar51.Calibration.SetCaptureBoundsAsync(bounds with { Size = bounds.Size + new Vector3(0.5f, 0, 0.5f) });
```

</Example>

</ApiBody>

## Related types

Small types used by this interface, documented here.

### CalibrationStatus

<Badge tone="type">record · sealed</Badge>

```csharp
public sealed record CalibrationStatus(bool IsRunning, double Progress, double ReprojectionErrorPixels, string? Message);
```

The status of camera calibration.

<div className="apiTable">

| Property | Type | Description |
|---|---|---|
| `IsRunning` | `bool` | Whether a calibration is running. |
| `Progress` | `double` | Progress, from 0 to 1. |
| `ReprojectionErrorPixels` | `double` | The calibration error so far, in pixels: lower is better. 0 before any error was measured. |
| `Message` | `string?` | A message for the operator from the server, for example "Calculating final camera calibration...", or `null`. |

</div>

### CalibrationOutcome

<Badge tone="type">enum</Badge>

```csharp
public enum CalibrationOutcome
```

How a calibration ended.

<div className="apiTable">

| Value | Meaning |
|---|---|
| `Unknown = 0` | Not known: the calibration was started by another app, or none was running. |
| `Succeeded = 1` | The calibration succeeded; [`ICameraClient.GetCamerasAsync`](/docs/sdk-api/csharp-api/classes/icameraclient/#getcamerasasync) returns the new values. |
| `Failed = 2` | The calibration failed; the cameras keep their previous values. |
| `Cancelled = 3` | The calibration was stopped before it had enough data; the cameras keep their previous values. |

</div>

### CalibrationResult

<Badge tone="type">record · sealed</Badge>

```csharp
public sealed record CalibrationResult(CalibrationOutcome Outcome, double ReprojectionErrorPixels);
```

The result of a calibration.

<div className="apiTable">

| Property | Type | Description |
|---|---|---|
| `Outcome` | [`CalibrationOutcome`](#calibrationoutcome) | How the calibration ended. |
| `ReprojectionErrorPixels` | `double` | The final calibration error, in pixels: lower is better. |

</div>

### CalibrationStatusEventArgs

<Badge tone="type">class · sealed</Badge>

```csharp
public sealed class CalibrationStatusEventArgs : EventArgs
```

Data for `ICalibrationClient.CalibrationStarted` and `ICalibrationClient.CalibrationProgressChanged`.

<div className="apiPropTable apiTable">

| Property | Type | Access | Description |
|---|---|---|---|
| `Status` | [`CalibrationStatus`](#calibrationstatus) | `get` | The calibration status. |

</div>

<details>
<summary>Constructor</summary>

```csharp
public CalibrationStatusEventArgs(CalibrationStatus status);
```

Creates the event data.

</details>

### CalibrationEndedEventArgs

<Badge tone="type">class · sealed</Badge>

```csharp
public sealed class CalibrationEndedEventArgs : EventArgs
```

Data for `ICalibrationClient.CalibrationEnded`.

<div className="apiPropTable apiTable">

| Property | Type | Access | Description |
|---|---|---|---|
| `Result` | [`CalibrationResult`](#calibrationresult) | `get` | How the calibration ended. |

</div>

<details>
<summary>Constructor</summary>

```csharp
public CalibrationEndedEventArgs(CalibrationResult result);
```

Creates the event data.

</details>

## See also

- [Calibration guide](/docs/csharp-api/calibration/) — walkthrough with a complete, compiled sample
- [Class index](/docs/sdk-api/csharp-api/classes/) — every AR51.Net type
- [Data conventions, threading and errors](/docs/csharp-api/conventions/)
