C++ 1kHz+ Echtzeit-Plotten mit ROOT
Das folgende Beispiel besteht aus zwei Komponenten:
- Ein C++-Server, der Sinuswellendaten (1Hz Sinuswellenfrequenz, 1kHz Samplerate = Datenrate) in Echtzeit generiert und jeden einzelnen Datenpunkt über einen ZeroMQ PUB-Socket auf Port
5555sendet. - Ein C++-Client, der die Daten empfängt und mit ROOT in Echtzeit plottet, mit einer Aktualisierungsrate von ca. 100 Hz und maximal 10k Datenpunkten im Speicher.
Der Server ist in server.cpp und der Client in main.cpp implementiert.
Bei einem geeigneten OpenGL-Backend zeigt der Client einen Echtzeit-Plot der Sinuswelle ohne spürbare Verzögerung an.
Kompilieren mit
root_plot_build_run.sh
g++ -o plot main.cpp `root-config --cflags --glibs` -lzmq
g++ -o server server.cpp `root-config --cflags --glibs` -lzmqführen Sie dann ./server und ./plot in separaten Shells aus.

Plotting-Client
main.cpp
#include <iostream>
#include <vector>
#include <zmq.hpp>
#include <TApplication.h>
#include <TGraph.h>
#include <TCanvas.h>
#include <TSystem.h>
using namespace std;
// Maximum number of data points to display
const int MAX_POINTS = 10000;
const int BATCH_SIZE = 10; // Only update plot every 10 points (100Hz update rate @1kHz)
int main(int argc, char* argv[]) {
// Initialize ROOT TApplication for event handling and GUI
TApplication app("app", &argc, argv);
// Create a canvas to display the graph
TCanvas *c1 = new TCanvas("c1", "Real-time Plot", 800, 600);
c1->SetGrid();
// Create a TGraph to hold the data
auto graph = std::make_unique<TGraph>();
graph->SetTitle("Real-time Data;Time;Value");
//graph->SetMarkerStyle(7); // Small dot marker
graph->Draw("AL");
// Setup ZeroMQ context and SUB socket
zmq::context_t context(1);
zmq::socket_t socket(context, zmq::socket_type::sub);
// Connect to the publisher socket (modify "tcp://localhost:5555" as needed)
socket.connect("tcp://localhost:5555");
// Subscribe to all messages (empty string filter)
socket.set(zmq::sockopt::subscribe, "");
// Variables to store data
double time = 0.0;
std::vector<double> times;
std::vector<double> values;
int batch_counter = 0;
while (true) {
// Poll for incoming data with a timeout of 100 ms
zmq::message_t message;
if (socket.recv(message, zmq::recv_flags::none)) {
// Extract double value from the message
double value;
memcpy(&value, message.data(), sizeof(double));
// Update the time (or you could use an external clock/timestamp)
time += 1.0 / 1000.0; // Assuming a 1kHz sample rate
// Add the new point to the vectors
times.push_back(time);
values.push_back(value);
// If we exceed the maximum number of points, remove the oldest point
if (times.size() > MAX_POINTS) {
times.erase(times.begin());
values.erase(values.begin());
}
// Only update the graph every BATCH_SIZE points
batch_counter++;
if (batch_counter >= BATCH_SIZE) {
batch_counter = 0;
// Update the TGraph with the new data
graph->Set(times.size());
for (size_t i = 0; i < times.size(); ++i) {
graph->SetPoint(i, times[i], values[i]);
}
graph->Draw("AL");
// Redraw the graph
c1->Modified();
c1->Update();
}
}
// Handle ROOT events (this keeps the GUI responsive)
gSystem->ProcessEvents();
// Introduce a small delay (optional)
gSystem->Sleep(1); // 1 ms sleep for smooth handling
}
// Enter the ROOT event loop (never reached in this case, but necessary for completeness)
app.Run();
return 0;
}Datengenerierungs-Server
server.cpp
#include <iostream>
#include <zmq.hpp>
#include <chrono>
#include <thread>
#include <cmath>
// Constants for sine wave generation
const double PI = 3.14159265358979323846;
const int SAMPLE_RATE = 1000; // 1kHz sampling rate
const double FREQUENCY = 1.0; // 1Hz sine wave frequency
int main() {
// Setup ZeroMQ context and PUB socket
zmq::context_t context(1);
zmq::socket_t socket(context, zmq::socket_type::pub);
// Bind to a port (e.g., tcp://*:5555)
socket.bind("tcp://*:5555");
// Start sine wave generation and publishing
int sample_count = 0;
while (true) {
// Generate the next sine wave value
double time = static_cast<double>(sample_count) / SAMPLE_RATE; // time in seconds
double sine_value = std::sin(2.0 * PI * FREQUENCY * time); // 1Hz sine wave
// Convert sine value to a message (sending a double)
zmq::message_t message(sizeof(double));
memcpy(message.data(), &sine_value, sizeof(double));
// Publish the sine wave value
socket.send(message, zmq::send_flags::none);
// Increment the sample count
sample_count++;
// Sleep to maintain 1kHz sample rate (1000 samples per second)
std::this_thread::sleep_for(std::chrono::microseconds(1000)); // 1 millisecond delay
if(sample_count % 1000 == 0) {
std::cout << "Sample count: " << sample_count << std::endl;
}
}
return 0;
}If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow