Skip to content

Getting Started

This guide assumes you are comfortable with async programming concepts, but you do not need to know the SDK in advance.

The SDK uses two async patterns:

  • Some APIs are one-shot async calls that complete once, such as connectInstrument(serialNumber:), runBluetoothScan(), and stopBluetoothScan().
  • Other APIs return async streams that keep yielding values over time, such as runInstrumentDiscovery(), runTest(checkRl:), and getBatchedTestResults().

That split is useful to keep in mind as you read the API surface: some calls perform a single action, while others represent an ongoing flow of updates.

The main entry point is Unify, and most applications begin with the shared singleton:

var unify = Unify.Instance;

Bluetooth scanning is a one-shot async action, while discovery is a stream because the visible set of instruments can change over time. A typical flow is:

  1. Start a Bluetooth scan.
  2. Consume instrument list snapshots from runInstrumentDiscovery().
  3. Wait for the snapshot that contains the instrument you want to connect to.
  4. Connect with connectInstrument(serialNumber:).

Once you have chosen an instrument, read its definition before building a test so that your configuration matches the connected hardware.

The SDK works with one configured Test at a time. That test can contain one or more traces, so even a single test run may produce several outputs.

You will see the same pattern in several SDK types: a single API value can carry one of a few concrete shapes. Trace is an early example. Test.traces is a list of Trace values, and each entry holds one specific trace kind. Most of the time you can pass those values around uniformly, then inspect the concrete case only when you need trace-specific settings or data.

Before you start a measurement, call configureTest(test:). This gives the SDK a chance to validate the request and return a TestConfigurationResult that tells you whether the configuration is valid.

TestConfigurationResult is also a useful place to inspect the configured traces and initial trace contexts. If you want to modify an existing configured trace, the easiest path is usually to start from the configured trace returned in that result and update it while preserving its trace identifier, rather than rebuilding the trace from scratch.

After configuration succeeds, start the measurement with runTest(checkRl:).

This is a stream of progress updates, not a single completion callback. In other words, you typically iterate over the values it yields until the stream finishes:

await foreach (var progressUpdate in unify.RunTest(checkRl: true))
{
var percentage = progressUpdate.TraceProgress;
Console.WriteLine($"Progress: {percentage * 100}%");
}

For a getting-started workflow, it is enough to think of this stream as “the test is still running and here is its latest progress.”

The SDK gives you two main ways to consume test results:

  • getRealtimeTestResults() for live updates while the test is running
  • getBatchedTestResults() for a simpler stream of complete results

If you are just getting started, the batched path is usually the easiest one to begin with:

var testResult = await unify.GetBatchedTestResults().FirstOrDefaultAsync();
if (testResult is not null)
{
Console.WriteLine($"Got {testResult.TracePoints.Count} points.");
}

If you need live visual updates or progressive processing, look at the realtime API instead.

Once this flow makes sense, continue with Understanding Test Contexts to understand trace readiness or Performing a Calibration to learn how calibration fits into the workflow.