#include "rtapi.h"
#include "rtapi_app.h"
#include "rtapi_string.h"
#include "hal.h"
#define MAX_CHAN 8
MODULE_AUTHOR("John Kasunich");
MODULE_DESCRIPTION("Simulated Encoder for EMC HAL");
MODULE_LICENSE("GPL");
static int num_chan;
static int default_num_chan = 1;
RTAPI_MP_INT(num_chan, "number of 'sim_encoders'");
static int howmany;
static char *names[MAX_CHAN] = {0,};
RTAPI_MP_ARRAY_STRING(names, MAX_CHAN, "names of sim_encoder");
typedef struct {
signed long addval;
unsigned long accum;
signed char state;
long cycle;
hal_bit_t *phaseA;
hal_bit_t *phaseB;
hal_bit_t *phaseZ;
hal_u32_t *ppr;
hal_float_t *scale;
hal_float_t *speed;
hal_s32_t *rawcounts;
double old_scale;
double scale_mult;
} sim_enc_t;
static sim_enc_t *sim_enc_array;
static int comp_id;
static long periodns;
static long old_periodns;
static double periodfp;
static double freqscale;
static double maxf;
static int export_sim_enc(sim_enc_t * addr, char *prefix);
static void make_pulses(void *arg, long period);
static void update_speed(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,
"SIM_ENCODER: ERROR: invalid number of channels %d\n", howmany);
return -1;
}
periodns = 50000;
periodfp = periodns * 0.000000001;
maxf = 1.0 / periodfp;
freqscale = ((1L << 30) * 2.0) / maxf;
old_periodns = periodns;
comp_id = hal_init("sim_encoder");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "SIM_ENCODER: ERROR: hal_init() failed\n");
return -1;
}
sim_enc_array = hal_malloc(howmany * sizeof(sim_enc_t));
if (sim_enc_array == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"SIM_ENCODER: 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), "sim-encoder.%d", n);
retval = export_sim_enc(&(sim_enc_array[n]),buf);
} else {
retval = export_sim_enc(&(sim_enc_array[n]),names[i++]);
}
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"SIM_ENCODER: ERROR: 'encoder' %d var export failed\n", n);
hal_exit(comp_id);
return -1;
}
}
retval = hal_export_funct("sim-encoder.make-pulses", make_pulses,
sim_enc_array, 0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"SIM_ENCODER: ERROR: makepulses funct export failed\n");
hal_exit(comp_id);
return -1;
}
retval = hal_export_funct("sim-encoder.update-speed", update_speed,
sim_enc_array, 1, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"SIM_ENCODER: ERROR: speed update funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,
"SIM_ENCODER: installed %d simulated encoders\n", howmany);
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void)
{
hal_exit(comp_id);
}
static void make_pulses(void *arg, long period)
{
sim_enc_t *sim_enc;
int n, overunder, dir;
periodns = period;
sim_enc = arg;
for (n = 0; n < howmany; n++) {
overunder = sim_enc->accum >> 31;
sim_enc->accum += sim_enc->addval;
overunder ^= sim_enc->accum >> 31;
if ( overunder ) {
dir = sim_enc->addval >> 31;
if ( dir ) {
(*sim_enc->rawcounts) --;
if (--(sim_enc->state) < 0) {
sim_enc->state = 3;
if (--(sim_enc->cycle) < 0) {
sim_enc->cycle += *(sim_enc->ppr);
}
}
} else {
(*sim_enc->rawcounts) ++;
if (++(sim_enc->state) > 3) {
sim_enc->state = 0;
if (++(sim_enc->cycle) >= *(sim_enc->ppr)) {
sim_enc->cycle -= *(sim_enc->ppr);
}
}
}
}
switch (sim_enc->state) {
case 0:
*(sim_enc->phaseA) = 1;
*(sim_enc->phaseB) = 0;
break;
case 1:
*(sim_enc->phaseA) = 1;
*(sim_enc->phaseB) = 1;
break;
case 2:
*(sim_enc->phaseA) = 0;
*(sim_enc->phaseB) = 1;
break;
case 3:
*(sim_enc->phaseA) = 0;
*(sim_enc->phaseB) = 0;
break;
default:
sim_enc->state = 0;
}
if ((sim_enc->state == 0) && (sim_enc->cycle == 0)) {
*(sim_enc->phaseZ) = 1;
} else {
*(sim_enc->phaseZ) = 0;
}
sim_enc++;
}
}
static void update_speed(void *arg, long period)
{
sim_enc_t *sim_enc;
int n;
double rev_sec, freq;
if (periodns != old_periodns) {
periodfp = periodns * 0.000000001;
maxf = 1.0 / periodfp;
freqscale = ((1L << 30) * 2.0) / maxf;
old_periodns = periodns;
}
sim_enc = arg;
for (n = 0; n < howmany; n++) {
if ( *(sim_enc->scale) != sim_enc->old_scale ) {
sim_enc->old_scale = *(sim_enc->scale);
if ((*(sim_enc->scale) < 1e-20) && (*(sim_enc->scale) > -1e-20)) {
*(sim_enc->scale) = 1.0;
}
sim_enc->scale_mult = 1.0 / *(sim_enc->scale);
}
rev_sec = *(sim_enc->speed) * sim_enc->scale_mult;
freq = rev_sec * (*(sim_enc->ppr)) * 4.0;
if (freq > maxf) {
freq = maxf;
} else if (freq < -maxf) {
freq = -maxf;
}
sim_enc->addval = freq * freqscale;
sim_enc++;
}
}
static int export_sim_enc(sim_enc_t * addr, char *prefix)
{
int retval, msg;
msg = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
retval = hal_pin_u32_newf(HAL_IO, &(addr->ppr), comp_id,
"%s.ppr", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IO, &(addr->scale), comp_id,
"%s.scale", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->speed), comp_id,
"%s.speed", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_OUT, &(addr->phaseA), comp_id,
"%s.phase-A", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_OUT, &(addr->phaseB), comp_id,
"%s.phase-B", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_OUT, &(addr->phaseZ), comp_id,
"%s.phase-Z", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_s32_newf(HAL_IN, &(addr->rawcounts), comp_id,
"%s.rawcounts", prefix);
if (retval != 0) {
return retval;
}
*(addr->ppr) = 100;
*(addr->scale) = 1.0;
addr->old_scale = 0.0;
addr->scale_mult = 1.0;
addr->accum = 0;
addr->addval = 0;
addr->state = 0;
addr->cycle = 0;
*(addr->phaseA) = 0;
*(addr->phaseB) = 0;
*(addr->phaseZ) = 0;
rtapi_set_msg_level(msg);
return 0;
}