#include "rtapi.h"
#include "rtapi_app.h"
#include "rtapi_string.h"
#include "hal.h"
MODULE_AUTHOR("John Kasunich");
MODULE_DESCRIPTION("PID Loop Component for EMC HAL");
MODULE_LICENSE("GPL");
static int num_chan;
static int default_num_chan = 3;
RTAPI_MP_INT(num_chan, "number of channels");
static int howmany;
#define MAX_CHAN 16
char *names[MAX_CHAN] ={0,};
RTAPI_MP_ARRAY_STRING(names, MAX_CHAN,"pid names");
static int debug = 0;
RTAPI_MP_INT(debug, "enables optional params");
typedef struct {
hal_bit_t *enable;
hal_float_t *command;
hal_float_t *commandvds;
hal_float_t *commandv;
hal_float_t *feedback;
hal_float_t *feedbackvds;
hal_float_t *feedbackv;
hal_float_t *error;
hal_float_t *deadband;
hal_float_t *maxerror;
hal_float_t *maxerror_i;
hal_float_t *maxerror_d;
hal_float_t *maxcmd_d;
hal_float_t *maxcmd_dd;
hal_float_t *maxcmd_ddd;
hal_float_t *error_i;
double prev_error;
hal_float_t *error_d;
double prev_cmd;
double prev_fb;
double limit_state;
hal_float_t *cmd_d;
hal_float_t *cmd_dd;
hal_float_t *cmd_ddd;
hal_float_t *bias;
hal_float_t *pgain;
hal_float_t *igain;
hal_float_t *dgain;
hal_float_t *ff0gain;
hal_float_t *ff1gain;
hal_float_t *ff2gain;
hal_float_t *ff3gain;
hal_float_t *maxoutput;
hal_float_t *output;
hal_bit_t *saturated;
hal_float_t *saturated_s;
hal_s32_t *saturated_count;
hal_bit_t *index_enable;
hal_bit_t *error_previous_target;
char prev_ie;
} hal_pid_t;
static hal_pid_t *pid_array;
static int comp_id;
static int export_pid(hal_pid_t * addr,char * prefix);
static void calc_pid(void *arg, long period);
int rtapi_app_main(void)
{
int n, retval,i;
if(num_chan && names[0]) {
rtapi_print_msg(RTAPI_MSG_ERR,"num_chan= and names= are mutually exclusive\n");
return -EINVAL;
}
if(!num_chan && !names[0]) num_chan = default_num_chan;
if(num_chan) {
howmany = num_chan;
} else {
howmany = 0;
for (i = 0; i < MAX_CHAN; i++) {
if ( (names[i] == NULL) || (*names[i] == 0) ){
break;
}
howmany = i + 1;
}
}
if ((howmany <= 0) || (howmany > MAX_CHAN)) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PID: ERROR: invalid number of channels: %d\n", howmany);
return -1;
}
comp_id = hal_init("pid");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "PID: ERROR: hal_init() failed\n");
return -1;
}
pid_array = hal_malloc(howmany * sizeof(hal_pid_t));
if (pid_array == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "PID: ERROR: hal_malloc() failed\n");
hal_exit(comp_id);
return -1;
}
i = 0; for (n = 0; n < howmany; n++) {
if(num_chan) {
char buf[HAL_NAME_LEN + 1];
rtapi_snprintf(buf, sizeof(buf), "pid.%d", n);
retval = export_pid(&(pid_array[n]), buf);
} else {
retval = export_pid(&(pid_array[n]), names[i++]);
}
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PID: ERROR: loop %d var export failed\n", n);
hal_exit(comp_id);
return -1;
}
}
rtapi_print_msg(RTAPI_MSG_INFO, "PID: installed %d PID loops\n",
howmany);
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void)
{
hal_exit(comp_id);
}
static void calc_pid(void *arg, long period)
{
hal_pid_t *pid;
double tmp1, tmp2, tmp3, command, feedback;
int enable;
double periodfp, periodrecip;
pid = arg;
periodfp = period * 0.000000001;
periodrecip = 1.0 / periodfp;
enable = *(pid->enable);
command = *(pid->command);
feedback = *(pid->feedback);
if((!(pid->prev_ie && !*(pid->index_enable))) &&
(*(pid->error_previous_target))) {
tmp1 = pid->prev_cmd - feedback;
} else {
tmp1 = command - feedback;
}
*(pid->error) = tmp1;
if (*(pid->maxerror) != 0.0) {
if (tmp1 > *(pid->maxerror)) {
tmp1 = *(pid->maxerror);
} else if (tmp1 < -*(pid->maxerror)) {
tmp1 = -*(pid->maxerror);
}
}
if (tmp1 > *(pid->deadband)) {
tmp1 -= *(pid->deadband);
} else if (tmp1 < -*(pid->deadband)) {
tmp1 += *(pid->deadband);
} else {
tmp1 = 0;
}
if (enable != 0) {
if ( ( tmp1 * pid->limit_state ) <= 0.0 ) {
*(pid->error_i) += tmp1 * periodfp;
}
if (*(pid->maxerror_i) != 0.0) {
if (*(pid->error_i) > *(pid->maxerror_i)) {
*(pid->error_i) = *(pid->maxerror_i);
} else if (*(pid->error_i) < -*(pid->maxerror_i)) {
*(pid->error_i) = -*(pid->maxerror_i);
}
}
} else {
*(pid->error_i) = 0;
}
if(!(pid->prev_ie && !*(pid->index_enable))) {
*(pid->commandvds) = (command - pid->prev_cmd) * periodrecip;
*(pid->feedbackvds) = (feedback - pid->prev_fb) * periodrecip;
}
*(pid->error_d) = *(pid->commandv) - *(pid->feedbackv);
pid->prev_error = tmp1;
if (*(pid->maxerror_d) != 0.0) {
if (*(pid->error_d) > *(pid->maxerror_d)) {
*(pid->error_d) = *(pid->maxerror_d);
} else if (*(pid->error_d) < -*(pid->maxerror_d)) {
*(pid->error_d) = -*(pid->maxerror_d);
}
}
tmp2 = *(pid->cmd_d);
*(pid->cmd_d) = *(pid->commandv);
pid->prev_ie = *(pid->index_enable);
pid->prev_cmd = command;
pid->prev_fb = feedback;
if (*(pid->maxcmd_d) != 0.0) {
if (*(pid->cmd_d) > *(pid->maxcmd_d)) {
*(pid->cmd_d) = *(pid->maxcmd_d);
} else if (*(pid->cmd_d) < -*(pid->maxcmd_d)) {
*(pid->cmd_d) = -*(pid->maxcmd_d);
}
}
tmp3 = *(pid->cmd_dd);
*(pid->cmd_dd) = (*(pid->cmd_d) - tmp2) * periodrecip;
if (*(pid->maxcmd_dd) != 0.0) {
if (*(pid->cmd_dd) > *(pid->maxcmd_dd)) {
*(pid->cmd_dd) = *(pid->maxcmd_dd);
} else if (*(pid->cmd_dd) < -*(pid->maxcmd_dd)) {
*(pid->cmd_dd) = -*(pid->maxcmd_dd);
}
}
*(pid->cmd_ddd) = (*(pid->cmd_dd) - tmp3) * periodrecip;
if (*(pid->maxcmd_ddd) != 0.0) {
if (*(pid->cmd_ddd) > *(pid->maxcmd_ddd)) {
*(pid->cmd_ddd) = *(pid->maxcmd_ddd);
} else if (*(pid->cmd_ddd) < -*(pid->maxcmd_ddd)) {
*(pid->cmd_ddd) = -*(pid->maxcmd_ddd);
}
}
if (enable != 0) {
tmp1 =
*(pid->bias) + *(pid->pgain) * tmp1 + *(pid->igain) * *(pid->error_i) +
*(pid->dgain) * *(pid->error_d);
tmp1 += command * *(pid->ff0gain) + *(pid->cmd_d) * *(pid->ff1gain) +
*(pid->cmd_dd) * *(pid->ff2gain) + *(pid->cmd_ddd) * *(pid->ff3gain);
if (*(pid->maxoutput) != 0.0) {
if (tmp1 > *(pid->maxoutput)) {
tmp1 = *(pid->maxoutput);
pid->limit_state = 1.0;
} else if (tmp1 < -*(pid->maxoutput)) {
tmp1 = -*(pid->maxoutput);
pid->limit_state = -1.0;
} else {
pid->limit_state = 0.0;
}
}
} else {
tmp1 = 0.0;
pid->limit_state = 0.0;
}
*(pid->output) = tmp1;
if(pid->limit_state) {
*(pid->saturated) = 1;
*(pid->saturated_s) += period * 1e-9;
if(*(pid->saturated_count) != 2147483647)
(*pid->saturated_count) ++;
} else {
*(pid->saturated) = 0;
*(pid->saturated_s) = 0;
*(pid->saturated_count) = 0;
}
}
static int export_pid(hal_pid_t * addr, char * prefix)
{
int retval, msg;
char buf[HAL_NAME_LEN + 1];
msg = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
retval = hal_pin_bit_newf(HAL_IN, &(addr->enable), comp_id,
"%s.enable", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->command), comp_id,
"%s.command", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->commandv), comp_id,
"%s.command-deriv", prefix);
if (retval != 0) {
return retval;
}
addr->commandvds = addr->commandv;
retval = hal_pin_float_newf(HAL_IN, &(addr->feedback), comp_id,
"%s.feedback", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->feedbackv), comp_id,
"%s.feedback-deriv", prefix);
if (retval != 0) {
return retval;
}
addr->feedbackvds = addr->feedbackv;
retval = hal_pin_float_newf(HAL_OUT, &(addr->error), comp_id,
"%s.error", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->output), comp_id,
"%s.output", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_OUT, &(addr->saturated), comp_id,
"%s.saturated", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->saturated_s), comp_id,
"%s.saturated-s", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_s32_newf(HAL_OUT, &(addr->saturated_count), comp_id,
"%s.saturated-count", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->pgain), comp_id,
"%s.Pgain", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->igain), comp_id,
"%s.Igain", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->dgain), comp_id,
"%s.Dgain", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->ff0gain), comp_id,
"%s.FF0", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->ff1gain), comp_id,
"%s.FF1", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->ff2gain), comp_id,
"%s.FF2", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->ff3gain), comp_id,
"%s.FF3", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->deadband), comp_id,
"%s.deadband", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->maxerror), comp_id,
"%s.maxerror", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->maxerror_i), comp_id,
"%s.maxerrorI", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->maxerror_d), comp_id,
"%s.maxerrorD", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->maxcmd_d), comp_id,
"%s.maxcmdD", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->maxcmd_dd), comp_id,
"%s.maxcmdDD", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->maxcmd_ddd), comp_id,
"%s.maxcmdDDD", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->bias), comp_id,
"%s.bias", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->maxoutput), comp_id,
"%s.maxoutput", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->index_enable), comp_id,
"%s.index-enable", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->error_previous_target), comp_id,
"%s.error-previous-target", prefix);
if (retval != 0) {
return retval;
}
if (debug > 0) {
retval = hal_pin_float_newf(HAL_OUT, &(addr->error_i), comp_id,
"%s.errorI", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->error_d), comp_id,
"%s.errorD", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->cmd_d), comp_id,
"%s.commandD", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->cmd_dd), comp_id,
"%s.commandDD", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->cmd_ddd), comp_id,
"%s.commandDDD", prefix);
if (retval != 0) {
return retval;
}
} else {
addr->error_i = (hal_float_t *) hal_malloc(sizeof(hal_float_t));
addr->error_d = (hal_float_t *) hal_malloc(sizeof(hal_float_t));
addr->cmd_d = (hal_float_t *) hal_malloc(sizeof(hal_float_t));
addr->cmd_dd = (hal_float_t *) hal_malloc(sizeof(hal_float_t));
addr->cmd_ddd = (hal_float_t *) hal_malloc(sizeof(hal_float_t));
}
*(addr->error_i) = 0.0;
*(addr->error_d) = 0.0;
*(addr->cmd_d) = 0.0;
*(addr->cmd_dd) = 0.0;
*(addr->cmd_ddd) = 0.0;
*(addr->enable) = 0;
*(addr->error_previous_target) = 1;
*(addr->command) = 0;
*(addr->feedback) = 0;
*(addr->error) = 0;
*(addr->output) = 0;
*(addr->deadband) = 0.0;
*(addr->maxerror) = 0.0;
*(addr->maxerror_i) = 0.0;
*(addr->maxerror_d) = 0.0;
*(addr->maxcmd_d) = 0.0;
*(addr->maxcmd_dd) = 0.0;
*(addr->maxcmd_ddd) = 0.0;
addr->prev_error = 0.0;
addr->prev_cmd = 0.0;
addr->limit_state = 0.0;
*(addr->bias) = 0.0;
*(addr->pgain) = 1.0;
*(addr->igain) = 0.0;
*(addr->dgain) = 0.0;
*(addr->ff0gain) = 0.0;
*(addr->ff1gain) = 0.0;
*(addr->ff2gain) = 0.0;
*(addr->ff3gain) = 0.0;
*(addr->maxoutput) = 0.0;
rtapi_snprintf(buf, sizeof(buf), "%s.do-pid-calcs", prefix);
retval =
hal_export_funct(buf, calc_pid, addr, 1, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PID: ERROR: do_pid_calcs funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_set_msg_level(msg);
return 0;
}