#define _GNU_SOURCE // for pthread_setname_np()
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <pthread.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#define MAX_SAFE_STACK (8 * 1024)
#define NSEC_PER_SEC (1000000000)
#define DIFF_NS(A, B) (((long long) (B).tv_sec - (A).tv_sec) * NSEC_PER_SEC \
+ (B).tv_nsec - (A).tv_nsec)
#define RT_PRIO (80)
int priority = -1;
const char *config = 0;
struct pdserv* pdserv = 0;
struct pdtask* pdtask = 0;
struct pdevent* event = 0;
unsigned int tsample_ns = 0.01e9;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
double omega = 1.2;
char enable = 1;
char reset = 0;
double omega_limit = 5.0;
#define AMP_INIT (10.0)
double amplitude_set = AMP_INIT;
double ampl_limit = 20.0;
unsigned int event_state[5] = {0, 0, 0, 0, 0};
double sine = 0.0, cosine = AMP_INIT;
double amplitude;
double ampl_modulation;
double derivative[2];
uint8_t counter;
int decimation_counter = 1;
size_t matrix_dim[] = {2, 3};
double matrix[2][3] = {{0, 1, 2}, {3, 4, 5}};
struct timespec* timer_add(struct timespec *t, unsigned int dt_ns)
{
t->tv_nsec += dt_ns;
while (t->tv_nsec >= NSEC_PER_SEC) {
t->tv_nsec -= NSEC_PER_SEC;
t->tv_sec++;
}
return t;
}
int gettime(struct timespec *time)
{
return clock_gettime(CLOCK_REALTIME, time);
}
void usage(FILE *f, const char *base_name)
{
fprintf(f,
"Usage: %s [OPTIONS]\n"
"PdServ library version: %s\n"
"Options:\n"
" --config -c conffile Set configuration file\n"
" --priority -p <PRIO> Set task priority.\n"
" Default is non-realtime.\n"
" --cycle-time -t <PERIOD> Cycle time in µs.\n"
" Default is 10000 (10 ms).\n"
" --help -h Show this help.\n",
}
void get_options(int argc, char **argv)
{
int c, arg_count;
static struct option longOptions[] = {
{"config", required_argument, NULL, 'c'},
{"priority", required_argument, NULL, 'p'},
{"cycle-time", required_argument, NULL, 't'},
{"help", no_argument, NULL, 'h'},
{NULL, no_argument, NULL, 0}
};
do {
c = getopt_long(argc, argv, "c:p:t:h", longOptions, NULL);
switch (c) {
case 'c':
config = optarg;
break;
case 'p':
if (!strcmp(optarg, "RT")) {
priority = RT_PRIO;
} else {
char *end;
priority = strtoul(optarg, &end, 10);
if (!*optarg || *end) {
fprintf(stderr, "Invalid priority: %s\n", optarg);
exit(1);
}
}
break;
case 't':
{
char *end;
unsigned long cycle_time_us = strtoul(optarg, &end, 10);
if (!*optarg || *end || cycle_time_us > 1000000) {
fprintf(stderr, "Invalid cycle time: %s\n", optarg);
exit(1);
}
tsample_ns = cycle_time_us * 1000;
}
break;
case 'h':
usage(stdout, argv[0]);
exit(0);
case '?':
usage(stderr, argv[0]);
exit(1);
default:
break;
}
}
while (c != -1);
arg_count = argc - optind;
if (arg_count) {
fprintf(stderr, "%s takes no arguments!\n", argv[0]);
usage(stderr, argv[0]);
exit(1);
}
}
int limit_test(const struct pdvariable* param,
void *dst, const void* src, size_t len,
struct timespec *time, void* priv_data)
{
double value = *(double*)src;
double limit = *(double*)priv_data;
(void)time;
(void)param;
if (value > limit || value < -limit)
return -EINVAL;
memcpy(dst, src, len);
clock_gettime(CLOCK_REALTIME, time);
return 0;
}
void lock_fn(int lock, void* priv_data)
{
if (lock)
pthread_mutex_lock(priv_data);
else
pthread_mutex_unlock(priv_data);
}
int init_pdserv()
{
struct pdvariable* var;
if (!(pdserv =
pdserv_create(
"PdServ Example",
"1.234", gettime))) {
fprintf(stderr, "Failed to init pdserv.");
return -1;
}
fprintf(stderr, "Failed to create task.");
return -1;
}
if (config) {
}
0666,
pd_double_T, &omega, 1, 0, limit_test, &omega_limit);
0666,
pd_double_T, matrix,
sizeof(matrix_dim) /
sizeof(
size_t),
matrix_dim, 0, 0);
"Derivative of [cos,sin]");
{
static const char *text[] = {
"Event message 1",
"Event message 2",
"Event message 3",
"Event message 4",
"Event message 5",
};
}
if (ret) {
fprintf(stderr, "Failed to prepare PdServ.\n");
return -1;
}
return 0;
}
void *cyclic_task(__attribute__((unused)) void *data)
{
double exec_time, cycle_time;
unsigned int overruns = 0;
struct timespec monotonic_time, world_time;
struct timespec start_time, end_time, last_start_time;
fprintf(stderr, "Starting cyclic thread with a period of %u µs.\n",
tsample_ns / 1000);
clock_gettime(CLOCK_MONOTONIC, &monotonic_time);
last_start_time = monotonic_time;
while (1) {
clock_gettime(CLOCK_MONOTONIC, &start_time);
clock_gettime(CLOCK_REALTIME, &world_time);
pthread_mutex_lock(&mutex);
if (reset) {
cosine = amplitude_set;
sine = 0.0;
}
else if (enable) {
amplitude = cosine * cosine + sine * sine;
ampl_modulation = 1.0 / 3.1415
* (amplitude / amplitude_set / amplitude_set - 1);
if (ampl_modulation > 1.0) {
ampl_modulation = 1.0;
}
derivative[0] = -omega * sine - ampl_modulation * cosine;
derivative[1] = omega * cosine - ampl_modulation * sine;
cosine += 1.0e-9 * tsample_ns * derivative[0];
sine += 1.0e-9 * tsample_ns * derivative[1];
if (cosine > ampl_limit) cosine = ampl_limit;
if (cosine < -ampl_limit) cosine = -ampl_limit;
if (sine > ampl_limit) sine = ampl_limit;
if (sine < -ampl_limit) sine = -ampl_limit;
}
if (!--decimation_counter) {
decimation_counter = 10;
++counter;
}
pthread_mutex_unlock(&mutex);
cycle_time = 1.0e-9 * DIFF_NS(last_start_time, start_time);
exec_time = 1.0e-9 * DIFF_NS(last_start_time, end_time);
last_start_time = start_time;
timer_add(&monotonic_time, tsample_ns);
clock_gettime(CLOCK_MONOTONIC, &end_time);
overruns += DIFF_NS(monotonic_time, end_time) > 0;
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &monotonic_time, 0);
}
return NULL;
}
int main(int argc, char **argv)
{
pthread_attr_t attr;
pthread_t thread;
int ret = 0;
get_options(argc, argv);
ret = init_pdserv();
if (ret) {
fprintf(stderr, "Failed to init PdServ.\n");
goto out;
}
ret = mlockall(MCL_CURRENT | MCL_FUTURE);
if (ret) {
fprintf(stderr, "mlockall() failed: %m\n");
}
ret = pthread_attr_init(&attr);
if (ret) {
fprintf(stderr, "init pthread attributes failed\n");
goto out_pdserv;
}
ret = pthread_attr_setstacksize(&attr,
PTHREAD_STACK_MIN + MAX_SAFE_STACK);
if (ret) {
fprintf(stderr, "pthread setstacksize failed\n");
goto out_pdserv;
}
if (priority > -1) {
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (ret) {
fprintf(stderr, "pthread setschedpolicy failed\n");
goto out_pdserv;
}
struct sched_param param;
param.sched_priority = priority;
ret = pthread_attr_setschedparam(&attr, ¶m);
if (ret) {
fprintf(stderr, "pthread setschedparam failed\n");
goto out_pdserv;
}
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret) {
fprintf(stderr, "pthread setinheritsched failed\n");
goto out_pdserv;
}
}
ret = pthread_create(&thread, &attr, cyclic_task, NULL);
if (ret) {
fprintf(stderr, "pthread create failed: %s\n", strerror(ret));
goto out_pdserv;
}
ret = pthread_setname_np(thread, "pdserv-rt");
if (ret) {
fprintf(stderr, "Failed to set thread name: %s\n", strerror(ret));
}
ret = pthread_join(thread, NULL);
if (ret) {
fprintf(stderr, "Join pthread failed: %m\n");
}
out_pdserv:
out:
pthread_mutex_destroy(&mutex);
return ret;
}