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
CarbonClientfrom aCarbonConfig - 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
carbon_simulatorrunning 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 carbon_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);
// config.setPfa(1e-4f);
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:
client.tryReceiveFrame()is non-blocking — it returnstruewhen a new frame is availableclient.latestFrame()returns the most recent frameclient.getSensorState()/client.getTimeSyncState()expose device health, SDL state, and host↔FPGA clock sync
while (client.isRunning() && !CarbonClient::isTerminated())
{
if (client.tryReceiveFrame())
{
std::cout << client.latestFrame() << 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));
}
latestFrame() returns a reference — avoid copying VoyantFrameWrapper in the hot loop, as each copy allocates a full frame’s worth of memory.
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 carbon_simulator
./bin/carbon_client_basic --sim
Expected Output
When streaming, you should see output similar to:
Listening for frames (Ctrl+C to exit)...
###############
Header{message type:2, device id:CAR-000, frame idx:35, stamp:1742330842.721523408, proto version:0.2.2, api version:0.14.1}
Config{ len: 0 }
Points[23040] {{idx:6238209,ts:163840,pos:[43.984,0.193966,11.0427],v:1.22985,snr:12.3234,refl:0,noise:34.0003,min_snr:-0.00802298,drop reason:1},...}
Sensor State: SensorState{
DeviceInfo{ serial=0 product=Software Simulator 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=... }
The device id:CAR-000 marks this as a Carbon-format frame.
Next Steps
- SDL Command — reconfigure the sensor (state, FOV, frame rate) at runtime
- Data Recording — save live frames to disk
- API Documentation — full C++ API reference