SDKC++ SDK

Data channels & read methods

How pose, IMU, Skeleton and output are read, and how to feed an external input source.

Current version · 1.4.0

Data channels & read methods

The two data families

The SDK publishes two families of data: raw values produced by the device, and values the SDK computes. They are read the same way; only how the data is produced differs:

Raw dataSDK-processed data
Typical APIpose(), imu(), status()skeleton(), output()
Algorithm packageNot requiredRequired for dexterous hand output, see Algorithm package configuration; Skeleton is provided by the SDK and needs none
Run modeNot requiredRequired, see Runtime modes

Raw data is covered in Reading raw data; SDK-processed data (retargeting) in Reading retargeting data.

Data read methods

The read methods deliver the same Sample<T>. Callback subscription is the recommended default: each sample is handled as it is produced, with no polling; use latest() when you only need the current value, and see Runtime modes for blocking waits and coroutines.

Pose, IMU, Skeleton, dexterous hand output, status, and calibration progress all use the same read methods: register a callback before start() to receive continuously, read the latest value with latest() after starting, wait for the next item with the blocking next(), or await it with coro::next. The executor, stop token and cancellation rules of the blocking and coroutine paths are in Runtime modes and C++20 coroutines.

Each read returns Sample<T>; for the fields, types and the constraints on the two timestamps, see Sample<T>.

Callback subscriptions

subscribe() invokes the callback each time new data arrives:

auto subscription = device.status().subscribe(
    [](const qnbot::Sample<qnbot::GloveStatus>& sample) {
        std::cout << sample.value.connected << '\n';
    });

Callbacks of the same subscription execute in order and never concurrently. Callbacks should stay short; an exception thrown inside one does not terminate the subscription. The application must keep the returned Subscription; call subscription.unsubscribe() when that callback needs to be stopped early. Do not synchronously cancel or destroy a subscription from inside its own callback.

For blocking on the next data item and C++20 coroutine usage, see Runtime modes.

Getting the latest value

latest() immediately returns the latest data currently saved; it returns std::nullopt when there is no data yet:

if (const auto sample = device.pose().latest()) {
    std::cout << sample->sequence << '\n';
}

External input

When there is no physical glove, or when the frames come from your own device or a simulated hand, ExternalConnection and push_frame() feed external hand frames as the input source. External frames join the same data path as a physical glove: raw poses are read directly, and the SDK computes the retargeting result.

Configuration and usage

External input has no device identity handshake, so the side must be configured explicitly in GloveConfig:

qnbot::GloveConfig glove_config{
    qnbot::Side::right, qnbot::ExternalConnection{}};

After configuring the target, subscribe to the output and start the SDK first, then have the application keep submitting frames:

auto device = glove.device();
auto output = device.output("hand");
auto subscription = output.subscribe(
    [](const qnbot::Sample<qnbot::HandJointCommand>& sample) {
        std::cout << sample.value.joints.size() << '\n';
    });
device.start();
glove.run_background();

qnbot::GlovePose pose = device.push_frame(frame);

push_frame() synchronously returns the raw GlovePose of the current frame; the retargeting result is read through output(). The example uses background mode.

External input uses the background or manual runtime: keep calling push_frame(frame) in background mode; in manual mode, submit each frame before calling update(). If the first run requires calibration, complete Capture and Calibration before expecting target output. See the complete background and manual update examples.

Frame constraints

  • The five fingertip poses (thumb, index, middle, ring, pinky) are required, and each value is a GloveNodePose.
  • position is 3 finite floating-point numbers, in meters.
  • quaternion_xyzw is 4 finite XYZW components with a non-zero overall norm; the SDK normalizes it before use.
  • timestamp_ms may be omitted; when provided it must be a finite number of milliseconds and strictly increasing for the same device; when omitted, the current shared time reference is used. See Timestamps & external time sync for the time unit, default reference, and synchronization flow.
  • timestamp_sec is retained only for compatibility with existing code. The SDK multiplies it by 1000 to convert it to milliseconds; do not set it together with timestamp_ms.

The public contract unifies units and the quaternion component order, but the specific coordinate origin, axis directions, and rotation reference frame must still match the product documentation of the selected algorithm package. The side is not passed with the frame; the device identity and sequence are determined by the selected device and the SDK. For the complete fields, see GloveNodePose and ExternalGloveFrame.