Ex: Live Client

Connect to a Carbon sensor and capture incoming frames with C++.

This example demonstrates how to connect to a Carbon sensor and capture incoming frames.

What You’ll Learn

  • How to create a CarbonClient from a CarbonConfig
  • How to receive and process frame data in a polling loop
  • How to read back sensor and time-sync state
  • Basic signal handling for graceful application termination

Prerequisites

  • Voyant SDK installed (see Installation)
  • A Carbon sensor, or the bundled voyant_simulator running locally (pass --sim)

Example Code

View the complete example on GitHub: carbon_client_basic.cpp

Key Concepts

This example demonstrates the basic usage of the CarbonClient class.

Signal Handling

The static CarbonClient::setupSignalHandling() method configures the application to gracefully handle interruption signals (like Ctrl+C). CarbonClient::isTerminated() then reports when a signal has been received so the main loop can exit cleanly.

Client Configuration

The client is created from a CarbonConfig. The defaults target a real sensor; pass --sim to point at a local voyant_simulator on loopback:

CarbonConfig config;
if (sim)
{
    config.setInterfaceAddr("127.0.0.1").setFpgaTargetAddr("127.0.0.1:1234");
}

// Optional: override defaults as needed
// config.setRangeMax(50.0f);

CarbonClient client(config);
if (!client.start())
{
    std::cerr << "Failed to start CarbonClient" << std::endl;
    return 1;
}

For real sensors, set the interface address to your host’s network interface IP. Refer to our Connections page for more information.

Frame Capture Loop

The main loop runs while the client is running and no termination signal has been received:

  1. client.tryReceiveFrame() is non-blocking — it returns the new VoyantFrame when one is available, or an empty std::optional when not
  2. client.getSensorState() / client.getTimeSyncState() expose device health, SDL state, and host↔FPGA clock sync
while (client.isRunning() && !CarbonClient::isTerminated())
{
    if (auto frame = client.tryReceiveFrame())
    {
        std::cout << *frame << std::endl;
        std::cout << "Sensor State: " << client.getSensorState() << std::endl;
        std::cout << "Time Sync: " << client.getTimeSyncState() << std::endl;
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
}

Building and Running

You can build and run this example using CMake:

  • From /workspace/ if building in the provided docker container.
  • From voyant-sdk/ if building on your native system.
# Build the examples
mkdir build && cd build
cmake ..
make

# Run against a real sensor
./bin/carbon_client_basic

# Or run against a local voyant_simulator
./bin/carbon_client_basic --sim

Expected Output

When streaming, you should see output similar to:

Listening for frames (Ctrl+C to exit)...
###############
VoyantFrame(frame_index=35, n_points=15850, n_valid_points=15850, timestamp=1742330842.721523, device_id=...)
Sensor State: SensorState{
  DeviceInfo{ serial=... product=... fpga=v... mcu=v... }
  SdlDeviceState{ state=PointCloud status=Applied fps=10 hfov=60deg hfov_center=0deg bw=6GHz ramp_length=V16_384us }
  ...
}
Time Sync: TimeSyncState{ quality=Good valid=1 offset=... round_trip=... jitter=... samples=... age_ms=... }

Next Steps


Copyright © Voyant Photonics, Inc.

This site uses Just the Docs, a documentation theme for Jekyll.