The SmartSpectra C++ SDK provides access to physiological metrics through the protobuf-based metrics system defined in @edge/modules/messages/metrics.proto. This document explains how to use and access these metrics in your C++ applications.
Table of Contents
- Metrics Overview
- Accessing Core Metrics
- Accessing Edge Metrics
- Metric Types and Structures
- Dense Face Landmarks
- Code Examples
- Protobuf Serialization
Metrics Overview
The SmartSpectra SDK provides two types of metrics:
- Core Metrics (MetricsBuffer): High-level physiological measurements computed via the Physiology REST API, including refined pulse rates, breathing rates, and face detection data.
- Edge Metrics (Metrics): Real-time measurements computed locally on the device, providing breathing traces, micromotion data, and face landmarks at frame rate.
NOTE: some metrics are not publicly available yet, please contact Presage Support for any inquiries.
Both MetricsBuffer and Metrics classes are generated from the Protobuf messages. See the SmartSpectra Protocol Buffer Reference for comprehensive descriptions of all their available fields.
***Important***
- The frequency of Core Mectrics (MetricsBuffer) correspond to the buffer_duration argument on the physiology_server.
- The breathing & micromotion trace signals for Edge Metrics (Metrics) appear at the target framerate (Currently fixed at 30 Hz) and the EDA signal for Edge Metrics (Metrics) comes in periodically at approximately every 0.2 seconds
Accessing Core Metrics
Core metrics are obtained through the OnCoreMetricsOutput callback and contain refined physiological data from the cloud-based Physiology API.
Setting Up the Core Metrics Callback
#include <physiology/modules/messages/metrics.h>
MP_RETURN_IF_ERROR(container.SetOnCoreMetricsOutput([](const presage::physiology::MetricsBuffer& metrics, int64_t timestamp_microseconds) {
LOG(INFO) << "Received core metrics: " << metrics;
if (metrics.has_pulse()) {
for (const auto& rate : metrics.pulse().rate()) {
std::cout << "Pulse rate: " << rate.value()
<< " bpm, time: " << rate.time()
<< "s, confidence: " << rate.confidence() << std::endl;
}
for (const auto& trace_point : metrics.pulse().trace()) {
std::cout << "Pulse trace value: " << trace_point.value()
<< " at time: " << trace_point.time() << "s" << std::endl;
}
}
if (metrics.has_breathing()) {
for (const auto& rate : metrics.breathing().rate()) {
std::cout << "Breathing rate: " << rate.value()
<< " breaths/min, confidence: " << rate.confidence() << std::endl;
}
}
return absl::OkStatus();
}));
Accessing Edge Metrics
Edge metrics provide real-time data computed locally on the device. To enable edge metrics, set enable_edge_metrics to true in your settings.
Setting Up the Edge Metrics Callback
settings.enable_edge_metrics = true;
MP_RETURN_IF_ERROR(container.SetOnEdgeMetricsOutput([](const presage::physiology::Metrics& metrics) {
LOG(INFO) << "Computed edge metrics: " << metrics;
if (metrics.has_breathing()) {
if (!metrics.breathing().upper_trace().empty()) {
const auto& latest_upper = *metrics.breathing().upper_trace().rbegin();
std::cout << "Upper breathing trace: " << latest_upper.value()
<< " at time: " << latest_upper.time() << "s" << std::endl;
}
if (!metrics.breathing().lower_trace().empty()) {
const auto& latest_lower = *metrics.breathing().lower_trace().rbegin();
std::cout << "Lower breathing trace: " << latest_lower.value()
<< " at time: " << latest_lower.time() << "s" << std::endl;
}
}
return absl::OkStatus();
}));
Metric Types and Structures
Measurement Types
The metrics system uses several fundamental measurement types:
Measurement - Basic measurement with time and value:
struct Measurement {
float time();
float value();
bool stable();
}
MeasurementWithConfidence - Measurement with confidence score:
struct MeasurementWithConfidence {
float time();
float value();
bool stable();
float confidence();
}
DetectionStatus - Used for binary detection events:
struct DetectionStatus {
float time();
bool detected();
bool stable();
}
Core Metric Structures
MetricsBuffer - Main container for core metrics:
struct MetricsBuffer {
Pulse pulse();
Breathing breathing();
BloodPressure blood_pressure();
Face face();
Metadata metadata();
}
Pulse - Pulse rate and trace data:
struct Pulse {
repeated MeasurementWithConfidence rate();
repeated Measurement trace();
repeated Measurement pulse_respiration_quotient();
Strict strict();
}
Breathing - Comprehensive breathing metrics:
struct Breathing {
repeated MeasurementWithConfidence rate();
repeated Measurement upper_trace();
repeated Measurement lower_trace();
repeated Measurement amplitude();
repeated DetectionStatus apnea();
repeated Measurement respiratory_line_length();
repeated Measurement baseline();
repeated Measurement inhale_exhale_ratio();
Strict strict();
}
Face - Face detection and landmark data:
struct Face {
repeated DetectionStatus blinking();
repeated DetectionStatus talking();
repeated Landmarks landmarks();
}
Edge Metric Structures
Metrics - Container for edge-computed metrics:
struct Metrics {
Breathing breathing();
MicroMotion micromotion();
Eda eda();
Face face();
}
Dense Face Landmarks
Dense face landmarks provide detailed facial feature points for advanced analysis. To access face landmarks:
Enabling Dense Face Landmarks
Enable dense face landmarks in your application settings:
settings.enable_dense_facemesh_points = true;
Accessing Face Landmarks from Edge Metrics
Face landmarks are available through the edge metrics system:
MP_RETURN_IF_ERROR(container.SetOnEdgeMetricsOutput([](const presage::physiology::Metrics& metrics) {
if (metrics.has_face() && !metrics.face().landmarks().empty()) {
const auto& latest_landmarks = *metrics.face().landmarks().rbegin();
std::cout << "Face landmarks at time: " << latest_landmarks.time() << "s" << std::endl;
std::cout << "Landmark count: " << latest_landmarks.value_size() << std::endl;
std::cout << "Stable: " << (latest_landmarks.stable() ? "yes" : "no") << std::endl;
std::cout << "Reset: " << (latest_landmarks.reset() ? "yes" : "no") << std::endl;
for (int i = 0; i < latest_landmarks.value_size(); ++i) {
const auto& point = latest_landmarks.value(i);
std::cout << "Landmark " << i << ": (" << point.x() << ", " << point.y() << ")" << std::endl;
}
}
return absl::OkStatus();
}));
Face Landmarks Structure
struct Landmarks {
float time();
repeated Point2dFloat value();
bool stable();
bool reset();
}
struct Point2dFloat {
float x();
float y();
}
Code Examples
Complete Metrics Processing Example
See the rest_continuous_example for a comprehensive example of:
- Setting up both core and edge metrics callbacks
- Processing real-time breathing traces
- Handling micromotion data
- Plotting metrics in real-time
Minimal Metrics Usage Example
See the minimal_rest_spot_example for a simple example of:
- Basic core metrics processing
- JSON serialization of metrics
- Essential callback setup
Protobuf Serialization
You can serialize metrics to binary format for storage or transmission:
Serializing Metrics to Binary
#include <google/protobuf/util/json_util.h>
std::string binary_data;
if (metrics.SerializeToString(&binary_data)) {
std::ofstream file("metrics.bin", std::ios::binary);
file.write(binary_data.data(), binary_data.size());
file.close();
}
Converting Metrics to JSON
std::string json_string;
google::protobuf::util::JsonPrintOptions options;
options.add_whitespace = true;
options.preserve_proto_field_names = true;
auto status = google::protobuf::util::MessageToJsonString(metrics, &json_string, options);
if (status.ok()) {
std::cout << "Metrics JSON: " << json_string << std::endl;
}
Deserializing Metrics from Binary
std::ifstream file("metrics.bin", std::ios::binary);
std::string binary_data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
presage::physiology::MetricsBuffer loaded_metrics;
if (loaded_metrics.ParseFromString(binary_data)) {
std::cout << "Loaded metrics successfully" << std::endl;
}
Command Line Options
The following command line flags control metrics behavior:
- --enable_edge_metrics: Enable real-time edge metrics computation
- --enable_dense_facemesh_points: Enable dense facial landmark detection
- --enable_micromotion: Enable micromotion analysis (requires thighs/knees in view)
- --enable_phasic_bp: Enable phasic blood pressure computation
- --verbosity: Set verbosity level for metrics logging (1-4)
Best Practices
- Performance: Edge metrics are computed at frame rate, so keep processing lightweight in the callback
- Stability: Check the stable() flag before using measurement values for critical applications
- Face Landmarks: Monitor the reset() flag to detect when landmark tracking has been reinitialized
- Memory Management: Process metrics data promptly in callbacks to avoid memory buildup
- Error Handling: Always check absl::Status return values from SDK methods
Troubleshooting
- No Edge Metrics: Ensure enable_edge_metrics = true in your settings
- No Face Landmarks: Enable enable_dense_facemesh_points = true
- Empty Measurements: Check that face is properly detected and in camera view
- Some metrics are not publicly available yet, please contact Presage Support for any inquiries
For additional support, contact support@presagetech.com or submit a GitHub issue.