Jetson Nano Precise Interval Timer C++ Example

Looking for a reliable method in C/C++ to call a function at a precise timing interval, in the order of 0.005 seconds (200hz sample rate). Needed for a feedback control system - the control law and filters require a fixed time step.

In the following link, @snarky says “a pthread with realtime priority and SCHED_RR scheduling policy will be your best bet.” Is there a C/C++ example?

[url]https://devtalk.nvidia.com/default/topic/1031797/jetson-tx2/using-timers-periodic-functioning-of-a-code-on-jetson-tx2/#[/url]

I understand Linux is “soft real time”, but can precise timing be achieved? Especially if all programs and web browsers are closed? Is it possible to sample the main Nano CPU clock to achieve precise timing, with C++?

1 Like

PThreads are just the standard API from pthread_create(3) - Linux manual page

For the specific use case you’re suggesting, you may be better served by SCHED_FIFO, but in practice, SCHED_RR is probably equivalent for most use cases.

Here’s an example program:

/* demonstration of real time thread on Linux:
 * Build with:
 * g++ -o prog realtime.cpp -O1 -g -lpthread
 *
 * By Jon Watte, 2019-07-19, MIT license.
 * https://github.com/jwatte
 */

#include <pthread.h>
#include <time.h>
#include <unistd.h>
#include <sched.h>
#include <stdint.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

bool volatile running = true;

void *myfunc(void *) {

    //  keep track of the passage of time at a 5-millisecond quantized interval
    struct timespec last = {}, now = {};
    clock_gettime(CLOCK_MONOTONIC_RAW, &last);

    while (running) {
        clock_gettime(CLOCK_MONOTONIC_RAW, &now);
        //  5 milliseconds, as nanoseconds
        int64_t tosleep = 5000000 - (now.tv_sec - last.tv_sec) * 1000000000 - (now.tv_nsec - last.tv_nsec);
        last.tv_nsec += 5000000;
        if (last.tv_nsec >= 1000000000) {
            last.tv_nsec -= 1000000000;
            last.tv_sec += 1;
        }
        if (tosleep > 10000000) {
            //  missed by more than one full interval! re-set the clock basis
            last = now;
            tosleep = 0;
        }
        if (tosleep > 0) {
            struct timespec slp = {};
            slp.tv_nsec = (long)tosleep;
            nanosleep(&slp, NULL);
        }

        //  do the thing
        //  whatever it is
    }

    return NULL;
}

void sigint(int) {
    running = false;
}

int main() {
    //  make sure ctrl-C stops the program under controlled circumstances
    signal(SIGINT, &sigint);

    //  create attributes for an isolated real-time thread
    pthread_attr_t attr = {};
    pthread_attr_init(&attr);
    //  lift the thread off core 0, which takes system interrupts
    cpu_set_t cpuset = {};
    CPU_ZERO(&cpuset);
    CPU_SET(1, &cpuset);
    pthread_attr_setaffinity_np(&attr, 1, &cpuset);
    //  make it use FIFO policy for real-time scheduling
    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    //  set the priority
    sched_param param = {};
    param.sched_priority = 30;
    pthread_attr_setschedparam(&attr, &param);

    //  create the real-time thread
    pthread_t mythread;
    int err;
    if ((err = pthread_create(&mythread, &attr, &myfunc, NULL)) != 0) {
        char const *emsg = ((err == EAGAIN) ? "EAGAIN" : ((err == EINVAL) ? "EINVAL" : ((err == EPERM) ? "EPERM" : "unknown")));
        fprintf(stderr, "pthread_create() failed (%d %s); are you sure you're root?\n", err, emsg);
        fprintf(stderr, "You may also need to do:\n");
        fprintf(stderr, "echo -1 > /proc/sys/kernel/sched_rt_runtime_us\n");
        exit(1);
    }

    //  wait for the program to be done
    void *ignore = NULL;
    pthread_join(mythread, &ignore);

    return 0;
}

You may also be interested in the setitimer() Linux system function, but that doesn’t necessarily let you control which CPU core the timer runs on, which means it may be blocked from execution by system interrupts on core 0. Also, it delivers a signal, and the rules for what you’re allowed to do inside a signal handler are REALLY STRiCT. (Many programs “get away with” breaking the rules, 99.9% of the time. Since you’re talking about real-time, I assume you’re also interested in verifiable correctness. Don’t do things other than setting volatile global variables from within a signal handler!)