mirror of
https://github.com/eddyem/small_tel.git
synced 2026-03-24 10:40:56 +03:00
Compare commits
20 Commits
581cdc60f9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f2e893f1a | |||
|
|
6ea6bad008 | ||
|
|
ddce8437ce | ||
|
|
6c36b0acc9 | ||
| fa305a7cc0 | |||
| 205a190820 | |||
| 7c2aaf1cb0 | |||
| 54778dcf9a | |||
|
|
31752b58cc | ||
| 09642743a6 | |||
| a80347643f | |||
|
|
50cbaea550 | ||
| c8449c916a | |||
| 74d9ebb75f | |||
|
|
2e9241f079 | ||
| 2aa8502796 | |||
| f6edc12b01 | |||
| 27a7388164 | |||
| 730629e933 | |||
| b77cb2277b |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -23,3 +23,6 @@
|
|||||||
*.so
|
*.so
|
||||||
*.so.*
|
*.so.*
|
||||||
|
|
||||||
|
# build dirs
|
||||||
|
mk/
|
||||||
|
build/
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ static glob_pars const Gdefault = {
|
|||||||
* Define command line options by filling structure:
|
* Define command line options by filling structure:
|
||||||
* name has_arg flag val type argptr help
|
* name has_arg flag val type argptr help
|
||||||
*/
|
*/
|
||||||
static myoption cmdlnopts[] = {
|
static sl_option_t cmdlnopts[] = {
|
||||||
// common options
|
// common options
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||||
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name (default: )" DEFAULT_DEV ")")},
|
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name (default: )" DEFAULT_DEV ")")},
|
||||||
@@ -75,10 +75,10 @@ glob_pars *parse_args(int argc, char **argv){
|
|||||||
char helpstring[1024], *hptr = helpstring;
|
char helpstring[1024], *hptr = helpstring;
|
||||||
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||||
// format of help: "Usage: progname [args]\n"
|
// format of help: "Usage: progname [args]\n"
|
||||||
change_helpstring(helpstring);
|
sl_helpstring(helpstring);
|
||||||
// parse arguments
|
// parse arguments
|
||||||
parseargs(&argc, &argv, cmdlnopts);
|
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||||
if(help) showhelp(-1, cmdlnopts);
|
if(help) sl_showhelp(-1, cmdlnopts);
|
||||||
if(argc > 0){
|
if(argc > 0){
|
||||||
WARNX("Ignore %d unknown parameters: ");
|
WARNX("Ignore %d unknown parameters: ");
|
||||||
for (i = 0; i < argc; i++)
|
for (i = 0; i < argc; i++)
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ static void dumpRchanges(rg11 *new, rg11 *old){
|
|||||||
int start = 1;
|
int start = 1;
|
||||||
for(int i = 0; i < RREGNUM; ++i){
|
for(int i = 0; i < RREGNUM; ++i){
|
||||||
if(o[i] != n[i]){
|
if(o[i] != n[i]){
|
||||||
sl_putlogt(start, globlog, LOGLEVEL_MSG, "%s=%d", regname(i), n[i]);
|
sl_putlogt(start, sl_globlog, LOGLEVEL_MSG, "%s=%d", regname(i), n[i]);
|
||||||
DBG("%s=%d", regname(i), n[i]);
|
DBG("%s=%d", regname(i), n[i]);
|
||||||
if(start) start = 0;
|
if(start) start = 0;
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ static void dumpRchanges(rg11 *new, rg11 *old){
|
|||||||
uint8_t f = 1;
|
uint8_t f = 1;
|
||||||
for(int i = 0; i < RGBITNUM; ++i, f <<= 1){
|
for(int i = 0; i < RGBITNUM; ++i, f <<= 1){
|
||||||
if(xOr & f){
|
if(xOr & f){
|
||||||
sl_putlogt(start, globlog, LOGLEVEL_MSG, "%s=%d", rgbitname(i), (new->RGBits & f) ? 1 : 0);
|
sl_putlogt(start, sl_globlog, LOGLEVEL_MSG, "%s=%d", rgbitname(i), (new->RGBits & f) ? 1 : 0);
|
||||||
DBG("%s=%d", rgbitname(i), (new->RGBits & f) ? 1 : 0);
|
DBG("%s=%d", rgbitname(i), (new->RGBits & f) ? 1 : 0);
|
||||||
if(start) start = 0;
|
if(start) start = 0;
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ static void dumpSchanges(slowregs *new, slowregs *old){
|
|||||||
int start = 1;
|
int start = 1;
|
||||||
for(int i = 0; i < SREGNUM; ++i){
|
for(int i = 0; i < SREGNUM; ++i){
|
||||||
if(o[i] != n[i]){
|
if(o[i] != n[i]){
|
||||||
sl_putlogt(start, globlog, LOGLEVEL_MSG, "%s=%d", slowname(i), n[i]);
|
sl_putlogt(start, sl_globlog, LOGLEVEL_MSG, "%s=%d", slowname(i), n[i]);
|
||||||
DBG("%s=%d", slowname(i), n[i]);
|
DBG("%s=%d", slowname(i), n[i]);
|
||||||
if(start) start = 0;
|
if(start) start = 0;
|
||||||
}
|
}
|
||||||
@@ -147,12 +147,12 @@ static void puttotable(rg11 *R, slowregs *S){
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
initial_setup();
|
sl_init();
|
||||||
char *self = strdup(argv[0]);
|
char *self = strdup(argv[0]);
|
||||||
G = parse_args(argc, argv);
|
G = parse_args(argc, argv);
|
||||||
if(G->timeout < 5) ERRX("Timeout should be not less than 5 seconds");
|
if(G->timeout < 5) ERRX("Timeout should be not less than 5 seconds");
|
||||||
if(!G->logfile && !G->outfile) ERRX("Point at least log or output file name");
|
if(!G->logfile && !G->outfile) ERRX("Point at least log or output file name");
|
||||||
check4running(self, G->pidfile);
|
sl_check4running(self, G->pidfile);
|
||||||
if(!hydreon_open(G->device)) return 1;
|
if(!hydreon_open(G->device)) return 1;
|
||||||
if(G->logfile) OPENLOG(G->logfile, LOGLEVEL_ANY, 0);
|
if(G->logfile) OPENLOG(G->logfile, LOGLEVEL_ANY, 0);
|
||||||
if(G->outfile){
|
if(G->outfile){
|
||||||
@@ -166,9 +166,9 @@ int main(int argc, char **argv){
|
|||||||
signal(SIGINT, signals); // ctrl+C - quit
|
signal(SIGINT, signals); // ctrl+C - quit
|
||||||
signal(SIGQUIT, signals); // ctrl+\ - quit
|
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||||
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||||
double t0 = dtime();
|
double t0 = sl_dtime();
|
||||||
puttotable(NULL, NULL);
|
puttotable(NULL, NULL);
|
||||||
while(dtime() - t0 < (double)G->timeout){ // dump only changes
|
while(sl_dtime() - t0 < (double)G->timeout){ // dump only changes
|
||||||
if(!hydreon_getpacket(&Rregs, &Sregs)) continue;
|
if(!hydreon_getpacket(&Rregs, &Sregs)) continue;
|
||||||
int changes = FALSE;
|
int changes = FALSE;
|
||||||
if(memcmp(&Rregs, &oRregs, RREGNUM + 1)){ // Rregs changed -> log changes
|
if(memcmp(&Rregs, &oRregs, RREGNUM + 1)){ // Rregs changed -> log changes
|
||||||
@@ -182,7 +182,7 @@ int main(int argc, char **argv){
|
|||||||
changes = TRUE;
|
changes = TRUE;
|
||||||
}
|
}
|
||||||
if(changes) puttotable(&Rregs, &Sregs);
|
if(changes) puttotable(&Rregs, &Sregs);
|
||||||
t0 = dtime();
|
t0 = sl_dtime();
|
||||||
}
|
}
|
||||||
signals(-1); // never reached
|
signals(-1); // never reached
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
#include "hydreon.h"
|
#include "hydreon.h"
|
||||||
|
|
||||||
static TTY_descr *dev = NULL;
|
static sl_tty_t *dev = NULL;
|
||||||
|
|
||||||
// regular registers names
|
// regular registers names
|
||||||
static const char* rregnames[RREGNUM] = {
|
static const char* rregnames[RREGNUM] = {
|
||||||
@@ -121,7 +121,7 @@ int hydreon_getpacket(rg11 *Rregs, slowregs *Sregs){
|
|||||||
if(!dev) return 0;
|
if(!dev) return 0;
|
||||||
static int buflen = 0;
|
static int buflen = 0;
|
||||||
static char strbuf[BUFLEN];
|
static char strbuf[BUFLEN];
|
||||||
int l = read_tty(dev);
|
int l = sl_tty_read(dev);
|
||||||
if(l < 1) return FALSE;
|
if(l < 1) return FALSE;
|
||||||
char s = dev->buf[0];
|
char s = dev->buf[0];
|
||||||
if(s == 's'){ // start of new packet -> encode old
|
if(s == 's'){ // start of new packet -> encode old
|
||||||
@@ -146,13 +146,13 @@ int hydreon_getpacket(rg11 *Rregs, slowregs *Sregs){
|
|||||||
* @return TRUE or FALSE if failed
|
* @return TRUE or FALSE if failed
|
||||||
*/
|
*/
|
||||||
int hydreon_open(const char *devname){
|
int hydreon_open(const char *devname){
|
||||||
dev = new_tty((char*)devname, 1200, 1);
|
dev = sl_tty_new((char*)devname, 1200, 1);
|
||||||
if(!dev) return FALSE;
|
if(!dev) return FALSE;
|
||||||
dev = tty_open(dev, 1);
|
dev = sl_tty_open(dev, 1);
|
||||||
if(!dev) return FALSE;
|
if(!dev) return FALSE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hydreon_close(){
|
void hydreon_close(){
|
||||||
if(dev) close_tty(&dev);
|
if(dev) sl_tty_close(&dev);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ set(CMAKE_COLOR_MAKEFILE ON)
|
|||||||
|
|
||||||
option(DEBUG "Compile in debug mode" OFF)
|
option(DEBUG "Compile in debug mode" OFF)
|
||||||
option(EXAMPLES "Compile also some examples" ON)
|
option(EXAMPLES "Compile also some examples" ON)
|
||||||
|
option(BUILD_SHARED "Build shared libarary" OFF)
|
||||||
|
|
||||||
|
|
||||||
# cmake -DDEBUG=on -> debugging
|
# cmake -DDEBUG=on -> debugging
|
||||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
@@ -52,7 +54,13 @@ aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
|||||||
#list(APPEND ${PROJ}_LIBRARIES "-lfftw3_threads")
|
#list(APPEND ${PROJ}_LIBRARIES "-lfftw3_threads")
|
||||||
|
|
||||||
# library
|
# library
|
||||||
add_library(${PROJ} SHARED ${SOURCES})
|
|
||||||
|
if(BUILD_SHARED)
|
||||||
|
add_library(${PROJ} SHARED ${SOURCES})
|
||||||
|
else()
|
||||||
|
add_library(${PROJ} STATIC ${SOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
# library header files
|
# library header files
|
||||||
set(LIBHEADER "sidservo.h")
|
set(LIBHEADER "sidservo.h")
|
||||||
# -I
|
# -I
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -25,33 +26,70 @@
|
|||||||
#include "PID.h"
|
#include "PID.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
PIDController_t *pid_create(const PIDpar_t *gain, size_t Iarrsz){
|
typedef struct {
|
||||||
|
PIDpar_t gain; // PID gains
|
||||||
|
double prev_error; // Previous error
|
||||||
|
double prev_tagpos; // previous target position
|
||||||
|
double integral; // Integral term
|
||||||
|
double *pidIarray; // array for Integral
|
||||||
|
struct timespec prevT; // time of previous correction
|
||||||
|
size_t pidIarrSize; // it's size
|
||||||
|
size_t curIidx; // and index of current element
|
||||||
|
} PIDController_t;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
axis_status_t state;
|
||||||
|
coordval_t position;
|
||||||
|
coordval_t speed;
|
||||||
|
} axisdata_t;
|
||||||
|
|
||||||
|
static PIDController_t *pid_create(const PIDpar_t *gain, size_t Iarrsz){
|
||||||
if(!gain || Iarrsz < 3) return NULL;
|
if(!gain || Iarrsz < 3) return NULL;
|
||||||
PIDController_t *pid = (PIDController_t*)calloc(1, sizeof(PIDController_t));
|
PIDController_t *pid = (PIDController_t*)calloc(1, sizeof(PIDController_t));
|
||||||
pid->gain = *gain;
|
pid->gain = *gain;
|
||||||
|
DBG("Created PID with P=%g, I=%g, D=%g\n", gain->P, gain->I, gain->D);
|
||||||
pid->pidIarrSize = Iarrsz;
|
pid->pidIarrSize = Iarrsz;
|
||||||
pid->pidIarray = (double*)calloc(Iarrsz, sizeof(double));
|
pid->pidIarray = (double*)calloc(Iarrsz, sizeof(double));
|
||||||
|
curtime(&pid->prevT);
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't clear lastT!
|
// don't clear lastT!
|
||||||
void pid_clear(PIDController_t *pid){
|
static void pid_clear(PIDController_t *pid){
|
||||||
if(!pid) return;
|
if(!pid) return;
|
||||||
DBG("CLEAR PID PARAMETERS");
|
DBG("CLEAR PID PARAMETERS");
|
||||||
bzero(pid->pidIarray, sizeof(double) * pid->pidIarrSize);
|
bzero(pid->pidIarray, sizeof(double) * pid->pidIarrSize);
|
||||||
pid->integral = 0.;
|
pid->integral = 0.;
|
||||||
pid->prev_error = 0.;
|
pid->prev_error = 0.;
|
||||||
pid->curIidx = 0;
|
pid->curIidx = 0;
|
||||||
|
curtime(&pid->prevT);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
void pid_delete(PIDController_t **pid){
|
static void pid_delete(PIDController_t **pid){
|
||||||
if(!pid || !*pid) return;
|
if(!pid || !*pid) return;
|
||||||
if((*pid)->pidIarray) free((*pid)->pidIarray);
|
if((*pid)->pidIarray) free((*pid)->pidIarray);
|
||||||
free(*pid);
|
free(*pid);
|
||||||
*pid = NULL;
|
*pid = NULL;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
double pid_calculate(PIDController_t *pid, double error, double dt){
|
// calculate new motor speed
|
||||||
|
static double pid_calculate(PIDController_t *pid, double axispos, const coordval_t *target){
|
||||||
|
double dtpid = timediff(&target->t, &pid->prevT);
|
||||||
|
if(dtpid < 0 || dtpid > Conf.PIDMaxDt){
|
||||||
|
DBG("time diff too big: clear PID");
|
||||||
|
pid_clear(pid);
|
||||||
|
pid->prev_tagpos = target->val;
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
double dt = timediff(&target->t, &pid->prevT);
|
||||||
|
if(dt < FLT_EPSILON){
|
||||||
|
DBG("Target time in past");
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
pid->prevT = target->t;
|
||||||
|
double error = target->val - axispos;
|
||||||
|
double tagspeed = (target->val - pid->prev_tagpos) / dt;
|
||||||
|
pid->prev_tagpos = target->val;
|
||||||
// calculate flowing integral
|
// calculate flowing integral
|
||||||
double oldi = pid->pidIarray[pid->curIidx], newi = error * dt;
|
double oldi = pid->pidIarray[pid->curIidx], newi = error * dt;
|
||||||
//DBG("oldi/new: %g, %g", oldi, newi);
|
//DBG("oldi/new: %g, %g", oldi, newi);
|
||||||
@@ -60,72 +98,62 @@ double pid_calculate(PIDController_t *pid, double error, double dt){
|
|||||||
pid->integral += newi - oldi;
|
pid->integral += newi - oldi;
|
||||||
double derivative = (error - pid->prev_error) / dt;
|
double derivative = (error - pid->prev_error) / dt;
|
||||||
pid->prev_error = error;
|
pid->prev_error = error;
|
||||||
double sum = pid->gain.P * error + pid->gain.I * pid->integral + pid->gain.D * derivative;
|
DBG("pid pars: P=%g, I=%g, D=%f", pid->gain.P, pid->gain.I, pid->gain.D);
|
||||||
DBG("P=%g, I=%g, D=%g; sum=%g", pid->gain.P * error, pid->gain.I * pid->integral, pid->gain.D * derivative, sum);
|
double sum = pid->gain.P * error + pid->gain.I * pid->integral + pid->gain.D * derivative + tagspeed;
|
||||||
|
DBG("tagspeed=%g, P=%g, I=%g, D=%g; sum=%g", tagspeed, pid->gain.P * error,
|
||||||
|
pid->gain.I * pid->integral, pid->gain.D * derivative, sum);
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct{
|
|
||||||
PIDController_t *PIDC;
|
|
||||||
PIDController_t *PIDV;
|
|
||||||
} PIDpair_t;
|
|
||||||
|
|
||||||
typedef struct{
|
|
||||||
axis_status_t state;
|
|
||||||
coordval_t position;
|
|
||||||
coordval_t speed;
|
|
||||||
} axisdata_t;
|
|
||||||
/**
|
/**
|
||||||
* @brief process - Process PID for given axis
|
* @brief process - Process PID for given axis
|
||||||
* @param tagpos - given coordinate of target position
|
* @param tagpos - given coordinate of target position
|
||||||
* @param endpoint - endpoint for this coordinate
|
* @param endpoint - endpoint for this coordinate
|
||||||
* @param pid - pid itself
|
* @param pid - pid itself
|
||||||
* @return calculated new speed or -1 for max speed
|
* @return calculated NEW SPEED or NAN for max speed
|
||||||
*/
|
*/
|
||||||
static double getspeed(const coordval_t *tagpos, PIDpair_t *pidpair, axisdata_t *axis){
|
static double getspeed(const coordval_t *tagpos, PIDController_t *pid, axisdata_t *axis){
|
||||||
if(tagpos->t < axis->position.t || tagpos->t - axis->position.t > MCC_PID_MAX_DT){
|
double dt = timediff(&tagpos->t, &axis->position.t);
|
||||||
DBG("target time: %g, axis time: %g - too big! (%g)", tagpos->t, axis->position.t, MCC_PID_MAX_DT);
|
if(dt < 0 || dt > Conf.PIDMaxDt){
|
||||||
|
DBG("target time: %ld, axis time: %ld - too big! (tag-ax=%g)", tagpos->t.tv_sec, axis->position.t.tv_sec, dt);
|
||||||
return axis->speed.val; // data is too old or wrong
|
return axis->speed.val; // data is too old or wrong
|
||||||
}
|
}
|
||||||
double error = tagpos->val - axis->position.val, fe = fabs(error);
|
double error = tagpos->val - axis->position.val, fe = fabs(error);
|
||||||
PIDController_t *pid = NULL;
|
DBG("error: %g'', cur speed: %g (deg/s)", error * 180. * 3600. / M_PI, axis->speed.val*180./M_PI);
|
||||||
switch(axis->state){
|
switch(axis->state){
|
||||||
case AXIS_SLEWING:
|
case AXIS_SLEWING:
|
||||||
if(fe < MCC_MAX_POINTING_ERR){
|
if(fe < Conf.MaxFinePointingErr){
|
||||||
axis->state = AXIS_POINTING;
|
axis->state = AXIS_POINTING;
|
||||||
DBG("--> Pointing");
|
DBG("--> Pointing");
|
||||||
pid = pidpair->PIDC;
|
|
||||||
}else{
|
}else{
|
||||||
DBG("Slewing...");
|
DBG("Slewing...");
|
||||||
return -1.; // max speed for given axis
|
return NAN; // max speed for given axis
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AXIS_POINTING:
|
case AXIS_POINTING:
|
||||||
if(fe < MCC_MAX_GUIDING_ERR){
|
if(fe < Conf.MaxFinePointingErr){
|
||||||
axis->state = AXIS_GUIDING;
|
axis->state = AXIS_GUIDING;
|
||||||
DBG("--> Guiding");
|
DBG("--> Guiding");
|
||||||
pid = pidpair->PIDV;
|
}else if(fe > Conf.MaxPointingErr){
|
||||||
}else if(fe > MCC_MAX_POINTING_ERR){
|
|
||||||
DBG("--> Slewing");
|
DBG("--> Slewing");
|
||||||
axis->state = AXIS_SLEWING;
|
axis->state = AXIS_SLEWING;
|
||||||
return -1.;
|
return NAN;
|
||||||
} else pid = pidpair->PIDC;
|
}
|
||||||
break;
|
break;
|
||||||
case AXIS_GUIDING:
|
case AXIS_GUIDING:
|
||||||
pid = pidpair->PIDV;
|
if(fe > Conf.MaxFinePointingErr){
|
||||||
if(fe > MCC_MAX_GUIDING_ERR){
|
|
||||||
DBG("--> Pointing");
|
DBG("--> Pointing");
|
||||||
axis->state = AXIS_POINTING;
|
axis->state = AXIS_POINTING;
|
||||||
pid = pidpair->PIDC;
|
}else if(fe < Conf.MaxGuidingErr){
|
||||||
}else if(fe < MCC_MAX_ATTARGET_ERR){
|
|
||||||
DBG("At target");
|
DBG("At target");
|
||||||
// TODO: we can point somehow that we are at target or introduce new axis state
|
// TODO: we can point somehow that we are at target or introduce new axis state
|
||||||
}else DBG("Current error: %g", fe);
|
}else DBG("Current abs error: %g", fe);
|
||||||
break;
|
break;
|
||||||
|
case AXIS_GONNASTOP:
|
||||||
case AXIS_STOPPED: // start pointing to target; will change speed next time
|
case AXIS_STOPPED: // start pointing to target; will change speed next time
|
||||||
DBG("AXIS STOPPED!!!!");
|
DBG("AXIS STOPPED!!!! --> Slewing");
|
||||||
axis->state = AXIS_SLEWING;
|
axis->state = AXIS_SLEWING;
|
||||||
return -1.;
|
return getspeed(tagpos, pid, axis);
|
||||||
case AXIS_ERROR:
|
case AXIS_ERROR:
|
||||||
DBG("Can't move from erroneous state");
|
DBG("Can't move from erroneous state");
|
||||||
return 0.;
|
return 0.;
|
||||||
@@ -134,17 +162,7 @@ static double getspeed(const coordval_t *tagpos, PIDpair_t *pidpair, axisdata_t
|
|||||||
DBG("WTF? Where is a PID?");
|
DBG("WTF? Where is a PID?");
|
||||||
return axis->speed.val;
|
return axis->speed.val;
|
||||||
}
|
}
|
||||||
if(tagpos->t < pid->prevT || tagpos->t - pid->prevT > MCC_PID_MAX_DT){
|
return pid_calculate(pid, axis->position.val, tagpos);
|
||||||
DBG("time diff too big: clear PID");
|
|
||||||
pid_clear(pid);
|
|
||||||
}
|
|
||||||
double dt = tagpos->t - pid->prevT;
|
|
||||||
if(dt > MCC_PID_MAX_DT) dt = MCC_PID_CYCLE_TIME;
|
|
||||||
pid->prevT = tagpos->t;
|
|
||||||
//DBG("CALC PID (er=%g, dt=%g)", error, dt);
|
|
||||||
double tagspeed = pid_calculate(pid, error, dt);
|
|
||||||
if(axis->state == AXIS_GUIDING) return axis->speed.val + tagspeed; // velocity-based
|
|
||||||
return tagspeed; // coordinate-based
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,41 +171,73 @@ static double getspeed(const coordval_t *tagpos, PIDpair_t *pidpair, axisdata_t
|
|||||||
* @param endpoint - stop point (some far enough point to stop in case of hang)
|
* @param endpoint - stop point (some far enough point to stop in case of hang)
|
||||||
* @return error code
|
* @return error code
|
||||||
*/
|
*/
|
||||||
mcc_errcodes_t correct2(const coordval_pair_t *target, const coordpair_t *endpoint){
|
mcc_errcodes_t correct2(const coordval_pair_t *target){
|
||||||
static PIDpair_t pidX = {0}, pidY = {0};
|
static PIDController_t *pidX = NULL, *pidY = NULL;
|
||||||
if(!pidX.PIDC){
|
if(!pidX){
|
||||||
pidX.PIDC = pid_create(&Conf.XPIDC, MCC_PID_CYCLE_TIME / MCC_PID_REFRESH_DT);
|
pidX = pid_create(&Conf.XPIDV, Conf.PIDCycleDt / Conf.PIDRefreshDt);
|
||||||
if(!pidX.PIDC) return MCC_E_FATAL;
|
if(!pidX) return MCC_E_FATAL;
|
||||||
pidX.PIDV = pid_create(&Conf.XPIDV, MCC_PID_CYCLE_TIME / MCC_PID_REFRESH_DT);
|
|
||||||
if(!pidX.PIDV) return MCC_E_FATAL;
|
|
||||||
}
|
}
|
||||||
if(!pidY.PIDC){
|
if(!pidY){
|
||||||
pidY.PIDC = pid_create(&Conf.YPIDC, MCC_PID_CYCLE_TIME / MCC_PID_REFRESH_DT);
|
pidY = pid_create(&Conf.YPIDV, Conf.PIDCycleDt / Conf.PIDRefreshDt);
|
||||||
if(!pidY.PIDC) return MCC_E_FATAL;
|
if(!pidY) return MCC_E_FATAL;
|
||||||
pidY.PIDV = pid_create(&Conf.YPIDV, MCC_PID_CYCLE_TIME / MCC_PID_REFRESH_DT);
|
|
||||||
if(!pidY.PIDV) return MCC_E_FATAL;
|
|
||||||
}
|
}
|
||||||
mountdata_t m;
|
mountdata_t m;
|
||||||
coordpair_t tagspeed;
|
coordpair_t tagspeed; // absolute value of speed
|
||||||
|
double Xsign = 1., Ysign = 1.; // signs of speed (for target calculation)
|
||||||
if(MCC_E_OK != Mount.getMountData(&m)) return MCC_E_FAILED;
|
if(MCC_E_OK != Mount.getMountData(&m)) return MCC_E_FAILED;
|
||||||
axisdata_t axis;
|
axisdata_t axis;
|
||||||
DBG("state: %d/%d", m.Xstate, m.Ystate);
|
DBG("state: %d/%d", m.Xstate, m.Ystate);
|
||||||
axis.state = m.Xstate;
|
axis.state = m.Xstate;
|
||||||
axis.position = m.encXposition;
|
axis.position = m.encXposition;
|
||||||
axis.speed = m.encXspeed;
|
axis.speed = m.encXspeed;
|
||||||
tagspeed.X = getspeed(&target->X, &pidX, &axis);
|
tagspeed.X = getspeed(&target->X, pidX, &axis);
|
||||||
if(tagspeed.X < 0. || tagspeed.X > MCC_MAX_X_SPEED) tagspeed.X = MCC_MAX_X_SPEED;
|
if(isnan(tagspeed.X)){ // max speed
|
||||||
|
if(target->X.val < axis.position.val) Xsign = -1.;
|
||||||
|
tagspeed.X = Xlimits.max.speed;
|
||||||
|
}else{
|
||||||
|
if(tagspeed.X < 0.){ tagspeed.X = -tagspeed.X; Xsign = -1.; }
|
||||||
|
if(tagspeed.X > Xlimits.max.speed) tagspeed.X = Xlimits.max.speed;
|
||||||
|
}
|
||||||
axis_status_t xstate = axis.state;
|
axis_status_t xstate = axis.state;
|
||||||
axis.state = m.Ystate;
|
axis.state = m.Ystate;
|
||||||
axis.position = m.encYposition;
|
axis.position = m.encYposition;
|
||||||
axis.speed = m.encYspeed;
|
axis.speed = m.encYspeed;
|
||||||
tagspeed.Y = getspeed(&target->Y, &pidY, &axis);
|
tagspeed.Y = getspeed(&target->Y, pidY, &axis);
|
||||||
if(tagspeed.Y < 0. || tagspeed.Y > MCC_MAX_Y_SPEED) tagspeed.Y = MCC_MAX_Y_SPEED;
|
if(isnan(tagspeed.Y)){ // max speed
|
||||||
|
if(target->Y.val < axis.position.val) Ysign = -1.;
|
||||||
|
tagspeed.Y = Ylimits.max.speed;
|
||||||
|
}else{
|
||||||
|
if(tagspeed.Y < 0.){ tagspeed.Y = -tagspeed.Y; Ysign = -1.; }
|
||||||
|
if(tagspeed.Y > Ylimits.max.speed) tagspeed.Y = Ylimits.max.speed;
|
||||||
|
}
|
||||||
axis_status_t ystate = axis.state;
|
axis_status_t ystate = axis.state;
|
||||||
if(m.Xstate != xstate || m.Ystate != ystate){
|
if(m.Xstate != xstate || m.Ystate != ystate){
|
||||||
DBG("State changed");
|
DBG("State changed");
|
||||||
setStat(xstate, ystate);
|
setStat(xstate, ystate);
|
||||||
}
|
}
|
||||||
DBG("TAG speeds: %g/%g", tagspeed.X, tagspeed.Y);
|
coordpair_t endpoint;
|
||||||
return Mount.moveWspeed(endpoint, &tagspeed);
|
#if 0
|
||||||
|
// allow at least PIDMaxDt moving with target speed
|
||||||
|
double dv = fabs(tagspeed.X - m.encXspeed.val);
|
||||||
|
double adder = dv/Xlimits.max.accel * (m.encXspeed.val + dv / 2.) // distanse with changing speed
|
||||||
|
+ Conf.PIDMaxDt * tagspeed.X // PIDMaxDt const speed moving
|
||||||
|
+ tagspeed.X * tagspeed.X / Xlimits.max.accel / 2.; // stopping
|
||||||
|
endpoint.X = m.encXposition.val + Xsign * adder;
|
||||||
|
dv = fabs(tagspeed.Y - m.encYspeed.val);
|
||||||
|
adder = dv/Ylimits.max.accel * (m.encYspeed.val + dv / 2.)
|
||||||
|
+ Conf.PIDMaxDt * tagspeed.Y
|
||||||
|
+ tagspeed.Y * tagspeed.Y / Ylimits.max.accel / 2.;
|
||||||
|
endpoint.Y = m.encYposition.val + Ysign * adder;
|
||||||
|
#endif
|
||||||
|
// allow 10s moving but not more than 10deg and not less than 1deg
|
||||||
|
double adder = fabs(tagspeed.X) * 10.;
|
||||||
|
if(adder > 0.17453) adder = 0.17453;
|
||||||
|
else if(adder < 0.017453) adder = 0.017453;
|
||||||
|
endpoint.X = m.encXposition.val + Xsign * adder;
|
||||||
|
adder = fabs(tagspeed.Y) * 10.;
|
||||||
|
if(adder > 0.17453) adder = 0.17453;
|
||||||
|
else if(adder < 0.017453) adder = 0.017453;
|
||||||
|
endpoint.Y = m.encYposition.val + Ysign * adder;
|
||||||
|
DBG("TAG speeds: %g/%g (deg/s); TAG pos: %g/%g (deg)", tagspeed.X/M_PI*180., tagspeed.Y/M_PI*180., endpoint.X/M_PI*180., endpoint.Y/M_PI*180.);
|
||||||
|
return Mount.moveWspeed(&endpoint, &tagspeed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,19 +22,10 @@
|
|||||||
|
|
||||||
#include "sidservo.h"
|
#include "sidservo.h"
|
||||||
|
|
||||||
typedef struct {
|
/*
|
||||||
PIDpar_t gain; // PID gains
|
|
||||||
double prev_error; // Previous error
|
|
||||||
double integral; // Integral term
|
|
||||||
double *pidIarray; // array for Integral
|
|
||||||
double prevT; // time of previous correction
|
|
||||||
size_t pidIarrSize; // it's size
|
|
||||||
size_t curIidx; // and index of current element
|
|
||||||
} PIDController_t;
|
|
||||||
|
|
||||||
PIDController_t *pid_create(const PIDpar_t *gain, size_t Iarrsz);
|
PIDController_t *pid_create(const PIDpar_t *gain, size_t Iarrsz);
|
||||||
void pid_clear(PIDController_t *pid);
|
void pid_clear(PIDController_t *pid);
|
||||||
void pid_delete(PIDController_t **pid);
|
void pid_delete(PIDController_t **pid);
|
||||||
double pid_calculate(PIDController_t *pid, double error, double dt);
|
double pid_calculate(PIDController_t *pid, double error, double dt);
|
||||||
|
*/
|
||||||
mcc_errcodes_t correct2(const coordval_pair_t *target, const coordpair_t *endpoint);
|
mcc_errcodes_t correct2(const coordval_pair_t *target);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
Dramp.c
|
Dramp.c
|
||||||
Dramp.h
|
Dramp.h
|
||||||
|
PID.c
|
||||||
|
PID.h
|
||||||
Sramp.c
|
Sramp.c
|
||||||
Sramp.h
|
Sramp.h
|
||||||
Tramp.c
|
Tramp.c
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
1. PID: slew2
|
fix encoders opening for several tries
|
||||||
2. add model & config "model ON"
|
encoderthread2() - change main cycle (remove pause, read data independently, ask for new only after timeout after last request)
|
||||||
|
Read HW config even in model mode
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ typedef struct{
|
|||||||
|
|
||||||
static hardware_configuration_t HW = {0};
|
static hardware_configuration_t HW = {0};
|
||||||
|
|
||||||
static parameters G = {0};
|
static parameters G = {
|
||||||
|
.conffile = "servo.conf",
|
||||||
|
};
|
||||||
|
|
||||||
static sl_option_t cmdlnopts[] = {
|
static sl_option_t cmdlnopts[] = {
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||||
@@ -53,7 +55,7 @@ static sl_option_t confopts[] = {
|
|||||||
|
|
||||||
static void dumpaxis(char axis, axis_config_t *c){
|
static void dumpaxis(char axis, axis_config_t *c){
|
||||||
#define STRUCTPAR(p) (c)->p
|
#define STRUCTPAR(p) (c)->p
|
||||||
#define DUMP(par) do{printf("%c%s=%g\n", axis, #par, STRUCTPAR(par));}while(0)
|
#define DUMP(par) do{printf("%c%s=%.10g\n", axis, #par, STRUCTPAR(par));}while(0)
|
||||||
#define DUMPD(par) do{printf("%c%s=%g\n", axis, #par, RAD2DEG(STRUCTPAR(par)));}while(0)
|
#define DUMPD(par) do{printf("%c%s=%g\n", axis, #par, RAD2DEG(STRUCTPAR(par)));}while(0)
|
||||||
DUMPD(accel);
|
DUMPD(accel);
|
||||||
DUMPD(backlash);
|
DUMPD(backlash);
|
||||||
@@ -64,6 +66,8 @@ static void dumpaxis(char axis, axis_config_t *c){
|
|||||||
DUMP(outplimit);
|
DUMP(outplimit);
|
||||||
DUMP(currlimit);
|
DUMP(currlimit);
|
||||||
DUMP(intlimit);
|
DUMP(intlimit);
|
||||||
|
DUMP(motor_stepsperrev);
|
||||||
|
DUMP(axis_stepsperrev);
|
||||||
#undef DUMP
|
#undef DUMP
|
||||||
#undef DUMPD
|
#undef DUMPD
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,25 +24,32 @@
|
|||||||
static conf_t Config = {
|
static conf_t Config = {
|
||||||
.MountDevPath = "/dev/ttyUSB0",
|
.MountDevPath = "/dev/ttyUSB0",
|
||||||
.MountDevSpeed = 19200,
|
.MountDevSpeed = 19200,
|
||||||
.EncoderXDevPath = "/dev/encoderX0",
|
.EncoderXDevPath = "/dev/encoder_X0",
|
||||||
.EncoderYDevPath = "/dev/encoderY0",
|
.EncoderYDevPath = "/dev/encoder_Y0",
|
||||||
.EncoderDevSpeed = 153000,
|
.EncoderDevSpeed = 153000,
|
||||||
.MountReqInterval = 0.1,
|
.MountReqInterval = 0.1,
|
||||||
.EncoderReqInterval = 0.05,
|
.EncoderReqInterval = 0.001,
|
||||||
.SepEncoder = 2,
|
.SepEncoder = 2,
|
||||||
.EncoderSpeedInterval = 0.1,
|
.EncoderSpeedInterval = 0.05,
|
||||||
.XPIDC.P = 0.8,
|
.EncodersDisagreement = 1e-5, // 2''
|
||||||
|
.PIDMaxDt = 1.,
|
||||||
|
.PIDRefreshDt = 0.1,
|
||||||
|
.PIDCycleDt = 5.,
|
||||||
|
.XPIDC.P = 0.5,
|
||||||
.XPIDC.I = 0.1,
|
.XPIDC.I = 0.1,
|
||||||
.XPIDC.D = 0.3,
|
.XPIDC.D = 0.2,
|
||||||
.XPIDV.P = 1.,
|
.XPIDV.P = 0.09,
|
||||||
.XPIDV.I = 0.01,
|
.XPIDV.I = 0.0,
|
||||||
.XPIDV.D = 0.2,
|
.XPIDV.D = 0.05,
|
||||||
.YPIDC.P = 0.8,
|
.YPIDC.P = 0.5,
|
||||||
.YPIDC.I = 0.1,
|
.YPIDC.I = 0.1,
|
||||||
.YPIDC.D = 0.3,
|
.YPIDC.D = 0.2,
|
||||||
.YPIDV.P = 0.5,
|
.YPIDV.P = 0.09,
|
||||||
.YPIDV.I = 0.2,
|
.YPIDV.I = 0.0,
|
||||||
.YPIDV.D = 0.5,
|
.YPIDV.D = 0.05,
|
||||||
|
.MaxPointingErr = 0.13962634,
|
||||||
|
.MaxFinePointingErr = 0.026179939,
|
||||||
|
.MaxGuidingErr = 4.8481368e-7,
|
||||||
};
|
};
|
||||||
|
|
||||||
static sl_option_t opts[] = {
|
static sl_option_t opts[] = {
|
||||||
@@ -50,13 +57,17 @@ static sl_option_t opts[] = {
|
|||||||
{"MountDevSpeed", NEED_ARG, NULL, 0, arg_int, APTR(&Config.MountDevSpeed), "serial speed of mount device"},
|
{"MountDevSpeed", NEED_ARG, NULL, 0, arg_int, APTR(&Config.MountDevSpeed), "serial speed of mount device"},
|
||||||
{"EncoderDevPath", NEED_ARG, NULL, 0, arg_string, APTR(&Config.EncoderDevPath), "path to encoder device"},
|
{"EncoderDevPath", NEED_ARG, NULL, 0, arg_string, APTR(&Config.EncoderDevPath), "path to encoder device"},
|
||||||
{"EncoderDevSpeed", NEED_ARG, NULL, 0, arg_int, APTR(&Config.EncoderDevSpeed), "serial speed of encoder device"},
|
{"EncoderDevSpeed", NEED_ARG, NULL, 0, arg_int, APTR(&Config.EncoderDevSpeed), "serial speed of encoder device"},
|
||||||
{"MountReqInterval",NEED_ARG, NULL, 0, arg_double, APTR(&Config.MountReqInterval), "interval of mount requests (not less than 0.05s)"},
|
{"SepEncoder", NEED_ARG, NULL, 0, arg_int, APTR(&Config.SepEncoder), "encoder is separate device (1 - one device, 2 - two devices)"},
|
||||||
{"EncoderReqInterval",NEED_ARG, NULL, 0, arg_double, APTR(&Config.EncoderReqInterval),"interval of encoder requests (in case of sep=2)"},
|
|
||||||
{"SepEncoder", NO_ARGS, NULL, 0, arg_int, APTR(&Config.SepEncoder), "encoder is separate device (1 - one device, 2 - two devices)"},
|
|
||||||
{"EncoderXDevPath", NEED_ARG, NULL, 0, arg_string, APTR(&Config.EncoderXDevPath), "path to X encoder (/dev/encoderX0)"},
|
{"EncoderXDevPath", NEED_ARG, NULL, 0, arg_string, APTR(&Config.EncoderXDevPath), "path to X encoder (/dev/encoderX0)"},
|
||||||
{"EncoderYDevPath", NEED_ARG, NULL, 0, arg_string, APTR(&Config.EncoderYDevPath), "path to Y encoder (/dev/encoderY0)"},
|
{"EncoderYDevPath", NEED_ARG, NULL, 0, arg_string, APTR(&Config.EncoderYDevPath), "path to Y encoder (/dev/encoderY0)"},
|
||||||
|
{"EncodersDisagreement", NEED_ARG,NULL, 0, arg_double, APTR(&Config.EncodersDisagreement),"acceptable disagreement between motor and axis encoders"},
|
||||||
|
{"MountReqInterval",NEED_ARG, NULL, 0, arg_double, APTR(&Config.MountReqInterval), "interval of mount requests (not less than 0.05s)"},
|
||||||
|
{"EncoderReqInterval",NEED_ARG, NULL, 0, arg_double, APTR(&Config.EncoderReqInterval),"interval of encoder requests (in case of sep=2)"},
|
||||||
{"EncoderSpeedInterval", NEED_ARG,NULL, 0, arg_double, APTR(&Config.EncoderSpeedInterval),"interval of speed calculations, s"},
|
{"EncoderSpeedInterval", NEED_ARG,NULL, 0, arg_double, APTR(&Config.EncoderSpeedInterval),"interval of speed calculations, s"},
|
||||||
{"RunModel", NEED_ARG, NULL, 0, arg_int, APTR(&Config.RunModel), "instead of real hardware run emulation"},
|
{"RunModel", NEED_ARG, NULL, 0, arg_int, APTR(&Config.RunModel), "instead of real hardware run emulation"},
|
||||||
|
{"PIDMaxDt", NEED_ARG, NULL, 0, arg_double, APTR(&Config.PIDMaxDt), "maximal PID refresh time interval (if larger all old data will be cleared)"},
|
||||||
|
{"PIDRefreshDt", NEED_ARG, NULL, 0, arg_double, APTR(&Config.PIDRefreshDt), "normal PID refresh interval by master process"},
|
||||||
|
{"PIDCycleDt", NEED_ARG, NULL, 0, arg_double, APTR(&Config.PIDCycleDt), "PID I cycle time (analog of \"RC\" for PID on opamps)"},
|
||||||
{"XPIDCP", NEED_ARG, NULL, 0, arg_double, APTR(&Config.XPIDC.P), "P of X PID (coordinate driven)"},
|
{"XPIDCP", NEED_ARG, NULL, 0, arg_double, APTR(&Config.XPIDC.P), "P of X PID (coordinate driven)"},
|
||||||
{"XPIDCI", NEED_ARG, NULL, 0, arg_double, APTR(&Config.XPIDC.I), "I of X PID (coordinate driven)"},
|
{"XPIDCI", NEED_ARG, NULL, 0, arg_double, APTR(&Config.XPIDC.I), "I of X PID (coordinate driven)"},
|
||||||
{"XPIDCD", NEED_ARG, NULL, 0, arg_double, APTR(&Config.XPIDC.D), "D of X PID (coordinate driven)"},
|
{"XPIDCD", NEED_ARG, NULL, 0, arg_double, APTR(&Config.XPIDC.D), "D of X PID (coordinate driven)"},
|
||||||
@@ -69,6 +80,12 @@ static sl_option_t opts[] = {
|
|||||||
{"YPIDVP", NEED_ARG, NULL, 0, arg_double, APTR(&Config.YPIDV.P), "P of Y PID (velocity driven)"},
|
{"YPIDVP", NEED_ARG, NULL, 0, arg_double, APTR(&Config.YPIDV.P), "P of Y PID (velocity driven)"},
|
||||||
{"YPIDVI", NEED_ARG, NULL, 0, arg_double, APTR(&Config.YPIDV.I), "I of Y PID (velocity driven)"},
|
{"YPIDVI", NEED_ARG, NULL, 0, arg_double, APTR(&Config.YPIDV.I), "I of Y PID (velocity driven)"},
|
||||||
{"YPIDVD", NEED_ARG, NULL, 0, arg_double, APTR(&Config.YPIDV.D), "D of Y PID (velocity driven)"},
|
{"YPIDVD", NEED_ARG, NULL, 0, arg_double, APTR(&Config.YPIDV.D), "D of Y PID (velocity driven)"},
|
||||||
|
{"MaxPointingErr", NEED_ARG, NULL, 0, arg_double, APTR(&Config.MaxPointingErr), "if angle < this, change state from \"slewing\" to \"pointing\" (coarse pointing): 8 degrees"},
|
||||||
|
{"MaxFinePointingErr",NEED_ARG, NULL, 0, arg_double, APTR(&Config.MaxFinePointingErr), "if angle < this, chane state from \"pointing\" to \"guiding\" (fine poinging): 1.5 deg"},
|
||||||
|
{"MaxGuidingErr", NEED_ARG, NULL, 0, arg_double, APTR(&Config.MaxGuidingErr), "if error less than this value we suppose that target is captured and guiding is good (true guiding): 0.1''"},
|
||||||
|
{"XEncZero", NEED_ARG, NULL, 0, arg_int, APTR(&Config.XEncZero), "X axis encoder approximate zero position"},
|
||||||
|
{"YEncZero", NEED_ARG, NULL, 0, arg_int, APTR(&Config.YEncZero), "Y axis encoder approximate zero position"},
|
||||||
|
// {"",NEED_ARG, NULL, 0, arg_double, APTR(&Config.), ""},
|
||||||
end_option
|
end_option
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,5 +110,19 @@ void dumpConf(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void confHelp(){
|
void confHelp(){
|
||||||
sl_showhelp(-1, opts);
|
sl_conf_showhelp(-1, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* errcodes[MCC_E_AMOUNT] = {
|
||||||
|
[MCC_E_OK] = "OK",
|
||||||
|
[MCC_E_FATAL] = "Fatal error",
|
||||||
|
[MCC_E_BADFORMAT] = "Wrong data format",
|
||||||
|
[MCC_E_ENCODERDEV] = "Encoder error",
|
||||||
|
[MCC_E_MOUNTDEV] = "Mount error",
|
||||||
|
[MCC_E_FAILED] = "Failed to run"
|
||||||
|
};
|
||||||
|
// return string with error code
|
||||||
|
const char *EcodeStr(mcc_errcodes_t e){
|
||||||
|
if(e >= MCC_E_AMOUNT) return "Wrong error code";
|
||||||
|
return errcodes[e];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,3 +25,4 @@
|
|||||||
void confHelp();
|
void confHelp();
|
||||||
conf_t *readServoConf(const char *filename);
|
conf_t *readServoConf(const char *filename);
|
||||||
void dumpConf();
|
void dumpConf();
|
||||||
|
const char *EcodeStr(mcc_errcodes_t e);
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
#include "dump.h"
|
#include "dump.h"
|
||||||
#include "simpleconv.h"
|
#include "simpleconv.h"
|
||||||
|
|
||||||
|
// starting dump time (to conform different logs)
|
||||||
|
static struct timespec dumpT0 = {0};
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// amount of elements used for encoders' data filtering
|
// amount of elements used for encoders' data filtering
|
||||||
#define NFILT (10)
|
#define NFILT (10)
|
||||||
@@ -59,6 +62,12 @@ static double filter(double val, int idx){
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// return starting time of dump
|
||||||
|
void dumpt0(struct timespec *t){
|
||||||
|
if(t) *t = dumpT0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief logmnt - log mount data into file
|
* @brief logmnt - log mount data into file
|
||||||
* @param fcoords - file to dump
|
* @param fcoords - file to dump
|
||||||
@@ -67,17 +76,13 @@ static double filter(double val, int idx){
|
|||||||
void logmnt(FILE *fcoords, mountdata_t *m){
|
void logmnt(FILE *fcoords, mountdata_t *m){
|
||||||
if(!fcoords) return;
|
if(!fcoords) return;
|
||||||
//DBG("LOG %s", m ? "data" : "header");
|
//DBG("LOG %s", m ? "data" : "header");
|
||||||
static double t0 = -1.;
|
|
||||||
if(!m){ // write header
|
if(!m){ // write header
|
||||||
fprintf(fcoords, "# time Xmot(deg) Ymot(deg) Xenc(deg) Yenc(deg) VX(d/s) VY(d/s) millis\n");
|
fprintf(fcoords, " time Xmot(deg) Ymot(deg) Xenc(deg) Yenc(deg) VX(d/s) VY(d/s) millis\n");
|
||||||
return;
|
return;
|
||||||
}
|
}else if(dumpT0.tv_sec == 0) dumpT0 = m->encXposition.t;
|
||||||
double tnow = (m->encXposition.t + m->encYposition.t) / 2.;
|
|
||||||
if(t0 < 0.) t0 = tnow;
|
|
||||||
double t = tnow - t0;
|
|
||||||
// write data
|
// write data
|
||||||
fprintf(fcoords, "%12.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10u\n",
|
fprintf(fcoords, "%12.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10u\n",
|
||||||
t, RAD2DEG(m->motXposition.val), RAD2DEG(m->motYposition.val),
|
Mount.timeDiff(&m->encXposition.t, &dumpT0), RAD2DEG(m->motXposition.val), RAD2DEG(m->motYposition.val),
|
||||||
RAD2DEG(m->encXposition.val), RAD2DEG(m->encYposition.val),
|
RAD2DEG(m->encXposition.val), RAD2DEG(m->encYposition.val),
|
||||||
RAD2DEG(m->encXspeed.val), RAD2DEG(m->encYspeed.val),
|
RAD2DEG(m->encXspeed.val), RAD2DEG(m->encYspeed.val),
|
||||||
m->millis);
|
m->millis);
|
||||||
@@ -103,16 +108,17 @@ void dumpmoving(FILE *fcoords, double t, int N){
|
|||||||
LOGWARN("Can't get mount data");
|
LOGWARN("Can't get mount data");
|
||||||
}
|
}
|
||||||
uint32_t mdmillis = mdata.millis;
|
uint32_t mdmillis = mdata.millis;
|
||||||
double enct = (mdata.encXposition.t + mdata.encYposition.t) / 2.;
|
struct timespec encXt = mdata.encXposition.t;
|
||||||
int ctr = -1;
|
int ctr = -1;
|
||||||
double xlast = mdata.motXposition.val, ylast = mdata.motYposition.val;
|
double xlast = mdata.motXposition.val, ylast = mdata.motYposition.val;
|
||||||
double t0 = Mount.currentT();
|
double t0 = Mount.timeFromStart();
|
||||||
while(Mount.currentT() - t0 < t && ctr < N){
|
while(Mount.timeFromStart() - t0 < t && ctr < N){
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
if(MCC_E_OK != Mount.getMountData(&mdata)){ WARNX("Can't get data"); continue;}
|
if(MCC_E_OK != Mount.getMountData(&mdata)){ WARNX("Can't get data"); continue;}
|
||||||
double tmsr = (mdata.encXposition.t + mdata.encYposition.t) / 2.;
|
//double tmsr = (mdata.encXposition.t + mdata.encYposition.t) / 2.;
|
||||||
if(tmsr == enct) continue;
|
struct timespec msrt = mdata.encXposition.t;
|
||||||
enct = tmsr;
|
if(msrt.tv_nsec == encXt.tv_nsec) continue;
|
||||||
|
encXt = msrt;
|
||||||
if(fcoords) logmnt(fcoords, &mdata);
|
if(fcoords) logmnt(fcoords, &mdata);
|
||||||
if(mdata.millis == mdmillis) continue;
|
if(mdata.millis == mdmillis) continue;
|
||||||
//DBG("ctr=%d, motpos=%g/%g", ctr, mdata.motXposition.val, mdata.motYposition.val);
|
//DBG("ctr=%d, motpos=%g/%g", ctr, mdata.motXposition.val, mdata.motYposition.val);
|
||||||
@@ -123,7 +129,7 @@ void dumpmoving(FILE *fcoords, double t, int N){
|
|||||||
ctr = 0;
|
ctr = 0;
|
||||||
}else ++ctr;
|
}else ++ctr;
|
||||||
}
|
}
|
||||||
DBG("Exit dumping; tend=%g, tmon=%g", t, Mount.currentT() - t0);
|
DBG("Exit dumping; tend=%g, tmon=%g", t, Mount.timeFromStart() - t0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,17 +140,15 @@ void waitmoving(int N){
|
|||||||
mountdata_t mdata;
|
mountdata_t mdata;
|
||||||
int ctr = -1;
|
int ctr = -1;
|
||||||
uint32_t millis = 0;
|
uint32_t millis = 0;
|
||||||
double xlast = 0., ylast = 0.;
|
//double xlast = 0., ylast = 0.;
|
||||||
|
DBG("Wait moving for %d stopped times", N);
|
||||||
while(ctr < N){
|
while(ctr < N){
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
if(MCC_E_OK != Mount.getMountData(&mdata)){ WARNX("Can't get data"); continue;}
|
if(MCC_E_OK != Mount.getMountData(&mdata)){ WARNX("Can't get data"); continue;}
|
||||||
if(mdata.millis == millis) continue;
|
if(mdata.millis == millis) continue;
|
||||||
millis = mdata.millis;
|
millis = mdata.millis;
|
||||||
if(mdata.motXposition.val != xlast || mdata.motYposition.val != ylast){
|
if(mdata.Xstate != AXIS_STOPPED || mdata.Ystate != AXIS_STOPPED) ctr = 0;
|
||||||
xlast = mdata.motXposition.val;
|
else ++ctr;
|
||||||
ylast = mdata.motYposition.val;
|
|
||||||
ctr = 0;
|
|
||||||
}else ++ctr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,3 +27,4 @@ void dumpmoving(FILE *fcoords, double t, int N);
|
|||||||
void waitmoving(int N);
|
void waitmoving(int N);
|
||||||
int getPos(coordval_pair_t *mot, coordval_pair_t *enc);
|
int getPos(coordval_pair_t *mot, coordval_pair_t *enc);
|
||||||
void chk0(int ncycles);
|
void chk0(int ncycles);
|
||||||
|
void dumpt0(struct timespec *t);
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ int main(int argc, char **argv){
|
|||||||
conf_t *Config = readServoConf(G.conffile);
|
conf_t *Config = readServoConf(G.conffile);
|
||||||
if(!Config){
|
if(!Config){
|
||||||
dumpConf();
|
dumpConf();
|
||||||
|
confHelp();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if(G.coordsoutput){
|
if(G.coordsoutput){
|
||||||
|
|||||||
@@ -139,8 +139,10 @@ static mcc_errcodes_t return2zero(){
|
|||||||
short_command_t cmd = {0};
|
short_command_t cmd = {0};
|
||||||
DBG("Try to move to zero");
|
DBG("Try to move to zero");
|
||||||
cmd.Xmot = 0.; cmd.Ymot = 0.;
|
cmd.Xmot = 0.; cmd.Ymot = 0.;
|
||||||
cmd.Xspeed = MCC_MAX_X_SPEED;
|
coordpair_t maxspd;
|
||||||
cmd.Yspeed = MCC_MAX_Y_SPEED;
|
if(MCC_E_OK != Mount.getMaxSpeed(&maxspd)) return MCC_E_FAILED;
|
||||||
|
cmd.Xspeed = maxspd.X;
|
||||||
|
cmd.Yspeed = maxspd.Y;
|
||||||
/*cmd.xychange = 1;
|
/*cmd.xychange = 1;
|
||||||
cmd.XBits = 100;
|
cmd.XBits = 100;
|
||||||
cmd.YBits = 20;*/
|
cmd.YBits = 20;*/
|
||||||
@@ -216,7 +218,7 @@ int main(int argc, char **argv){
|
|||||||
sleep(5);
|
sleep(5);
|
||||||
// return to zero and wait
|
// return to zero and wait
|
||||||
green("Return 2 zero and wait\n");
|
green("Return 2 zero and wait\n");
|
||||||
if(!return2zero()) ERRX("Can't return");
|
if(MCC_E_OK != return2zero()) ERRX("Can't return");
|
||||||
Wait(0., 0);
|
Wait(0., 0);
|
||||||
Wait(0., 1);
|
Wait(0., 1);
|
||||||
// wait moving ends
|
// wait moving ends
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ void waithalf(double t){
|
|||||||
uint32_t millis = 0;
|
uint32_t millis = 0;
|
||||||
double xlast = 0., ylast = 0.;
|
double xlast = 0., ylast = 0.;
|
||||||
while(ctr < 5){
|
while(ctr < 5){
|
||||||
if(Mount.currentT() >= t) return;
|
if(Mount.timeFromStart() >= t) return;
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
if(MCC_E_OK != Mount.getMountData(&mdata)){ WARNX("Can't get data"); continue;}
|
if(MCC_E_OK != Mount.getMountData(&mdata)){ WARNX("Can't get data"); continue;}
|
||||||
if(mdata.millis == millis) continue;
|
if(mdata.millis == millis) continue;
|
||||||
@@ -110,16 +110,28 @@ int main(int argc, char **argv){
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if(G.coordsoutput){
|
if(G.coordsoutput){
|
||||||
if(!(fcoords = fopen(G.coordsoutput, "w")))
|
if(!(fcoords = fopen(G.coordsoutput, "w"))){
|
||||||
ERRX("Can't open %s", G.coordsoutput);
|
WARNX("Can't open %s", G.coordsoutput);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}else fcoords = stdout;
|
}else fcoords = stdout;
|
||||||
if(G.Ncycles < 7) ERRX("Ncycles should be >7");
|
if(G.Ncycles < 2){
|
||||||
|
WARNX("Ncycles should be >2");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
double absamp = fabs(G.amplitude);
|
double absamp = fabs(G.amplitude);
|
||||||
if(absamp < 0.01 || absamp > 45.)
|
if(absamp < 0.01 || absamp > 45.){
|
||||||
ERRX("Amplitude should be from 0.01 to 45 degrees");
|
WARNX("Amplitude should be from 0.01 to 45 degrees");
|
||||||
if(G.period < 0.1 || G.period > 900.)
|
return 1;
|
||||||
ERRX("Period should be from 0.1 to 900s");
|
}
|
||||||
if(G.Nswings < 1) ERRX("Nswings should be more than 0");
|
if(G.period < 0.1 || G.period > 900.){
|
||||||
|
WARNX("Period should be from 0.1 to 900s");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(G.Nswings < 1){
|
||||||
|
WARNX("Nswings should be more than 0");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
conf_t *Config = readServoConf(G.conffile);
|
conf_t *Config = readServoConf(G.conffile);
|
||||||
if(!Config){
|
if(!Config){
|
||||||
dumpConf();
|
dumpConf();
|
||||||
@@ -146,24 +158,24 @@ int main(int argc, char **argv){
|
|||||||
}else{
|
}else{
|
||||||
tagX = 0.; tagY = DEG2RAD(G.amplitude);
|
tagX = 0.; tagY = DEG2RAD(G.amplitude);
|
||||||
}
|
}
|
||||||
double t = Mount.currentT(), t0 = t;
|
double t = Mount.timeFromStart(), t0 = t;
|
||||||
coordpair_t tag = {.X = tagX, .Y = tagY}, rtag = {.X = -tagX, .Y = -tagY};
|
coordpair_t tag = {.X = tagX, .Y = tagY}, rtag = {.X = -tagX, .Y = -tagY};
|
||||||
double divide = 2.;
|
double divide = 2.;
|
||||||
for(int i = 0; i < G.Nswings; ++i){
|
for(int i = 0; i < G.Nswings; ++i){
|
||||||
Mount.moveTo(&tag);
|
Mount.moveTo(&tag);
|
||||||
DBG("CMD: %g", Mount.currentT()-t0);
|
DBG("CMD: %g", Mount.timeFromStart()-t0);
|
||||||
t += G.period / divide;
|
t += G.period / divide;
|
||||||
divide = 1.;
|
divide = 1.;
|
||||||
waithalf(t);
|
waithalf(t);
|
||||||
DBG("Moved to +, t=%g", t-t0);
|
DBG("Moved to +, t=%g", t-t0);
|
||||||
DBG("CMD: %g", Mount.currentT()-t0);
|
DBG("CMD: %g", Mount.timeFromStart()-t0);
|
||||||
Mount.moveTo(&rtag);
|
Mount.moveTo(&rtag);
|
||||||
t += G.period;
|
t += G.period;
|
||||||
waithalf(t);
|
waithalf(t);
|
||||||
DBG("Moved to -, t=%g", t-t0);
|
DBG("Moved to -, t=%g", t-t0);
|
||||||
DBG("CMD: %g", Mount.currentT()-t0);
|
DBG("CMD: %g", Mount.timeFromStart()-t0);
|
||||||
}
|
}
|
||||||
green("Move to zero @ %g\n", Mount.currentT());
|
green("Move to zero @ %g\n", Mount.timeFromStart());
|
||||||
tag = (coordpair_t){0};
|
tag = (coordpair_t){0};
|
||||||
// be sure to move @ 0,0
|
// be sure to move @ 0,0
|
||||||
if(MCC_E_OK != Mount.moveTo(&tag)){
|
if(MCC_E_OK != Mount.moveTo(&tag)){
|
||||||
|
|||||||
@@ -61,12 +61,13 @@ static FILE* fcoords = NULL;
|
|||||||
static pthread_t dthr;
|
static pthread_t dthr;
|
||||||
|
|
||||||
void signals(int sig){
|
void signals(int sig){
|
||||||
pthread_cancel(dthr);
|
|
||||||
if(sig){
|
if(sig){
|
||||||
signal(sig, SIG_IGN);
|
signal(sig, SIG_IGN);
|
||||||
DBG("Get signal %d, quit.\n", sig);
|
DBG("Get signal %d, quit.\n", sig);
|
||||||
}
|
}
|
||||||
|
DBG("Quit");
|
||||||
Mount.quit();
|
Mount.quit();
|
||||||
|
DBG("close");
|
||||||
if(fcoords) fclose(fcoords);
|
if(fcoords) fclose(fcoords);
|
||||||
exit(sig);
|
exit(sig);
|
||||||
}
|
}
|
||||||
@@ -90,11 +91,10 @@ int main(int _U_ argc, char _U_ **argv){
|
|||||||
if(MCC_E_OK != Mount.init(Config)) ERRX("Can't init mount");
|
if(MCC_E_OK != Mount.init(Config)) ERRX("Can't init mount");
|
||||||
coordval_pair_t M, E;
|
coordval_pair_t M, E;
|
||||||
if(!getPos(&M, &E)) ERRX("Can't get current position");
|
if(!getPos(&M, &E)) ERRX("Can't get current position");
|
||||||
|
printf("Current time: %.10f\n", Mount.timeFromStart());
|
||||||
if(G.coordsoutput){
|
if(G.coordsoutput){
|
||||||
if(!G.wait) green("When logging I should wait until moving ends; added '-w'");
|
if(!G.wait) green("When logging I should wait until moving ends; added '-w'\n");
|
||||||
G.wait = 1;
|
G.wait = 1;
|
||||||
}
|
|
||||||
if(G.coordsoutput){
|
|
||||||
if(!(fcoords = fopen(G.coordsoutput, "w")))
|
if(!(fcoords = fopen(G.coordsoutput, "w")))
|
||||||
ERRX("Can't open %s", G.coordsoutput);
|
ERRX("Can't open %s", G.coordsoutput);
|
||||||
logmnt(fcoords, NULL);
|
logmnt(fcoords, NULL);
|
||||||
@@ -120,7 +120,11 @@ int main(int _U_ argc, char _U_ **argv){
|
|||||||
}
|
}
|
||||||
printf("Moving to X=%gdeg, Y=%gdeg\n", G.X, G.Y);
|
printf("Moving to X=%gdeg, Y=%gdeg\n", G.X, G.Y);
|
||||||
tag.X = DEG2RAD(G.X); tag.Y = DEG2RAD(G.Y);
|
tag.X = DEG2RAD(G.X); tag.Y = DEG2RAD(G.Y);
|
||||||
Mount.moveTo(&tag);
|
mcc_errcodes_t e = Mount.moveTo(&tag);
|
||||||
|
if(MCC_E_OK != e){
|
||||||
|
WARNX("Cant go to given coordinates: %s\n", EcodeStr(e));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
if(G.wait){
|
if(G.wait){
|
||||||
sleep(1);
|
sleep(1);
|
||||||
waitmoving(G.Ncycles);
|
waitmoving(G.Ncycles);
|
||||||
@@ -132,7 +136,9 @@ out:
|
|||||||
if(G.coordsoutput) pthread_join(dthr, NULL);
|
if(G.coordsoutput) pthread_join(dthr, NULL);
|
||||||
DBG("QUIT");
|
DBG("QUIT");
|
||||||
if(G.wait){
|
if(G.wait){
|
||||||
if(getPos(&M, NULL)) printf("Mount position: X=%g, Y=%g\n", RAD2DEG(M.X.val), RAD2DEG(M.Y.val));
|
usleep(250000); // pause to refresh coordinates
|
||||||
|
if(getPos(&M, &E)) printf("Mount position: X=%g, Y=%g; encoders: X=%g, Y=%g\n", RAD2DEG(M.X.val), RAD2DEG(M.Y.val),
|
||||||
|
RAD2DEG(E.X.val), RAD2DEG(E.Y.val));
|
||||||
Mount.quit();
|
Mount.quit();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ typedef struct{
|
|||||||
char *conffile;
|
char *conffile;
|
||||||
} parameters;
|
} parameters;
|
||||||
|
|
||||||
|
static conf_t *Config = NULL;
|
||||||
static FILE *fcoords = NULL, *errlog = NULL;
|
static FILE *fcoords = NULL, *errlog = NULL;
|
||||||
static pthread_t dthr;
|
static pthread_t dthr;
|
||||||
static parameters G = {
|
static parameters G = {
|
||||||
@@ -75,7 +76,6 @@ static sl_option_t cmdlnopts[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void signals(int sig){
|
void signals(int sig){
|
||||||
pthread_cancel(dthr);
|
|
||||||
if(sig){
|
if(sig){
|
||||||
signal(sig, SIG_IGN);
|
signal(sig, SIG_IGN);
|
||||||
DBG("Get signal %d, quit.\n", sig);
|
DBG("Get signal %d, quit.\n", sig);
|
||||||
@@ -97,35 +97,45 @@ static void runtraectory(traectory_fn tfn){
|
|||||||
if(!tfn) return;
|
if(!tfn) return;
|
||||||
coordval_pair_t telXY;
|
coordval_pair_t telXY;
|
||||||
coordval_pair_t target;
|
coordval_pair_t target;
|
||||||
coordpair_t traectXY, endpoint;
|
coordpair_t traectXY;
|
||||||
endpoint.X = G.Xmax, endpoint.Y = G.Ymax;
|
double tlast = 0., tstart = Mount.timeFromStart();
|
||||||
double t0 = Mount.currentT(), tlast = 0.;
|
long tlastXnsec = 0, tlastYnsec = 0;
|
||||||
double tlastX = 0., tlastY = 0.;
|
struct timespec tcur, t0 = {0};
|
||||||
|
dumpt0(&t0);
|
||||||
while(1){
|
while(1){
|
||||||
if(!telpos(&telXY)){
|
if(!telpos(&telXY)){
|
||||||
WARNX("No next telescope position");
|
WARNX("No next telescope position");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(telXY.X.t == tlastX && telXY.Y.t == tlastY) continue; // last measure - don't mind
|
if(!Mount.currentT(&tcur)) continue;
|
||||||
DBG("\n\nTELPOS: %g'/%g' measured @ %g/%g", RAD2AMIN(telXY.X.val), RAD2AMIN(telXY.Y.val), telXY.X.t, telXY.Y.t);
|
if(telXY.X.t.tv_nsec == tlastXnsec && telXY.Y.t.tv_nsec == tlastYnsec) continue; // last measure - don't mind
|
||||||
tlastX = telXY.X.t; tlastY = telXY.Y.t;
|
tlastXnsec = telXY.X.t.tv_nsec; tlastYnsec = telXY.Y.t.tv_nsec;
|
||||||
double t = Mount.currentT();
|
double t = Mount.timeFromStart();
|
||||||
if(fabs(telXY.X.val) > G.Xmax || fabs(telXY.Y.val) > G.Ymax || t - t0 > G.tmax) break;
|
if(fabs(telXY.X.val) > G.Xmax || fabs(telXY.Y.val) > G.Ymax || t - tstart > G.tmax){
|
||||||
if(!traectory_point(&traectXY, t)) break;
|
if(fabs(telXY.X.val) > G.Xmax) DBG("X over maximal limit!");
|
||||||
|
if(fabs(telXY.Y.val) > G.Ymax) DBG("Y over maximal limit!");
|
||||||
|
if(t - tstart > G.tmax) DBG("Time over...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!traectory_point(&traectXY, t)){
|
||||||
|
DBG("Error in 'traectory_point', time from start=%g", t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DBG("\n\nTELPOS: %g'/%g' (%.6f/%.6f); traectory: %g'/%g' (%.6f/%.6f)",
|
||||||
|
RAD2AMIN(telXY.X.val), RAD2AMIN(telXY.Y.val), RAD2DEG(telXY.X.val), RAD2DEG(telXY.Y.val),
|
||||||
|
RAD2AMIN(traectXY.X), RAD2AMIN(traectXY.Y), RAD2DEG(traectXY.X), RAD2DEG(traectXY.Y));
|
||||||
target.X.val = traectXY.X; target.Y.val = traectXY.Y;
|
target.X.val = traectXY.X; target.Y.val = traectXY.Y;
|
||||||
target.X.t = target.Y.t = t;
|
target.X.t = target.Y.t = tcur;
|
||||||
// check whether we should change direction
|
if(t0.tv_nsec == 0 && t0.tv_sec == 0) dumpt0(&t0);
|
||||||
if(telXY.X.val > traectXY.X) endpoint.X = -G.Xmax;
|
else{
|
||||||
else if(telXY.X.val < traectXY.X) endpoint.X = G.Xmax;
|
//DBG("target: %g'/%g'", RAD2AMIN(traectXY.X), RAD2AMIN(traectXY.Y));
|
||||||
if(telXY.Y.val > traectXY.Y) endpoint.Y = -G.Ymax;
|
DBG("%g, target-telescope: dX=%.4f'', dY=%.4f''", t-tstart, RAD2ASEC(traectXY.X-telXY.X.val), RAD2ASEC(traectXY.Y-telXY.Y.val));
|
||||||
else if(telXY.Y.val < traectXY.Y) endpoint.Y = G.Ymax;
|
//DBG("Correct to: %g/%g with EP %g/%g", RAD2DEG(target.X.val), RAD2DEG(target.Y.val), RAD2DEG(endpoint.X), RAD2DEG(endpoint.Y));
|
||||||
DBG("target: %g'/%g'", RAD2AMIN(traectXY.X), RAD2AMIN(traectXY.Y));
|
if(errlog)
|
||||||
DBG("%g: dX=%.4f'', dY=%.4f''", t-t0, RAD2ASEC(traectXY.X-telXY.X.val), RAD2ASEC(traectXY.Y-telXY.Y.val));
|
fprintf(errlog, "%10.4f %10.4f %10.4f\n", Mount.timeDiff(&telXY.X.t, &t0), RAD2ASEC(traectXY.X-telXY.X.val), RAD2ASEC(traectXY.Y-telXY.Y.val));
|
||||||
DBG("Correct to: %g/%g with EP %g/%g", RAD2DEG(target.X.val), RAD2DEG(target.Y.val), RAD2DEG(endpoint.X), RAD2DEG(endpoint.Y));
|
}
|
||||||
if(errlog)
|
if(MCC_E_OK != Mount.correctTo(&target)) WARNX("Error of correction!");
|
||||||
fprintf(errlog, "%10.4g %10.4g %10.4g\n", t, RAD2ASEC(traectXY.X-telXY.X.val), RAD2ASEC(traectXY.Y-telXY.Y.val));
|
while((t = Mount.timeFromStart()) - tlast < Config->PIDRefreshDt) usleep(500);
|
||||||
if(MCC_E_OK != Mount.correctTo(&target, &endpoint)) WARNX("Error of correction!");
|
|
||||||
while((t = Mount.currentT()) - tlast < MCC_PID_REFRESH_DT) usleep(50);
|
|
||||||
tlast = t;
|
tlast = t;
|
||||||
}
|
}
|
||||||
WARNX("No next traectory point or emulation ends");
|
WARNX("No next traectory point or emulation ends");
|
||||||
@@ -146,12 +156,14 @@ int main(int argc, char **argv){
|
|||||||
ERRX("Can't open error log %s", G.errlog);
|
ERRX("Can't open error log %s", G.errlog);
|
||||||
else
|
else
|
||||||
fprintf(errlog, "# time Xerr'' Yerr'' // target - real\n");
|
fprintf(errlog, "# time Xerr'' Yerr'' // target - real\n");
|
||||||
|
setbuf(errlog, NULL);
|
||||||
}
|
}
|
||||||
if(G.coordsoutput){
|
if(G.coordsoutput){
|
||||||
if(!(fcoords = fopen(G.coordsoutput, "w")))
|
if(!(fcoords = fopen(G.coordsoutput, "w")))
|
||||||
ERRX("Can't open %s", G.coordsoutput);
|
ERRX("Can't open %s", G.coordsoutput);
|
||||||
|
setbuf(fcoords, NULL);
|
||||||
}else fcoords = stdout;
|
}else fcoords = stdout;
|
||||||
conf_t *Config = readServoConf(G.conffile);
|
Config = readServoConf(G.conffile);
|
||||||
if(!Config || G.dumpconf){
|
if(!Config || G.dumpconf){
|
||||||
dumpConf();
|
dumpConf();
|
||||||
return 1;
|
return 1;
|
||||||
@@ -164,21 +176,22 @@ int main(int argc, char **argv){
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
coordpair_t c = {.X = DEG2RAD(G.X0), .Y = DEG2RAD(G.Y0)};
|
coordpair_t c = {.X = DEG2RAD(G.X0), .Y = DEG2RAD(G.Y0)};
|
||||||
if(!init_traectory(tfn, &c)){
|
|
||||||
ERRX("Can't init traectory");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
mcc_errcodes_t e = Mount.init(Config);
|
mcc_errcodes_t e = Mount.init(Config);
|
||||||
if(e != MCC_E_OK){
|
if(e != MCC_E_OK){
|
||||||
WARNX("Can't init devices");
|
WARNX("Can't init devices");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
// run this function only after mount inited!
|
||||||
|
if(!init_traectory(tfn, &c)){
|
||||||
|
ERRX("Can't init traectory");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
signal(SIGTERM, signals); // kill (-15) - quit
|
signal(SIGTERM, signals); // kill (-15) - quit
|
||||||
signal(SIGHUP, SIG_IGN); // hup - ignore
|
signal(SIGHUP, SIG_IGN); // hup - ignore
|
||||||
signal(SIGINT, signals); // ctrl+C - quit
|
signal(SIGINT, signals); // ctrl+C - quit
|
||||||
signal(SIGQUIT, signals); // ctrl+\ - quit
|
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||||
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||||
chk0(G.Ncycles);
|
// chk0(G.Ncycles);
|
||||||
logmnt(fcoords, NULL);
|
logmnt(fcoords, NULL);
|
||||||
if(pthread_create(&dthr, NULL, dumping, NULL)) ERRX("Can't run dump thread");
|
if(pthread_create(&dthr, NULL, dumping, NULL)) ERRX("Can't run dump thread");
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Current configuration:
|
# Current configuration
|
||||||
MountDevPath=/dev/ttyUSB0
|
MountDevPath=/dev/ttyUSB0
|
||||||
MountDevSpeed=19200
|
MountDevSpeed=19200
|
||||||
EncoderDevPath=(null)
|
EncoderDevPath=(null)
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ int init_traectory(traectory_fn f, coordpair_t *XY0){
|
|||||||
if(!f || !XY0) return FALSE;
|
if(!f || !XY0) return FALSE;
|
||||||
cur_traectory = f;
|
cur_traectory = f;
|
||||||
XYstart = *XY0;
|
XYstart = *XY0;
|
||||||
tstart = Mount.currentT();
|
tstart = Mount.timeFromStart();
|
||||||
|
DBG("inited @ %gs from start", tstart);
|
||||||
mountdata_t mdata;
|
mountdata_t mdata;
|
||||||
int ntries = 0;
|
int ntries = 0;
|
||||||
for(; ntries < 10; ++ntries){
|
for(; ntries < 10; ++ntries){
|
||||||
@@ -58,11 +59,21 @@ int init_traectory(traectory_fn f, coordpair_t *XY0){
|
|||||||
* @return FALSE if something wrong (e.g. X not in -90..90 or Y not in -180..180)
|
* @return FALSE if something wrong (e.g. X not in -90..90 or Y not in -180..180)
|
||||||
*/
|
*/
|
||||||
int traectory_point(coordpair_t *nextpt, double t){
|
int traectory_point(coordpair_t *nextpt, double t){
|
||||||
if(t < 0. || !cur_traectory) return FALSE;
|
if(t < 0. || !cur_traectory){
|
||||||
|
if(t < 0.) DBG("time in past!");
|
||||||
|
else DBG("no current traectory selected!");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
coordpair_t pt;
|
coordpair_t pt;
|
||||||
if(!cur_traectory(&pt, t)) return FALSE;
|
if(!cur_traectory(&pt, t)){
|
||||||
|
DBG("error in cur_traectory");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
if(nextpt) *nextpt = pt;
|
if(nextpt) *nextpt = pt;
|
||||||
if(pt.X < -M_PI_2 || pt.X > M_PI_2 || pt.Y < -M_PI || pt.Y > M_PI) return FALSE;
|
if(pt.X < -M_PI_2 || pt.X > M_PI_2 || pt.Y < -M_PI || pt.Y > M_PI){
|
||||||
|
DBG("some points is over limits, pt.x=%g, pt.y=%g degrees", RAD2DEG(pt.X), RAD2DEG(pt.Y));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +87,7 @@ int telpos(coordval_pair_t *curpos){
|
|||||||
}
|
}
|
||||||
if(ntries == 10) return FALSE;
|
if(ntries == 10) return FALSE;
|
||||||
coordval_pair_t pt;
|
coordval_pair_t pt;
|
||||||
|
//DBG("\n\nTELPOS: %g'/%g' measured @ %.6f", RAD2AMIN(mdata.encXposition.val), RAD2AMIN(mdata.encYposition.val), mdata.encXposition.t);
|
||||||
pt.X.val = mdata.encXposition.val;
|
pt.X.val = mdata.encXposition.val;
|
||||||
pt.Y.val = mdata.encYposition.val;
|
pt.Y.val = mdata.encYposition.val;
|
||||||
pt.X.t = mdata.encXposition.t;
|
pt.X.t = mdata.encXposition.t;
|
||||||
@@ -87,6 +99,7 @@ int telpos(coordval_pair_t *curpos){
|
|||||||
// X=X0+1'/s, Y=Y0+15''/s
|
// X=X0+1'/s, Y=Y0+15''/s
|
||||||
int Linear(coordpair_t *nextpt, double t){
|
int Linear(coordpair_t *nextpt, double t){
|
||||||
coordpair_t pt;
|
coordpair_t pt;
|
||||||
|
DBG("t=%g, tfromstart=%g, dt=%g", t, tstart, t-tstart);
|
||||||
pt.X = XYstart.X + ASEC2RAD(0.1) * (t - tstart);
|
pt.X = XYstart.X + ASEC2RAD(0.1) * (t - tstart);
|
||||||
pt.Y = XYstart.Y + ASEC2RAD(15.)* (t - tstart);
|
pt.Y = XYstart.Y + ASEC2RAD(15.)* (t - tstart);
|
||||||
if(nextpt) *nextpt = pt;
|
if(nextpt) *nextpt = pt;
|
||||||
@@ -97,7 +110,7 @@ int Linear(coordpair_t *nextpt, double t){
|
|||||||
int SinCos(coordpair_t *nextpt, double t){
|
int SinCos(coordpair_t *nextpt, double t){
|
||||||
coordpair_t pt;
|
coordpair_t pt;
|
||||||
pt.X = XYstart.X + ASEC2RAD(5.) * sin((t-tstart)/30.*2*M_PI);
|
pt.X = XYstart.X + ASEC2RAD(5.) * sin((t-tstart)/30.*2*M_PI);
|
||||||
pt.Y = XYstart.Y + AMIN2RAD(10.)* cos((t-tstart)/200.*2*M_PI);
|
pt.Y = XYstart.Y + AMIN2RAD(1.)* cos((t-tstart)/10.*2*M_PI);
|
||||||
if(nextpt) *nextpt = pt;
|
if(nextpt) *nextpt = pt;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -110,7 +123,7 @@ typedef struct{
|
|||||||
|
|
||||||
static tr_names names[] = {
|
static tr_names names[] = {
|
||||||
{Linear, "linear", "X=X0+0.1''/s, Y=Y0+15''/s"},
|
{Linear, "linear", "X=X0+0.1''/s, Y=Y0+15''/s"},
|
||||||
{SinCos, "sincos", "X=X0+5''*sin(t/30*2pi), Y=Y0+10'*cos(t/200*2pi)"},
|
{SinCos, "sincos", "X=X0+5''*sin(t/30*2pi), Y=Y0+1'*cos(t/10*2pi)"},
|
||||||
{NULL, NULL, NULL}
|
{NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
169
Auxiliary_utils/LibSidServo/kalman.c
Normal file
169
Auxiliary_utils/LibSidServo/kalman.c
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the libsidservo project.
|
||||||
|
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "kalman.h"
|
||||||
|
|
||||||
|
void kalman3_init(Kalman3 *kf, double dt, double enc_var){
|
||||||
|
kf->dt = dt;
|
||||||
|
|
||||||
|
kf->x[0] = 0;
|
||||||
|
kf->x[1] = 0;
|
||||||
|
kf->x[2] = 0;
|
||||||
|
|
||||||
|
for(int i=0;i<3;i++)
|
||||||
|
for(int j=0;j<3;j++)
|
||||||
|
kf->P[i][j] = 0;
|
||||||
|
|
||||||
|
kf->P[0][0] = 1;
|
||||||
|
kf->P[1][1] = 1;
|
||||||
|
kf->P[2][2] = 1;
|
||||||
|
|
||||||
|
// process noise
|
||||||
|
kf->Q[0][0] = 1e-6;
|
||||||
|
kf->Q[1][1] = 1e-4;
|
||||||
|
kf->Q[2][2] = 1e-3;
|
||||||
|
|
||||||
|
kf->R = enc_var; // encoder noise variance
|
||||||
|
}
|
||||||
|
|
||||||
|
void kalman3_set_jerk_noise(Kalman3 *kf, double sigma_j){
|
||||||
|
double dt = kf->dt;
|
||||||
|
|
||||||
|
double dt2 = dt*dt;
|
||||||
|
double dt3 = dt2*dt;
|
||||||
|
double dt4 = dt3*dt;
|
||||||
|
double dt5 = dt4*dt;
|
||||||
|
|
||||||
|
double q = sigma_j * sigma_j;
|
||||||
|
|
||||||
|
kf->Q[0][0] = q * dt5 / 20.0;
|
||||||
|
kf->Q[0][1] = q * dt4 / 8.0;
|
||||||
|
kf->Q[0][2] = q * dt3 / 6.0;
|
||||||
|
|
||||||
|
kf->Q[1][0] = q * dt4 / 8.0;
|
||||||
|
kf->Q[1][1] = q * dt3 / 3.0;
|
||||||
|
kf->Q[1][2] = q * dt2 / 2.0;
|
||||||
|
|
||||||
|
kf->Q[2][0] = q * dt3 / 6.0;
|
||||||
|
kf->Q[2][1] = q * dt2 / 2.0;
|
||||||
|
kf->Q[2][2] = q * dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kalman3_predict(Kalman3 *kf){
|
||||||
|
double dt = kf->dt;
|
||||||
|
double dt2 = 0.5 * dt * dt;
|
||||||
|
|
||||||
|
double theta = kf->x[0];
|
||||||
|
double omega = kf->x[1];
|
||||||
|
double alpha = kf->x[2];
|
||||||
|
|
||||||
|
// state prediction
|
||||||
|
kf->x[0] = theta + omega*dt + alpha*dt2;
|
||||||
|
kf->x[1] = omega + alpha*dt;
|
||||||
|
kf->x[2] = alpha;
|
||||||
|
|
||||||
|
// transition matrix
|
||||||
|
double F[3][3] =
|
||||||
|
{
|
||||||
|
{1, dt, dt2},
|
||||||
|
{0, 1, dt},
|
||||||
|
{0, 0, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
// P = FPF^T + Q
|
||||||
|
|
||||||
|
double FP[3][3];
|
||||||
|
|
||||||
|
for(int i=0;i<3;i++)
|
||||||
|
for(int j=0;j<3;j++){
|
||||||
|
FP[i][j] =
|
||||||
|
F[i][0]*kf->P[0][j] +
|
||||||
|
F[i][1]*kf->P[1][j] +
|
||||||
|
F[i][2]*kf->P[2][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
double Pnew[3][3];
|
||||||
|
|
||||||
|
for(int i=0;i<3;i++)
|
||||||
|
for(int j=0;j<3;j++){
|
||||||
|
Pnew[i][j] =
|
||||||
|
FP[i][0]*F[j][0] +
|
||||||
|
FP[i][1]*F[j][1] +
|
||||||
|
FP[i][2]*F[j][2] +
|
||||||
|
kf->Q[i][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0;i<3;i++)
|
||||||
|
for(int j=0;j<3;j++)
|
||||||
|
kf->P[i][j] = Pnew[i][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
void kalman3_update(Kalman3 *kf, double z){
|
||||||
|
// innovation
|
||||||
|
double y = z - kf->x[0];
|
||||||
|
|
||||||
|
// S = HPH^T + R
|
||||||
|
double S = kf->P[0][0] + kf->R;
|
||||||
|
|
||||||
|
// Kalman gain
|
||||||
|
double K[3];
|
||||||
|
|
||||||
|
K[0] = kf->P[0][0] / S;
|
||||||
|
K[1] = kf->P[1][0] / S;
|
||||||
|
K[2] = kf->P[2][0] / S;
|
||||||
|
|
||||||
|
// state update
|
||||||
|
kf->x[0] += K[0] * y;
|
||||||
|
kf->x[1] += K[1] * y;
|
||||||
|
kf->x[2] += K[2] * y;
|
||||||
|
|
||||||
|
// covariance update
|
||||||
|
double P00 = kf->P[0][0];
|
||||||
|
double P01 = kf->P[0][1];
|
||||||
|
double P02 = kf->P[0][2];
|
||||||
|
|
||||||
|
double P10 = kf->P[1][0];
|
||||||
|
double P11 = kf->P[1][1];
|
||||||
|
double P12 = kf->P[1][2];
|
||||||
|
|
||||||
|
double P20 = kf->P[2][0];
|
||||||
|
double P21 = kf->P[2][1];
|
||||||
|
double P22 = kf->P[2][2];
|
||||||
|
|
||||||
|
kf->P[0][0] = P00 - K[0]*P00;
|
||||||
|
kf->P[0][1] = P01 - K[0]*P01;
|
||||||
|
kf->P[0][2] = P02 - K[0]*P02;
|
||||||
|
|
||||||
|
kf->P[1][0] = P10 - K[1]*P00;
|
||||||
|
kf->P[1][1] = P11 - K[1]*P01;
|
||||||
|
kf->P[1][2] = P12 - K[1]*P02;
|
||||||
|
|
||||||
|
kf->P[2][0] = P20 - K[2]*P00;
|
||||||
|
kf->P[2][1] = P21 - K[2]*P01;
|
||||||
|
kf->P[2][2] = P22 - K[2]*P02;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// estimation of the R
|
||||||
|
double encoder_noise(int counts){
|
||||||
|
double d = 2.0*M_PI / counts;
|
||||||
|
return d*d / 12.0;
|
||||||
|
}
|
||||||
|
|
||||||
34
Auxiliary_utils/LibSidServo/kalman.h
Normal file
34
Auxiliary_utils/LibSidServo/kalman.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the libsidservo project.
|
||||||
|
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
double x[3]; // [theta, omega, alpha]
|
||||||
|
double P[3][3]; // covariance
|
||||||
|
double Q[3][3]; // process noise
|
||||||
|
double R; // measurement noise
|
||||||
|
double dt;
|
||||||
|
} Kalman3;
|
||||||
|
|
||||||
|
|
||||||
|
double encoder_noise(int counts);
|
||||||
|
void kalman3_update(Kalman3 *kf, double z);
|
||||||
|
void kalman3_predict(Kalman3 *kf);
|
||||||
|
void kalman3_set_jerk_noise(Kalman3 *kf, double sigma_j);
|
||||||
|
void kalman3_init(Kalman3 *kf, double dt, double enc_var);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProject>
|
<!DOCTYPE QtCreatorProject>
|
||||||
<!-- Written by QtCreator 17.0.0, 2025-07-30T17:30:52. -->
|
<!-- Written by QtCreator 18.0.0, 2026-03-23T09:21:54. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>EnvironmentId</variable>
|
<variable>EnvironmentId</variable>
|
||||||
@@ -86,6 +86,7 @@
|
|||||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
|
<value type="int" key="RcSync">0</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
@@ -110,8 +111,8 @@
|
|||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||||
@@ -123,8 +124,8 @@
|
|||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||||
@@ -139,8 +140,8 @@
|
|||||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
@@ -164,6 +165,7 @@
|
|||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
@@ -173,8 +175,8 @@
|
|||||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
@@ -198,6 +200,7 @@
|
|||||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
@@ -208,10 +211,6 @@
|
|||||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||||
<value type="qlonglong">1</value>
|
<value type="qlonglong">1</value>
|
||||||
</data>
|
</data>
|
||||||
<data>
|
|
||||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
|
||||||
<value type="int">22</value>
|
|
||||||
</data>
|
|
||||||
<data>
|
<data>
|
||||||
<variable>Version</variable>
|
<variable>Version</variable>
|
||||||
<value type="int">22</value>
|
<value type="int">22</value>
|
||||||
|
|||||||
@@ -13,15 +13,18 @@ examples/dumpswing.c
|
|||||||
examples/goto.c
|
examples/goto.c
|
||||||
examples/scmd_traectory.c
|
examples/scmd_traectory.c
|
||||||
examples/simpleconv.h
|
examples/simpleconv.h
|
||||||
|
kalman.c
|
||||||
main.c
|
main.c
|
||||||
sidservo.h
|
sidservo.h
|
||||||
serial.c
|
serial.c
|
||||||
examples/CMakeLists.txt
|
examples/CMakeLists.txt
|
||||||
examples/traectories.c
|
examples/traectories.c
|
||||||
examples/traectories.h
|
examples/traectories.h
|
||||||
|
kalman.h
|
||||||
main.h
|
main.h
|
||||||
movingmodel.c
|
movingmodel.c
|
||||||
movingmodel.h
|
movingmodel.h
|
||||||
|
polltest/main.c
|
||||||
ramp.c
|
ramp.c
|
||||||
ramp.h
|
ramp.h
|
||||||
serial.h
|
serial.h
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "movingmodel.h"
|
#include "movingmodel.h"
|
||||||
@@ -32,40 +33,82 @@
|
|||||||
#include "ssii.h"
|
#include "ssii.h"
|
||||||
#include "PID.h"
|
#include "PID.h"
|
||||||
|
|
||||||
|
// adder for monotonic time by realtime: inited any call of init()
|
||||||
|
static struct timespec timeadder = {0}, // adder of CLOCK_REALTIME to CLOCK_MONOTONIC
|
||||||
|
t0 = {0}, // curtime() for initstarttime() call
|
||||||
|
starttime = {0}; // starting time by monotonic (for timefromstart())
|
||||||
|
|
||||||
conf_t Conf = {0};
|
conf_t Conf = {0};
|
||||||
// parameters for model
|
// parameters for model
|
||||||
static movemodel_t *Xmodel, *Ymodel;
|
static movemodel_t *Xmodel, *Ymodel;
|
||||||
|
// limits for model and/or real mount (in latter case data should be read from mount on init)
|
||||||
// radians, rad/sec, rad/sec^2
|
// radians, rad/sec, rad/sec^2
|
||||||
static limits_t
|
// max speeds (rad/s): xs=10 deg/s, ys=8 deg/s
|
||||||
|
// accelerations: xa=12.6 deg/s^2, ya= 9.5 deg/s^2
|
||||||
|
limits_t
|
||||||
Xlimits = {
|
Xlimits = {
|
||||||
.min = {.coord = -3.1241, .speed = 1e-10, .accel = 1e-6},
|
.min = {.coord = -3.1241, .speed = 1e-10, .accel = 1e-6},
|
||||||
.max = {.coord = 3.1241, .speed = MCC_MAX_X_SPEED, .accel = MCC_X_ACCELERATION}},
|
.max = {.coord = 3.1241, .speed = 0.174533, .accel = 0.219911}},
|
||||||
Ylimits = {
|
Ylimits = {
|
||||||
.min = {.coord = -3.1241, .speed = 1e-10, .accel = 1e-6},
|
.min = {.coord = -3.1241, .speed = 1e-10, .accel = 1e-6},
|
||||||
.max = {.coord = 3.1241, .speed = MCC_MAX_Y_SPEED, .accel = MCC_Y_ACCELERATION}}
|
.max = {.coord = 3.1241, .speed = 0.139626, .accel = 0.165806}}
|
||||||
;
|
;
|
||||||
static mcc_errcodes_t shortcmd(short_command_t *cmd);
|
static mcc_errcodes_t shortcmd(short_command_t *cmd);
|
||||||
|
static mcc_errcodes_t get_hwconf(hardware_configuration_t *hwConfig);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief nanotime - monotonic time from first run
|
* @brief curtime - monotonic time from first run
|
||||||
* @return time in seconds
|
* @param t - struct timespec by CLOCK_MONOTONIC but with setpoint by CLOCK_REALTIME on observations start
|
||||||
|
* @return TRUE if all OK
|
||||||
|
* FIXME: double -> struct timespec; on init: init t0 by CLOCK_REALTIME
|
||||||
*/
|
*/
|
||||||
double nanotime(){
|
int curtime(struct timespec *t){
|
||||||
static struct timespec *start = NULL;
|
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
if(!start){
|
if(clock_gettime(CLOCK_MONOTONIC, &now)) return FALSE;
|
||||||
start = malloc(sizeof(struct timespec));
|
now.tv_sec += timeadder.tv_sec;
|
||||||
if(!start) return -1.;
|
now.tv_nsec += timeadder.tv_nsec;
|
||||||
if(clock_gettime(CLOCK_MONOTONIC, start)) return -1.;
|
if(now.tv_nsec > 999999999L){
|
||||||
|
++now.tv_sec;
|
||||||
|
now.tv_nsec -= 1000000000L;
|
||||||
}
|
}
|
||||||
|
if(t) *t = now;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init starttime; @return TRUE if all OK
|
||||||
|
static int initstarttime(){
|
||||||
|
struct timespec start;
|
||||||
|
if(clock_gettime(CLOCK_MONOTONIC, &starttime)) return FALSE;
|
||||||
|
if(clock_gettime(CLOCK_REALTIME, &start)) return FALSE;
|
||||||
|
timeadder.tv_sec = start.tv_sec - starttime.tv_sec;
|
||||||
|
timeadder.tv_nsec = start.tv_nsec - starttime.tv_nsec;
|
||||||
|
if(timeadder.tv_nsec < 0){
|
||||||
|
--timeadder.tv_sec;
|
||||||
|
timeadder.tv_nsec += 1000000000L;
|
||||||
|
}
|
||||||
|
curtime(&t0);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return difference (in seconds) between time1 and time0
|
||||||
|
double timediff(const struct timespec *time1, const struct timespec *time0){
|
||||||
|
if(!time1 || !time0) return -1.;
|
||||||
|
return (time1->tv_sec - time0->tv_sec) + (time1->tv_nsec - time0->tv_nsec) / 1e9;
|
||||||
|
}
|
||||||
|
// difference between given time and last initstarttime() call
|
||||||
|
double timediff0(const struct timespec *time1){
|
||||||
|
return timediff(time1, &t0);
|
||||||
|
}
|
||||||
|
// time from last initstarttime() call
|
||||||
|
double timefromstart(){
|
||||||
|
struct timespec now;
|
||||||
if(clock_gettime(CLOCK_MONOTONIC, &now)) return -1.;
|
if(clock_gettime(CLOCK_MONOTONIC, &now)) return -1.;
|
||||||
double nd = ((double)now.tv_nsec - (double)start->tv_nsec) * 1e-9;
|
return (now.tv_sec - starttime.tv_sec) + (now.tv_nsec - starttime.tv_nsec) / 1e9;
|
||||||
double sd = (double)now.tv_sec - (double)start->tv_sec;
|
|
||||||
return sd + nd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief quit - close all opened and return to default state
|
* @brief quit - close all opened and return to default state
|
||||||
|
* TODO: close serial devices even in "model" mode
|
||||||
*/
|
*/
|
||||||
static void quit(){
|
static void quit(){
|
||||||
if(Conf.RunModel) return;
|
if(Conf.RunModel) return;
|
||||||
@@ -75,25 +118,19 @@ static void quit(){
|
|||||||
DBG("Exit");
|
DBG("Exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
void getModData(mountdata_t *mountdata){
|
void getModData(coordpair_t *c, movestate_t *xst, movestate_t *yst){
|
||||||
if(!mountdata || !Xmodel || !Ymodel) return;
|
if(!c || !Xmodel || !Ymodel) return;
|
||||||
static double oldmt = -100.; // old `millis measurement` time
|
double tnow = timefromstart();
|
||||||
static uint32_t oldmillis = 0;
|
|
||||||
double tnow = nanotime();
|
|
||||||
moveparam_t Xp, Yp;
|
moveparam_t Xp, Yp;
|
||||||
movestate_t Xst = Xmodel->get_state(Xmodel, &Xp);
|
movestate_t Xst = Xmodel->get_state(Xmodel, &Xp);
|
||||||
//DBG("Xstate = %d", Xst);
|
//DBG("Xstate = %d", Xst);
|
||||||
if(Xst == ST_MOVE) Xst = Xmodel->proc_move(Xmodel, &Xp, tnow);
|
if(Xst == ST_MOVE) Xst = Xmodel->proc_move(Xmodel, &Xp, tnow);
|
||||||
movestate_t Yst = Ymodel->get_state(Ymodel, &Yp);
|
movestate_t Yst = Ymodel->get_state(Ymodel, &Yp);
|
||||||
if(Yst == ST_MOVE) Yst = Ymodel->proc_move(Ymodel, &Yp, tnow);
|
if(Yst == ST_MOVE) Yst = Ymodel->proc_move(Ymodel, &Yp, tnow);
|
||||||
mountdata->motXposition.t = mountdata->encXposition.t = mountdata->motYposition.t = mountdata->encYposition.t = tnow;
|
c->X = Xp.coord;
|
||||||
mountdata->motXposition.val = mountdata->encXposition.val = Xp.coord;
|
c->Y = Yp.coord;
|
||||||
mountdata->motYposition.val = mountdata->encYposition.val = Yp.coord;
|
if(xst) *xst = Xst;
|
||||||
getXspeed(); getYspeed();
|
if(yst) *yst = Yst;
|
||||||
if(tnow - oldmt > Conf.MountReqInterval){
|
|
||||||
oldmillis = mountdata->millis = (uint32_t)(tnow * 1e3);
|
|
||||||
oldmt = tnow;
|
|
||||||
}else mountdata->millis = oldmillis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,6 +161,8 @@ double LS_calc_slope(less_square_t *l, double x, double t){
|
|||||||
if(!l) return 0.;
|
if(!l) return 0.;
|
||||||
size_t idx = l->idx;
|
size_t idx = l->idx;
|
||||||
double oldx = l->x[idx], oldt = l->t[idx], oldt2 = l->t2[idx], oldxt = l->xt[idx];
|
double oldx = l->x[idx], oldt = l->t[idx], oldt2 = l->t2[idx], oldxt = l->xt[idx];
|
||||||
|
/*DBG("old: x=%g, t=%g, t2=%g, xt=%g; sum: %g, t=%g, t2=%g, xt=%g", oldx, oldt, oldt2, oldxt,
|
||||||
|
l->xsum, l->tsum, l->t2sum, l->xtsum);*/
|
||||||
double t2 = t * t, xt = x * t;
|
double t2 = t * t, xt = x * t;
|
||||||
l->x[idx] = x; l->t2[idx] = t2;
|
l->x[idx] = x; l->t2[idx] = t2;
|
||||||
l->t[idx] = t; l->xt[idx] = xt;
|
l->t[idx] = t; l->xt[idx] = xt;
|
||||||
@@ -135,14 +174,13 @@ double LS_calc_slope(less_square_t *l, double x, double t){
|
|||||||
l->xtsum += xt - oldxt;
|
l->xtsum += xt - oldxt;
|
||||||
double n = (double)l->arraysz;
|
double n = (double)l->arraysz;
|
||||||
double denominator = n * l->t2sum - l->tsum * l->tsum;
|
double denominator = n * l->t2sum - l->tsum * l->tsum;
|
||||||
//DBG("idx=%zd, arrsz=%zd, den=%g", l->idx, l->arraysz, denominator);
|
|
||||||
if(fabs(denominator) < 1e-7) return 0.;
|
if(fabs(denominator) < 1e-7) return 0.;
|
||||||
double numerator = n * l->xtsum - l->xsum * l->tsum;
|
double numerator = n * l->xtsum - l->xsum * l->tsum;
|
||||||
|
//DBG("x=%g, t=%g; idx=%zd, arrsz=%zd, den=%g; xsum=%g, num=%g", x, t, l->idx, l->arraysz, denominator, l->xsum, numerator);
|
||||||
// point: (sum_x - slope * sum_t) / n;
|
// point: (sum_x - slope * sum_t) / n;
|
||||||
return (numerator / denominator);
|
return (numerator / denominator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief init - open serial devices and do other job
|
* @brief init - open serial devices and do other job
|
||||||
* @param c - initial configuration
|
* @param c - initial configuration
|
||||||
@@ -151,65 +189,97 @@ double LS_calc_slope(less_square_t *l, double x, double t){
|
|||||||
static mcc_errcodes_t init(conf_t *c){
|
static mcc_errcodes_t init(conf_t *c){
|
||||||
FNAME();
|
FNAME();
|
||||||
if(!c) return MCC_E_BADFORMAT;
|
if(!c) return MCC_E_BADFORMAT;
|
||||||
|
if(!initstarttime()) return MCC_E_FAILED;
|
||||||
Conf = *c;
|
Conf = *c;
|
||||||
mcc_errcodes_t ret = MCC_E_OK;
|
mcc_errcodes_t ret = MCC_E_OK;
|
||||||
Xmodel = model_init(&Xlimits);
|
Xmodel = model_init(&Xlimits);
|
||||||
Ymodel = model_init(&Ylimits);
|
Ymodel = model_init(&Ylimits);
|
||||||
|
if(Conf.MountReqInterval > 1. || Conf.MountReqInterval < 0.05){
|
||||||
|
DBG("Bad value of MountReqInterval");
|
||||||
|
ret = MCC_E_BADFORMAT;
|
||||||
|
}
|
||||||
if(Conf.RunModel){
|
if(Conf.RunModel){
|
||||||
if(!Xmodel || !Ymodel || !openMount()) return MCC_E_FAILED;
|
if(!Xmodel || !Ymodel || !openMount()) return MCC_E_FAILED;
|
||||||
return MCC_E_OK;
|
return MCC_E_OK;
|
||||||
}
|
}
|
||||||
if(!Conf.MountDevPath || Conf.MountDevSpeed < 1200){
|
DBG("Try to open mount device");
|
||||||
|
if(!Conf.MountDevPath || Conf.MountDevSpeed < MOUNT_BAUDRATE_MIN){
|
||||||
DBG("Define mount device path and speed");
|
DBG("Define mount device path and speed");
|
||||||
ret = MCC_E_BADFORMAT;
|
ret = MCC_E_BADFORMAT;
|
||||||
}else if(!openMount()){
|
}else if(!openMount()){
|
||||||
DBG("Can't open %s with speed %d", Conf.MountDevPath, Conf.MountDevSpeed);
|
DBG("Can't open %s with speed %d", Conf.MountDevPath, Conf.MountDevSpeed);
|
||||||
ret = MCC_E_MOUNTDEV;
|
ret = MCC_E_MOUNTDEV;
|
||||||
}
|
}
|
||||||
if(Conf.SepEncoder){
|
// TODO: read hardware configuration on init
|
||||||
if(!Conf.EncoderDevPath && !Conf.EncoderXDevPath){
|
|
||||||
DBG("Define encoder device path");
|
|
||||||
ret = MCC_E_BADFORMAT;
|
|
||||||
}else if(!openEncoder()){
|
|
||||||
DBG("Can't open encoder device");
|
|
||||||
ret = MCC_E_ENCODERDEV;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(Conf.MountReqInterval > 1. || Conf.MountReqInterval < 0.05){
|
|
||||||
DBG("Bad value of MountReqInterval");
|
|
||||||
ret = MCC_E_BADFORMAT;
|
|
||||||
}
|
|
||||||
if(Conf.EncoderSpeedInterval < Conf.EncoderReqInterval * MCC_CONF_MIN_SPEEDC || Conf.EncoderSpeedInterval > MCC_CONF_MAX_SPEEDINT){
|
if(Conf.EncoderSpeedInterval < Conf.EncoderReqInterval * MCC_CONF_MIN_SPEEDC || Conf.EncoderSpeedInterval > MCC_CONF_MAX_SPEEDINT){
|
||||||
DBG("Wrong speed interval");
|
DBG("Wrong speed interval");
|
||||||
ret = MCC_E_BADFORMAT;
|
ret = MCC_E_BADFORMAT;
|
||||||
}
|
}
|
||||||
//uint8_t buf[1024];
|
|
||||||
//data_t d = {.buf = buf, .len = 0, .maxlen = 1024};
|
|
||||||
if(!SSrawcmd(CMD_EXITACM, NULL)) ret = MCC_E_FAILED;
|
|
||||||
if(ret != MCC_E_OK) return ret;
|
if(ret != MCC_E_OK) return ret;
|
||||||
return updateMotorPos();
|
DBG("Exit ACM, exit manual mode");
|
||||||
|
SSrawcmd(CMD_EXITACM, NULL);
|
||||||
|
SStextcmd(CMD_AUTOX, NULL);
|
||||||
|
SStextcmd(CMD_AUTOY, NULL);
|
||||||
|
// read HW config to update constants
|
||||||
|
hardware_configuration_t HW;
|
||||||
|
DBG("Read hardware configuration");
|
||||||
|
ret = MCC_E_FAILED;
|
||||||
|
for(int i = 0; i < MAX_ERR_CTR; ++i){
|
||||||
|
DBG("TRY %d..", i);
|
||||||
|
ret = get_hwconf(&HW);
|
||||||
|
if(ret == MCC_E_OK) break;
|
||||||
|
}
|
||||||
|
if(MCC_E_OK != ret) return ret;
|
||||||
|
// make a pause for actual encoder's values
|
||||||
|
DBG("Check encoders");
|
||||||
|
if(Conf.SepEncoder){
|
||||||
|
if(!Conf.EncoderDevPath && !Conf.EncoderXDevPath){
|
||||||
|
DBG("Define encoder device path");
|
||||||
|
ret = MCC_E_BADFORMAT;
|
||||||
|
}else{
|
||||||
|
ret = MCC_E_ENCODERDEV;
|
||||||
|
for(int i = 0; i < MAX_ERR_CTR; ++i){
|
||||||
|
if(openEncoder()){
|
||||||
|
ret = MCC_E_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(MCC_E_OK != ret) return ret;
|
||||||
|
double t0 = timefromstart();
|
||||||
|
DBG("Wait for first encoders' measurement");
|
||||||
|
while(timefromstart() - t0 < Conf.EncoderReqInterval * 15.) usleep(1000);
|
||||||
|
DBG("Update motor position");
|
||||||
|
mcc_errcodes_t e = updateMotorPos();
|
||||||
|
// and refresh data after updating
|
||||||
|
DBG("Wait for next mount reading");
|
||||||
|
t0 = timefromstart();
|
||||||
|
while(timefromstart() - t0 < Conf.MountReqInterval * 5.) usleep(1000);
|
||||||
|
DBG("ALL READY!");
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check coordinates (rad) and speeds (rad/s); return FALSE if failed
|
// check coordinates (rad) and speeds (rad/s); return FALSE if failed
|
||||||
// TODO fix to real limits!!!
|
// TODO fix to real limits!!!
|
||||||
static int chkX(double X){
|
static int chkX(double X){
|
||||||
if(X > 2.*M_PI || X < -2.*M_PI) return FALSE;
|
if(X > Xlimits.max.coord || X < Xlimits.min.coord) return FALSE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
static int chkY(double Y){
|
static int chkY(double Y){
|
||||||
if(Y > 2.*M_PI || Y < -2.*M_PI) return FALSE;
|
if(Y > Ylimits.max.coord || Y < Ylimits.min.coord) return FALSE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
static int chkXs(double s){
|
static int chkXs(double s){
|
||||||
if(s < 0. || s > MCC_MAX_X_SPEED) return FALSE;
|
if(s < Xlimits.min.speed || s > Xlimits.max.speed) return FALSE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
static int chkYs(double s){
|
static int chkYs(double s){
|
||||||
if(s < 0. || s > MCC_MAX_Y_SPEED) return FALSE;
|
if(s < Ylimits.min.speed || s > Ylimits.max.speed) return FALSE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set SLEWING state if axis was stopped later
|
// set SLEWING state if axis was stopped
|
||||||
static void setslewingstate(){
|
static void setslewingstate(){
|
||||||
//FNAME();
|
//FNAME();
|
||||||
mountdata_t d;
|
mountdata_t d;
|
||||||
@@ -225,19 +295,6 @@ static void setslewingstate(){
|
|||||||
}else DBG("CAN't GET MOUNT DATA!");
|
}else DBG("CAN't GET MOUNT DATA!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
static mcc_errcodes_t slew2(const coordpair_t *target, slewflags_t flags){
|
|
||||||
(void)target;
|
|
||||||
(void)flags;
|
|
||||||
//if(Conf.RunModel) return ... ;
|
|
||||||
if(MCC_E_OK != updateMotorPos()) return MCC_E_FAILED;
|
|
||||||
//...
|
|
||||||
setStat(AXIS_SLEWING, AXIS_SLEWING);
|
|
||||||
//...
|
|
||||||
return MCC_E_FAILED;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief move2 - simple move to given point and stop
|
* @brief move2 - simple move to given point and stop
|
||||||
* @param X - new X coordinate (radians: -pi..pi) or NULL
|
* @param X - new X coordinate (radians: -pi..pi) or NULL
|
||||||
@@ -252,12 +309,9 @@ static mcc_errcodes_t move2(const coordpair_t *target){
|
|||||||
DBG("x,y: %g, %g", target->X, target->Y);
|
DBG("x,y: %g, %g", target->X, target->Y);
|
||||||
cmd.Xmot = target->X;
|
cmd.Xmot = target->X;
|
||||||
cmd.Ymot = target->Y;
|
cmd.Ymot = target->Y;
|
||||||
cmd.Xspeed = MCC_MAX_X_SPEED;
|
cmd.Xspeed = Xlimits.max.speed;
|
||||||
cmd.Yspeed = MCC_MAX_Y_SPEED;
|
cmd.Yspeed = Ylimits.max.speed;
|
||||||
mcc_errcodes_t r = shortcmd(&cmd);
|
return shortcmd(&cmd);
|
||||||
if(r != MCC_E_OK) return r;
|
|
||||||
setslewingstate();
|
|
||||||
return MCC_E_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -286,16 +340,14 @@ static mcc_errcodes_t move2s(const coordpair_t *target, const coordpair_t *speed
|
|||||||
if(!target || !speed) return MCC_E_BADFORMAT;
|
if(!target || !speed) return MCC_E_BADFORMAT;
|
||||||
if(!chkX(target->X) || !chkY(target->Y)) return MCC_E_BADFORMAT;
|
if(!chkX(target->X) || !chkY(target->Y)) return MCC_E_BADFORMAT;
|
||||||
if(!chkXs(speed->X) || !chkYs(speed->Y)) return MCC_E_BADFORMAT;
|
if(!chkXs(speed->X) || !chkYs(speed->Y)) return MCC_E_BADFORMAT;
|
||||||
|
// updateMotorPos() here can make a problem; TODO: remove?
|
||||||
if(MCC_E_OK != updateMotorPos()) return MCC_E_FAILED;
|
if(MCC_E_OK != updateMotorPos()) return MCC_E_FAILED;
|
||||||
short_command_t cmd = {0};
|
short_command_t cmd = {0};
|
||||||
cmd.Xmot = target->X;
|
cmd.Xmot = target->X;
|
||||||
cmd.Ymot = target->Y;
|
cmd.Ymot = target->Y;
|
||||||
cmd.Xspeed = speed->X;
|
cmd.Xspeed = speed->X;
|
||||||
cmd.Yspeed = speed->Y;
|
cmd.Yspeed = speed->Y;
|
||||||
mcc_errcodes_t r = shortcmd(&cmd);
|
return shortcmd(&cmd);
|
||||||
if(r != MCC_E_OK) return r;
|
|
||||||
setslewingstate();
|
|
||||||
return MCC_E_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -305,7 +357,7 @@ static mcc_errcodes_t move2s(const coordpair_t *target, const coordpair_t *speed
|
|||||||
static mcc_errcodes_t emstop(){
|
static mcc_errcodes_t emstop(){
|
||||||
FNAME();
|
FNAME();
|
||||||
if(Conf.RunModel){
|
if(Conf.RunModel){
|
||||||
double curt = nanotime();
|
double curt = timefromstart();
|
||||||
Xmodel->emergency_stop(Xmodel, curt);
|
Xmodel->emergency_stop(Xmodel, curt);
|
||||||
Ymodel->emergency_stop(Ymodel, curt);
|
Ymodel->emergency_stop(Ymodel, curt);
|
||||||
return MCC_E_OK;
|
return MCC_E_OK;
|
||||||
@@ -317,7 +369,7 @@ static mcc_errcodes_t emstop(){
|
|||||||
static mcc_errcodes_t stop(){
|
static mcc_errcodes_t stop(){
|
||||||
FNAME();
|
FNAME();
|
||||||
if(Conf.RunModel){
|
if(Conf.RunModel){
|
||||||
double curt = nanotime();
|
double curt = timefromstart();
|
||||||
Xmodel->stop(Xmodel, curt);
|
Xmodel->stop(Xmodel, curt);
|
||||||
Ymodel->stop(Ymodel,curt);
|
Ymodel->stop(Ymodel,curt);
|
||||||
return MCC_E_OK;
|
return MCC_E_OK;
|
||||||
@@ -334,7 +386,7 @@ static mcc_errcodes_t stop(){
|
|||||||
static mcc_errcodes_t shortcmd(short_command_t *cmd){
|
static mcc_errcodes_t shortcmd(short_command_t *cmd){
|
||||||
if(!cmd) return MCC_E_BADFORMAT;
|
if(!cmd) return MCC_E_BADFORMAT;
|
||||||
if(Conf.RunModel){
|
if(Conf.RunModel){
|
||||||
double curt = nanotime();
|
double curt = timefromstart();
|
||||||
moveparam_t param = {0};
|
moveparam_t param = {0};
|
||||||
param.coord = cmd->Xmot; param.speed = cmd->Xspeed;
|
param.coord = cmd->Xmot; param.speed = cmd->Xspeed;
|
||||||
if(!model_move2(Xmodel, ¶m, curt)) return MCC_E_FAILED;
|
if(!model_move2(Xmodel, ¶m, curt)) return MCC_E_FAILED;
|
||||||
@@ -366,7 +418,7 @@ static mcc_errcodes_t shortcmd(short_command_t *cmd){
|
|||||||
static mcc_errcodes_t longcmd(long_command_t *cmd){
|
static mcc_errcodes_t longcmd(long_command_t *cmd){
|
||||||
if(!cmd) return MCC_E_BADFORMAT;
|
if(!cmd) return MCC_E_BADFORMAT;
|
||||||
if(Conf.RunModel){
|
if(Conf.RunModel){
|
||||||
double curt = nanotime();
|
double curt = timefromstart();
|
||||||
moveparam_t param = {0};
|
moveparam_t param = {0};
|
||||||
param.coord = cmd->Xmot; param.speed = cmd->Xspeed;
|
param.coord = cmd->Xmot; param.speed = cmd->Xspeed;
|
||||||
if(!model_move2(Xmodel, ¶m, curt)) return MCC_E_FAILED;
|
if(!model_move2(Xmodel, ¶m, curt)) return MCC_E_FAILED;
|
||||||
@@ -393,6 +445,7 @@ static mcc_errcodes_t get_hwconf(hardware_configuration_t *hwConfig){
|
|||||||
if(!hwConfig) return MCC_E_BADFORMAT;
|
if(!hwConfig) return MCC_E_BADFORMAT;
|
||||||
if(Conf.RunModel) return MCC_E_FAILED;
|
if(Conf.RunModel) return MCC_E_FAILED;
|
||||||
SSconfig config;
|
SSconfig config;
|
||||||
|
DBG("Read HW configuration");
|
||||||
if(!cmdC(&config, FALSE)) return MCC_E_FAILED;
|
if(!cmdC(&config, FALSE)) return MCC_E_FAILED;
|
||||||
// Convert acceleration (ticks per loop^2 to rad/s^2)
|
// Convert acceleration (ticks per loop^2 to rad/s^2)
|
||||||
hwConfig->Xconf.accel = X_MOTACC2RS(config.Xconf.accel);
|
hwConfig->Xconf.accel = X_MOTACC2RS(config.Xconf.accel);
|
||||||
@@ -432,8 +485,8 @@ static mcc_errcodes_t get_hwconf(hardware_configuration_t *hwConfig){
|
|||||||
// Copy ticks per revolution
|
// Copy ticks per revolution
|
||||||
hwConfig->Xsetpr = __bswap_32(config.Xsetpr);
|
hwConfig->Xsetpr = __bswap_32(config.Xsetpr);
|
||||||
hwConfig->Ysetpr = __bswap_32(config.Ysetpr);
|
hwConfig->Ysetpr = __bswap_32(config.Ysetpr);
|
||||||
hwConfig->Xmetpr = __bswap_32(config.Xmetpr) / 4; // as documentation said, real ticks are 4 times less
|
hwConfig->Xmetpr = __bswap_32(config.Xmetpr); // as documentation said, real ticks are 4 times less
|
||||||
hwConfig->Ymetpr = __bswap_32(config.Ymetpr) / 4;
|
hwConfig->Ymetpr = __bswap_32(config.Ymetpr);
|
||||||
// Convert slew rates (ticks per loop to rad/s)
|
// Convert slew rates (ticks per loop to rad/s)
|
||||||
hwConfig->Xslewrate = X_MOTSPD2RS(config.Xslewrate);
|
hwConfig->Xslewrate = X_MOTSPD2RS(config.Xslewrate);
|
||||||
hwConfig->Yslewrate = Y_MOTSPD2RS(config.Yslewrate);
|
hwConfig->Yslewrate = Y_MOTSPD2RS(config.Yslewrate);
|
||||||
@@ -451,6 +504,30 @@ static mcc_errcodes_t get_hwconf(hardware_configuration_t *hwConfig){
|
|||||||
hwConfig->locsspeed = (double)config.locsspeed * M_PI / (180.0 * 3600.0);
|
hwConfig->locsspeed = (double)config.locsspeed * M_PI / (180.0 * 3600.0);
|
||||||
// Convert backlash speed (ticks per loop to rad/s)
|
// Convert backlash speed (ticks per loop to rad/s)
|
||||||
hwConfig->backlspd = X_MOTSPD2RS(config.backlspd);
|
hwConfig->backlspd = X_MOTSPD2RS(config.backlspd);
|
||||||
|
// now read text commands
|
||||||
|
int64_t i64;
|
||||||
|
double Xticks, Yticks;
|
||||||
|
DBG("SERIAL");
|
||||||
|
// motor's encoder ticks per rev
|
||||||
|
if(!SSgetint(CMD_MEPRX, &i64)) return MCC_E_FAILED;
|
||||||
|
Xticks = ((double) i64); // divide by 4 as these values stored ???
|
||||||
|
if(!SSgetint(CMD_MEPRY, &i64)) return MCC_E_FAILED;
|
||||||
|
Yticks = ((double) i64);
|
||||||
|
X_ENC_ZERO = Conf.XEncZero;
|
||||||
|
Y_ENC_ZERO = Conf.YEncZero;
|
||||||
|
DBG("xyrev: %d/%d", config.xbits.motrev, config.ybits.motrev);
|
||||||
|
X_MOT_STEPSPERREV = hwConfig->Xconf.motor_stepsperrev = Xticks; // (config.xbits.motrev) ? -Xticks : Xticks;
|
||||||
|
Y_MOT_STEPSPERREV = hwConfig->Yconf.motor_stepsperrev = Yticks; //(config.ybits.motrev) ? -Yticks : Yticks;
|
||||||
|
DBG("zero: %d/%d; motsteps: %.10g/%.10g", X_ENC_ZERO, Y_ENC_ZERO, X_MOT_STEPSPERREV, Y_MOT_STEPSPERREV);
|
||||||
|
// axis encoder ticks per rev
|
||||||
|
if(!SSgetint(CMD_AEPRX, &i64)) return MCC_E_FAILED;
|
||||||
|
Xticks = (double) i64;
|
||||||
|
if(!SSgetint(CMD_AEPRY, &i64)) return MCC_E_FAILED;
|
||||||
|
Yticks = (double) i64;
|
||||||
|
DBG("xyencrev: %d/%d", config.xbits.encrev, config.ybits.encrev);
|
||||||
|
X_ENC_STEPSPERREV = hwConfig->Xconf.axis_stepsperrev = (config.xbits.encrev) ? -Xticks : Xticks;
|
||||||
|
Y_ENC_STEPSPERREV = hwConfig->Yconf.axis_stepsperrev = (config.ybits.encrev) ? -Yticks : Yticks;
|
||||||
|
DBG("encsteps: %.10g/%.10g", X_ENC_STEPSPERREV, Y_ENC_STEPSPERREV);
|
||||||
return MCC_E_OK;
|
return MCC_E_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,17 +583,37 @@ static mcc_errcodes_t write_hwconf(hardware_configuration_t *hwConfig){
|
|||||||
config.Ysetpr = __bswap_32(hwConfig->Ysetpr);
|
config.Ysetpr = __bswap_32(hwConfig->Ysetpr);
|
||||||
config.Xmetpr = __bswap_32(hwConfig->Xmetpr);
|
config.Xmetpr = __bswap_32(hwConfig->Xmetpr);
|
||||||
config.Ymetpr = __bswap_32(hwConfig->Ymetpr);
|
config.Ymetpr = __bswap_32(hwConfig->Ymetpr);
|
||||||
|
// todo - also write text params
|
||||||
// TODO - next
|
// TODO - next
|
||||||
(void) config;
|
(void) config;
|
||||||
return MCC_E_OK;
|
return MCC_E_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getters of max/min speed and acceleration
|
||||||
|
mcc_errcodes_t maxspeed(coordpair_t *v){
|
||||||
|
if(!v) return MCC_E_BADFORMAT;
|
||||||
|
v->X = Xlimits.max.speed;
|
||||||
|
v->Y = Ylimits.max.speed;
|
||||||
|
return MCC_E_OK;
|
||||||
|
}
|
||||||
|
mcc_errcodes_t minspeed(coordpair_t *v){
|
||||||
|
if(!v) return MCC_E_BADFORMAT;
|
||||||
|
v->X = Xlimits.min.speed;
|
||||||
|
v->Y = Ylimits.min.speed;
|
||||||
|
return MCC_E_OK;
|
||||||
|
}
|
||||||
|
mcc_errcodes_t acceleration(coordpair_t *a){
|
||||||
|
if(!a) return MCC_E_BADFORMAT;
|
||||||
|
a->X = Xlimits.max.accel;
|
||||||
|
a->Y = Ylimits.max.accel;
|
||||||
|
return MCC_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// init mount class
|
// init mount class
|
||||||
mount_t Mount = {
|
mount_t Mount = {
|
||||||
.init = init,
|
.init = init,
|
||||||
.quit = quit,
|
.quit = quit,
|
||||||
.getMountData = getMD,
|
.getMountData = getMD,
|
||||||
// .slewTo = slew2,
|
|
||||||
.moveTo = move2,
|
.moveTo = move2,
|
||||||
.moveWspeed = move2s,
|
.moveWspeed = move2s,
|
||||||
.setSpeed = setspeed,
|
.setSpeed = setspeed,
|
||||||
@@ -526,7 +623,13 @@ mount_t Mount = {
|
|||||||
.longCmd = longcmd,
|
.longCmd = longcmd,
|
||||||
.getHWconfig = get_hwconf,
|
.getHWconfig = get_hwconf,
|
||||||
.saveHWconfig = write_hwconf,
|
.saveHWconfig = write_hwconf,
|
||||||
.currentT = nanotime,
|
.currentT = curtime,
|
||||||
|
.timeFromStart = timefromstart,
|
||||||
|
.timeDiff = timediff,
|
||||||
|
.timeDiff0 = timediff0,
|
||||||
.correctTo = correct2,
|
.correctTo = correct2,
|
||||||
|
.getMaxSpeed = maxspeed,
|
||||||
|
.getMinSpeed = minspeed,
|
||||||
|
.getAcceleration = acceleration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,16 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "movingmodel.h"
|
||||||
#include "sidservo.h"
|
#include "sidservo.h"
|
||||||
|
|
||||||
extern conf_t Conf;
|
extern conf_t Conf;
|
||||||
double nanotime();
|
extern limits_t Xlimits, Ylimits;
|
||||||
void getModData(mountdata_t *mountdata);
|
int curtime(struct timespec *t);
|
||||||
|
double timediff(const struct timespec *time1, const struct timespec *time0);
|
||||||
|
double timediff0(const struct timespec *time1);
|
||||||
|
double timefromstart();
|
||||||
|
void getModData(coordpair_t *c, movestate_t *xst, movestate_t *yst);
|
||||||
typedef struct{
|
typedef struct{
|
||||||
double *x, *t, *t2, *xt; // arrays of coord/time and multiply
|
double *x, *t, *t2, *xt; // arrays of coord/time and multiply
|
||||||
double xsum, tsum, t2sum, xtsum; // sums of coord/time and their multiply
|
double xsum, tsum, t2sum, xtsum; // sums of coord/time and their multiply
|
||||||
@@ -42,10 +47,6 @@ double LS_calc_slope(less_square_t *l, double x, double t);
|
|||||||
|
|
||||||
// unused arguments of functions
|
// unused arguments of functions
|
||||||
#define _U_ __attribute__((__unused__))
|
#define _U_ __attribute__((__unused__))
|
||||||
// break absent in `case`
|
|
||||||
#define FALLTHRU __attribute__ ((fallthrough))
|
|
||||||
// and synonym for FALLTHRU
|
|
||||||
#define NOBREAKHERE __attribute__ ((fallthrough))
|
|
||||||
// weak functions
|
// weak functions
|
||||||
#define WEAK __attribute__ ((weak))
|
#define WEAK __attribute__ ((weak))
|
||||||
|
|
||||||
|
|||||||
@@ -60,9 +60,14 @@ movemodel_t *model_init(limits_t *l){
|
|||||||
|
|
||||||
int model_move2(movemodel_t *model, moveparam_t *target, double t){
|
int model_move2(movemodel_t *model, moveparam_t *target, double t){
|
||||||
if(!target || !model) return FALSE;
|
if(!target || !model) return FALSE;
|
||||||
DBG("MOVE to %g at speed %g", target->coord, target->speed);
|
DBG("MOVE to %g (deg) at speed %g (deg/s)", target->coord/M_PI*180., target->speed/M_PI*180.);
|
||||||
// only positive velocity
|
// only positive velocity
|
||||||
if(target->speed < 0.) target->speed = -target->speed;
|
if(target->speed < 0.) target->speed = -target->speed;
|
||||||
|
if(fabs(target->speed) < model->Min.speed){
|
||||||
|
DBG("STOP");
|
||||||
|
model->stop(model, t);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
// don't mind about acceleration - user cannot set it now
|
// don't mind about acceleration - user cannot set it now
|
||||||
return model->calculate(model, target, t);
|
return model->calculate(model, target, t);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ typedef struct{
|
|||||||
typedef struct{
|
typedef struct{
|
||||||
moveparam_t min;
|
moveparam_t min;
|
||||||
moveparam_t max;
|
moveparam_t max;
|
||||||
double acceleration;
|
//double acceleration;
|
||||||
} limits_t;
|
} limits_t;
|
||||||
|
|
||||||
typedef enum{
|
typedef enum{
|
||||||
|
|||||||
237
Auxiliary_utils/LibSidServo/polltest/main.c
Normal file
237
Auxiliary_utils/LibSidServo/polltest/main.c
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the libsidservo project.
|
||||||
|
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#define XYBUFSZ (2048)
|
||||||
|
|
||||||
|
struct{
|
||||||
|
int help;
|
||||||
|
char *Xpath;
|
||||||
|
char *Ypath;
|
||||||
|
double dt;
|
||||||
|
} G = {
|
||||||
|
.Xpath = "/dev/encoder_X0",
|
||||||
|
.Ypath = "/dev/encoder_Y0",
|
||||||
|
.dt = 0.001,
|
||||||
|
};
|
||||||
|
|
||||||
|
sl_option_t options[] = {
|
||||||
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||||
|
{"Xpath", NEED_ARG, NULL, 'X', arg_string, APTR(&G.Xpath), "path to X encoder"},
|
||||||
|
{"Ypath", NEED_ARG, NULL, 'Y', arg_string, APTR(&G.Ypath), "path to Y encoder"},
|
||||||
|
{"dt", NEED_ARG, NULL, 'd', arg_double, APTR(&G.dt), "request interval (1e-4..10s)"},
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
char buf[XYBUFSZ+1];
|
||||||
|
int len;
|
||||||
|
} buf_t;
|
||||||
|
|
||||||
|
static int Xfd = -1, Yfd = -1;
|
||||||
|
|
||||||
|
void signals(int sig){
|
||||||
|
if(sig){
|
||||||
|
signal(sig, SIG_IGN);
|
||||||
|
DBG("Get signal %d, quit.\n", sig);
|
||||||
|
}
|
||||||
|
DBG("close");
|
||||||
|
if(Xfd > 0){ close(Xfd); Xfd = -1; }
|
||||||
|
if(Yfd > 0){ close(Yfd); Yfd = -1; }
|
||||||
|
exit(sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op(const char *nm){
|
||||||
|
int fd = open(nm, O_RDWR|O_NOCTTY|O_NONBLOCK);
|
||||||
|
if(fd < 0) ERR("Can't open %s", nm);
|
||||||
|
struct termios2 tty;
|
||||||
|
if(ioctl(fd, TCGETS2, &tty)) ERR("Can't read TTY settings");
|
||||||
|
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||||
|
tty.c_iflag = 0; // don't do any changes in input stream
|
||||||
|
tty.c_oflag = 0; // don't do any changes in output stream
|
||||||
|
tty.c_cflag = BOTHER | CS8 | CREAD | CLOCAL; // other speed, 8bit, RW, ignore line ctrl
|
||||||
|
tty.c_ispeed = 1000000;
|
||||||
|
tty.c_ospeed = 1000000;
|
||||||
|
if(ioctl(fd, TCSETS2, &tty)) ERR("Can't set TTY settings");
|
||||||
|
// try to set exclusive
|
||||||
|
if(ioctl(fd, TIOCEXCL)){DBG("Can't make exclusive");}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eolcnt(buf_t *buf){
|
||||||
|
if(!buf) return -1;
|
||||||
|
int cnt = 0;
|
||||||
|
for(int i = 0; i < buf->len; ++i)
|
||||||
|
if(buf->buf[i] == '\n') ++cnt;
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
// move last record (if any) into head of buffer
|
||||||
|
static void movelast(buf_t *buf){
|
||||||
|
FNAME();
|
||||||
|
if(!buf) return;
|
||||||
|
DBG("buf was: %s", buf->buf);
|
||||||
|
int cnt = eolcnt(buf);
|
||||||
|
char *E = strrchr(buf->buf, '\n');
|
||||||
|
int idx = -1;
|
||||||
|
if(E){
|
||||||
|
idx = ++E - buf->buf; // position of symbol after '\n'
|
||||||
|
}else{
|
||||||
|
buf->len = strlen(buf->buf);
|
||||||
|
DBG("leave as is (%s)", buf->buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DBG("cnt=%d, idx=%d", cnt, idx);
|
||||||
|
switch(cnt){
|
||||||
|
case 0: // EOL not found - clear buf
|
||||||
|
buf->len = 0;
|
||||||
|
break;
|
||||||
|
case 1: // only one record - move all after '\n'
|
||||||
|
if(idx > 0 && idx < XYBUFSZ){
|
||||||
|
buf->len = XYBUFSZ - idx;
|
||||||
|
memmove(buf->buf, E, buf->len);
|
||||||
|
}else buf->len = 0;
|
||||||
|
break;
|
||||||
|
default: // more than one record - move
|
||||||
|
{
|
||||||
|
int i = idx - 2;
|
||||||
|
for(; i > -1; --i)
|
||||||
|
if(buf->buf[i] == '\n') break;
|
||||||
|
++i;
|
||||||
|
buf->len = XYBUFSZ - i;
|
||||||
|
memmove(buf->buf, &buf->buf[i], buf->len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf->buf[buf->len] = 0;
|
||||||
|
DBG("MOVED; now buf[%d]=%s", buf->len, buf->buf);
|
||||||
|
}
|
||||||
|
// write to buffer next data portion; return FALSE in case of error
|
||||||
|
static int readstrings(buf_t *buf, int fd){
|
||||||
|
if(!buf){WARNX("Empty buffer"); return FALSE;}
|
||||||
|
int L = XYBUFSZ - buf->len;
|
||||||
|
if(L == 0){
|
||||||
|
DBG("len: %d", buf->len);
|
||||||
|
movelast(buf);
|
||||||
|
L = XYBUFSZ - buf->len;
|
||||||
|
}
|
||||||
|
int got = read(fd, &buf->buf[buf->len], L);
|
||||||
|
if(got < 0){
|
||||||
|
WARN("read()");
|
||||||
|
return FALSE;
|
||||||
|
}else if(got == 0) return TRUE;
|
||||||
|
buf->len += got;
|
||||||
|
buf->buf[buf->len] = 0;
|
||||||
|
DBG("buf[%d]: %s", buf->len, buf->buf);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
// return TRUE if got, FALSE if no data found
|
||||||
|
static int getdata(buf_t *buf, long *out){
|
||||||
|
if(!buf) return -1;
|
||||||
|
// read record between last '\n' and previous (or start of string)
|
||||||
|
int cnt = eolcnt(buf);
|
||||||
|
if(cnt < 1) return FALSE;
|
||||||
|
char *last = strrchr(buf->buf, '\n');
|
||||||
|
if(!last) return FALSE; // WTF?
|
||||||
|
*last = 0;
|
||||||
|
char *prev = buf->buf;
|
||||||
|
if(cnt > 1) prev = strrchr(buf->buf, '\n') + 1;
|
||||||
|
if(!prev) prev = buf->buf; // ??
|
||||||
|
if(out) *out = atol(prev);
|
||||||
|
int l = strlen(++last);
|
||||||
|
if(l < XYBUFSZ){
|
||||||
|
buf->len = l;
|
||||||
|
if(l){
|
||||||
|
memmove(buf->buf, last, l);
|
||||||
|
DBG("moved: %s", buf->buf);
|
||||||
|
}else DBG("empty line");
|
||||||
|
}else{
|
||||||
|
buf->len = 0;
|
||||||
|
DBG("buffer clear");
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
// try to write '\n' asking new data portion; return FALSE if failed
|
||||||
|
static int asknext(int fd){
|
||||||
|
FNAME();
|
||||||
|
if(fd < 0) return FALSE;
|
||||||
|
int i = 0;
|
||||||
|
for(; i < 5; ++i){
|
||||||
|
int l = write(fd, "\n", 1);
|
||||||
|
DBG("l=%d", l);
|
||||||
|
if(1 == l) return TRUE;
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
DBG("5 tries... failed!");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
buf_t xbuf, ybuf;
|
||||||
|
long xlast, ylast;
|
||||||
|
double xtlast, ytlast;
|
||||||
|
sl_init();
|
||||||
|
sl_parseargs(&argc, &argv, options);
|
||||||
|
if(G.help) sl_showhelp(-1, options);
|
||||||
|
if(G.dt < 1e-4) ERRX("dx too small");
|
||||||
|
if(G.dt > 10.) ERRX("dx too big");
|
||||||
|
Xfd = op(G.Xpath);
|
||||||
|
Yfd = op(G.Ypath);
|
||||||
|
struct pollfd pfds[2];
|
||||||
|
pfds[0].fd = Xfd; pfds[0].events = POLLIN;
|
||||||
|
pfds[1].fd = Yfd; pfds[1].events = POLLIN;
|
||||||
|
double t0x, t0y, tstart;
|
||||||
|
asknext(Xfd); asknext(Yfd);
|
||||||
|
t0x = t0y = tstart = sl_dtime();
|
||||||
|
DBG("Start");
|
||||||
|
do{ // main cycle
|
||||||
|
if(poll(pfds, 2, 1) < 0){
|
||||||
|
WARN("poll()");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(pfds[0].revents && POLLIN){
|
||||||
|
if(!readstrings(&xbuf, Xfd)) break;
|
||||||
|
}
|
||||||
|
if(pfds[1].revents && POLLIN){
|
||||||
|
if(!readstrings(&ybuf, Yfd)) break;
|
||||||
|
}
|
||||||
|
double curt = sl_dtime();
|
||||||
|
if(getdata(&xbuf, &xlast)) xtlast = curt;
|
||||||
|
if(curt - t0x >= G.dt){ // get last records
|
||||||
|
if(curt - xtlast < 1.5*G.dt)
|
||||||
|
printf("%-14.4fX=%ld\n", xtlast-tstart, xlast);
|
||||||
|
if(!asknext(Xfd)) break;
|
||||||
|
t0x = (curt - t0x < 2.*G.dt) ? t0x + G.dt : curt;
|
||||||
|
}
|
||||||
|
curt = sl_dtime();
|
||||||
|
if(getdata(&ybuf, &ylast)) ytlast = curt;
|
||||||
|
if(curt - t0y >= G.dt){ // get last records
|
||||||
|
if(curt - ytlast < 1.5*G.dt)
|
||||||
|
printf("%-14.4fY=%ld\n", ytlast-tstart, ylast);
|
||||||
|
if(!asknext(Yfd)) break;
|
||||||
|
t0y = (curt - t0y < 2.*G.dt) ? t0y + G.dt : curt;
|
||||||
|
}
|
||||||
|
}while(Xfd > 0 && Yfd > 0);
|
||||||
|
DBG("OOps: disconnected");
|
||||||
|
signals(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -23,12 +23,14 @@
|
|||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "ramp.h"
|
#include "ramp.h"
|
||||||
/*
|
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
#undef DBG
|
#undef DBG
|
||||||
#define DBG(...)
|
#define DBG(...)
|
||||||
|
#undef FNAME
|
||||||
|
#define FNAME()
|
||||||
#endif
|
#endif
|
||||||
*/
|
|
||||||
static double coord_tolerance = COORD_TOLERANCE_DEFAULT;
|
static double coord_tolerance = COORD_TOLERANCE_DEFAULT;
|
||||||
|
|
||||||
static void emstop(movemodel_t *m, double _U_ t){
|
static void emstop(movemodel_t *m, double _U_ t){
|
||||||
@@ -120,7 +122,7 @@ static void unlockedcalc(movemodel_t *m, moveparam_t *x, double t){
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
// if we are here, we have the worst case: change speed direction
|
// if we are here, we have the worst case: change speed direction
|
||||||
DBG("Hardest case: change speed direction");
|
// DBG("Hardest case: change speed direction");
|
||||||
// now we should calculate coordinate at which model stops and biuld new trapezium from that point
|
// now we should calculate coordinate at which model stops and biuld new trapezium from that point
|
||||||
double x0 = m->curparams.coord, v0 = m->curparams.speed;
|
double x0 = m->curparams.coord, v0 = m->curparams.speed;
|
||||||
double xstop = x0 + sign_v0 * abs_dx_stop, tstop = t + abs_v0 / abs_a;
|
double xstop = x0 + sign_v0 * abs_dx_stop, tstop = t + abs_v0 / abs_a;
|
||||||
@@ -132,7 +134,7 @@ static void unlockedcalc(movemodel_t *m, moveparam_t *x, double t){
|
|||||||
m->Times[STAGE_ACCEL] = t;
|
m->Times[STAGE_ACCEL] = t;
|
||||||
m->Params[STAGE_ACCEL].coord = x0;
|
m->Params[STAGE_ACCEL].coord = x0;
|
||||||
m->Params[STAGE_ACCEL].speed = v0;
|
m->Params[STAGE_ACCEL].speed = v0;
|
||||||
DBG("NOW t[0]=%g, X[0]=%g, V[0]=%g", t, x0, v0);
|
// DBG("NOW t[0]=%g, X[0]=%g, V[0]=%g", t, x0, v0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m->state = ST_MOVE;
|
m->state = ST_MOVE;
|
||||||
@@ -210,11 +212,10 @@ static movestate_t proc(movemodel_t *m, moveparam_t *next, double t){
|
|||||||
if(m->movingstage == STAGE_STOPPED){
|
if(m->movingstage == STAGE_STOPPED){
|
||||||
m->curparams.coord = m->Params[STAGE_STOPPED].coord;
|
m->curparams.coord = m->Params[STAGE_STOPPED].coord;
|
||||||
pthread_mutex_unlock(&m->mutex);
|
pthread_mutex_unlock(&m->mutex);
|
||||||
DBG("REACHED STOPping stage @ t=%g", t);
|
/* DBG("REACHED STOPping stage @ t=%g", t);
|
||||||
for(int s = STAGE_STOPPED; s >= 0; --s){
|
for(int s = STAGE_STOPPED; s >= 0; --s){
|
||||||
DBG("T[%d]=%g, ", s, m->Times[s]);
|
DBG("T[%d]=%g, ", s, m->Times[s]);
|
||||||
}
|
}*/
|
||||||
fflush(stdout);
|
|
||||||
emstop(m, t);
|
emstop(m, t);
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <poll.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "kalman.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "movingmodel.h"
|
#include "movingmodel.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
@@ -48,7 +50,13 @@ static pthread_mutex_t mntmutex = PTHREAD_MUTEX_INITIALIZER,
|
|||||||
// encoders thread and mount thread
|
// encoders thread and mount thread
|
||||||
static pthread_t encthread, mntthread;
|
static pthread_t encthread, mntthread;
|
||||||
// max timeout for 1.5 bytes of encoder and 2 bytes of mount - for `select`
|
// max timeout for 1.5 bytes of encoder and 2 bytes of mount - for `select`
|
||||||
static struct timeval encRtmout = {.tv_sec = 0, .tv_usec = 50000}, mntRtmout = {.tv_sec = 0, .tv_usec = 50000};
|
// this values will be modified later
|
||||||
|
static struct timeval encRtmout = {.tv_sec = 0, .tv_usec = 100}, // encoder reading timeout
|
||||||
|
mnt1Rtmout = {.tv_sec = 0, .tv_usec = 200000}, // first reading
|
||||||
|
mntRtmout = {.tv_sec = 0, .tv_usec = 50000}; // next readings
|
||||||
|
|
||||||
|
static volatile int GlobExit = 0;
|
||||||
|
|
||||||
// encoders raw data
|
// encoders raw data
|
||||||
typedef struct __attribute__((packed)){
|
typedef struct __attribute__((packed)){
|
||||||
uint8_t magick;
|
uint8_t magick;
|
||||||
@@ -64,18 +72,12 @@ void getXspeed(){
|
|||||||
ls = LS_init(Conf.EncoderSpeedInterval / Conf.EncoderReqInterval);
|
ls = LS_init(Conf.EncoderSpeedInterval / Conf.EncoderReqInterval);
|
||||||
if(!ls) return;
|
if(!ls) return;
|
||||||
}
|
}
|
||||||
double speed = LS_calc_slope(ls, mountdata.encXposition.val, mountdata.encXposition.t);
|
double dt = timediff0(&mountdata.encXposition.t);
|
||||||
if(fabs(speed) < 1.5 * MCC_MAX_X_SPEED){
|
double speed = LS_calc_slope(ls, mountdata.encXposition.val, dt);
|
||||||
|
if(fabs(speed) < 1.5 * Xlimits.max.speed){
|
||||||
mountdata.encXspeed.val = speed;
|
mountdata.encXspeed.val = speed;
|
||||||
mountdata.encXspeed.t = mountdata.encXposition.t;
|
mountdata.encXspeed.t = mountdata.encXposition.t;
|
||||||
}
|
}
|
||||||
//DBG("Xspeed=%g", mountdata.encXspeed.val);
|
|
||||||
#if 0
|
|
||||||
mountdata.encXspeed.val = (mountdata.encXposition.val - lastXenc.val) / (t - lastXenc.t);
|
|
||||||
mountdata.encXspeed.t = (lastXenc.t + mountdata.encXposition.t) / 2.;
|
|
||||||
lastXenc.val = mountdata.encXposition.val;
|
|
||||||
lastXenc.t = t;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
void getYspeed(){
|
void getYspeed(){
|
||||||
static less_square_t *ls = NULL;
|
static less_square_t *ls = NULL;
|
||||||
@@ -83,17 +85,12 @@ void getYspeed(){
|
|||||||
ls = LS_init(Conf.EncoderSpeedInterval / Conf.EncoderReqInterval);
|
ls = LS_init(Conf.EncoderSpeedInterval / Conf.EncoderReqInterval);
|
||||||
if(!ls) return;
|
if(!ls) return;
|
||||||
}
|
}
|
||||||
double speed = LS_calc_slope(ls, mountdata.encYposition.val, mountdata.encYposition.t);
|
double dt = timediff0(&mountdata.encYposition.t);
|
||||||
if(fabs(speed) < 1.5 * MCC_MAX_Y_SPEED){
|
double speed = LS_calc_slope(ls, mountdata.encYposition.val, dt);
|
||||||
|
if(fabs(speed) < 1.5 * Ylimits.max.speed){
|
||||||
mountdata.encYspeed.val = speed;
|
mountdata.encYspeed.val = speed;
|
||||||
mountdata.encYspeed.t = mountdata.encYposition.t;
|
mountdata.encYspeed.t = mountdata.encYposition.t;
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
mountdata.encYspeed.val = (mountdata.encYposition.val - lastYenc.val) / (t - lastYenc.t);
|
|
||||||
mountdata.encYspeed.t = (lastYenc.t + mountdata.encYposition.t) / 2.;
|
|
||||||
lastYenc.val = mountdata.encYposition.val;
|
|
||||||
lastYenc.t = t;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +98,8 @@ void getYspeed(){
|
|||||||
* @param databuf - input buffer with 13 bytes of data
|
* @param databuf - input buffer with 13 bytes of data
|
||||||
* @param t - time when databuf[0] got
|
* @param t - time when databuf[0] got
|
||||||
*/
|
*/
|
||||||
static void parse_encbuf(uint8_t databuf[ENC_DATALEN], double t){
|
static void parse_encbuf(uint8_t databuf[ENC_DATALEN], struct timespec *t){
|
||||||
|
if(!t) return;
|
||||||
enc_t *edata = (enc_t*) databuf;
|
enc_t *edata = (enc_t*) databuf;
|
||||||
/*
|
/*
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
@@ -136,66 +134,16 @@ static void parse_encbuf(uint8_t databuf[ENC_DATALEN], double t){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pthread_mutex_lock(&datamutex);
|
pthread_mutex_lock(&datamutex);
|
||||||
mountdata.encXposition.val = X_ENC2RAD(edata->encX);
|
mountdata.encXposition.val = Xenc2rad(edata->encX);
|
||||||
mountdata.encYposition.val = Y_ENC2RAD(edata->encY);
|
mountdata.encYposition.val = Yenc2rad(edata->encY);
|
||||||
DBG("Got positions X/Y= %.6g / %.6g", mountdata.encXposition.val, mountdata.encYposition.val);
|
DBG("Got positions X/Y= %.6g / %.6g", mountdata.encXposition.val, mountdata.encYposition.val);
|
||||||
mountdata.encXposition.t = t;
|
mountdata.encXposition.t = *t;
|
||||||
mountdata.encYposition.t = t;
|
mountdata.encYposition.t = *t;
|
||||||
//if(t - lastXenc.t > Conf.EncoderSpeedInterval) getXspeed();
|
|
||||||
//if(t - lastYenc.t > Conf.EncoderSpeedInterval) getYspeed();
|
|
||||||
getXspeed(); getYspeed();
|
getXspeed(); getYspeed();
|
||||||
pthread_mutex_unlock(&datamutex);
|
pthread_mutex_unlock(&datamutex);
|
||||||
//DBG("time = %zd+%zd/1e6, X=%g deg, Y=%g deg", tv->tv_sec, tv->tv_usec, mountdata.encposition.X*180./M_PI, mountdata.encposition.Y*180./M_PI);
|
//DBG("time = %zd+%zd/1e6, X=%g deg, Y=%g deg", tv->tv_sec, tv->tv_usec, mountdata.encposition.X*180./M_PI, mountdata.encposition.Y*180./M_PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief getencval - get uint64_t data from encoder
|
|
||||||
* @param fd - encoder fd
|
|
||||||
* @param val - value read
|
|
||||||
* @param t - measurement time
|
|
||||||
* @return amount of data read or 0 if problem
|
|
||||||
*/
|
|
||||||
static int getencval(int fd, double *val, double *t){
|
|
||||||
if(fd < 0) return FALSE;
|
|
||||||
char buf[128];
|
|
||||||
int got = 0, Lmax = 127;
|
|
||||||
double t0 = nanotime();
|
|
||||||
do{
|
|
||||||
fd_set rfds;
|
|
||||||
FD_ZERO(&rfds);
|
|
||||||
FD_SET(fd, &rfds);
|
|
||||||
struct timeval tv = encRtmout;
|
|
||||||
int retval = select(fd + 1, &rfds, NULL, NULL, &tv);
|
|
||||||
if(!retval) continue;
|
|
||||||
if(retval < 0){
|
|
||||||
if(errno == EINTR) continue;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(FD_ISSET(fd, &rfds)){
|
|
||||||
ssize_t l = read(fd, &buf[got], Lmax);
|
|
||||||
if(l < 1) return 0; // disconnected ??
|
|
||||||
got += l; Lmax -= l;
|
|
||||||
buf[got] = 0;
|
|
||||||
} else continue;
|
|
||||||
if(strchr(buf, '\n')) break;
|
|
||||||
}while(Lmax && nanotime() - t0 < Conf.EncoderReqInterval);
|
|
||||||
if(got == 0) return 0; // WTF?
|
|
||||||
char *estr = strrchr(buf, '\n');
|
|
||||||
if(!estr) return 0;
|
|
||||||
*estr = 0;
|
|
||||||
char *bgn = strrchr(buf, '\n');
|
|
||||||
if(bgn) ++bgn;
|
|
||||||
else bgn = buf;
|
|
||||||
char *eptr;
|
|
||||||
long data = strtol(bgn, &eptr, 10);
|
|
||||||
if(eptr != estr){
|
|
||||||
DBG("NAN");
|
|
||||||
return 0; // wrong number
|
|
||||||
}
|
|
||||||
if(val) *val = (double) data;
|
|
||||||
if(t) *t = t0;
|
|
||||||
return got;
|
|
||||||
}
|
|
||||||
// try to read 1 byte from encoder; return -1 if nothing to read or -2 if device seems to be disconnected
|
// try to read 1 byte from encoder; return -1 if nothing to read or -2 if device seems to be disconnected
|
||||||
static int getencbyte(){
|
static int getencbyte(){
|
||||||
if(encfd[0] < 0) return -1;
|
if(encfd[0] < 0) return -1;
|
||||||
@@ -219,46 +167,61 @@ static int getencbyte(){
|
|||||||
}while(1);
|
}while(1);
|
||||||
return (int)byte;
|
return (int)byte;
|
||||||
}
|
}
|
||||||
// read 1 byte from mount; return -1 if nothing to read, -2 if disconnected
|
|
||||||
static int getmntbyte(){
|
/**
|
||||||
if(mntfd < 0) return -1;
|
* @brief readmntdata - read data
|
||||||
uint8_t byte;
|
* @param buffer - input buffer
|
||||||
|
* @param maxlen - maximal buffer length
|
||||||
|
* @return amount of bytes read or -1 in case of error
|
||||||
|
*/
|
||||||
|
static int readmntdata(uint8_t *buffer, int maxlen){
|
||||||
|
if(mntfd < 0){
|
||||||
|
DBG("mntfd non opened");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(!buffer || maxlen < 1) return 0;
|
||||||
|
//DBG("ask for %d bytes", maxlen);
|
||||||
|
int got = 0;
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
/* ssize_t l = read(mntfd, &byte, 1);
|
struct timeval tv = mnt1Rtmout;
|
||||||
//DBG("MNT read=%zd byte=0x%X", l, byte);
|
|
||||||
if(l == 0) return -1;
|
|
||||||
if(l != 1) return -2; // disconnected ??
|
|
||||||
return (int) byte;*/
|
|
||||||
do{
|
do{
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_SET(mntfd, &rfds);
|
FD_SET(mntfd, &rfds);
|
||||||
struct timeval tv = mntRtmout;
|
//DBG("select");
|
||||||
int retval = select(mntfd + 1, &rfds, NULL, NULL, &tv);
|
int retval = select(mntfd + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
//DBG("returned %d", retval);
|
||||||
if(retval < 0){
|
if(retval < 0){
|
||||||
if(errno == EINTR) continue;
|
if(errno == EINTR) continue;
|
||||||
DBG("Error in select()");
|
DBG("Error in select()");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//DBG("FD_ISSET = %d", FD_ISSET(mntfd, &rfds));
|
|
||||||
if(FD_ISSET(mntfd, &rfds)){
|
if(FD_ISSET(mntfd, &rfds)){
|
||||||
ssize_t l = read(mntfd, &byte, 1);
|
ssize_t l = read(mntfd, buffer, maxlen);
|
||||||
//DBG("MNT read=%zd byte=0x%X", l, byte);
|
if(l == 0){
|
||||||
if(l != 1){
|
DBG("read ZERO");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(l < 0){
|
||||||
DBG("Mount disconnected?");
|
DBG("Mount disconnected?");
|
||||||
return -2; // disconnected ??
|
return -2; // disconnected ??
|
||||||
}
|
}
|
||||||
|
buffer += l;
|
||||||
|
maxlen -= l;
|
||||||
|
got += l;
|
||||||
|
}else{
|
||||||
|
DBG("no new data after %d bytes (%s)", got, buffer - got);
|
||||||
break;
|
break;
|
||||||
} else return -1;
|
}
|
||||||
}while(1);
|
tv = mntRtmout;
|
||||||
return (int)byte;
|
}while(maxlen);
|
||||||
|
return got;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear data from input buffer
|
// clear data from input buffer
|
||||||
static void clrmntbuf(){
|
static void clrmntbuf(){
|
||||||
if(mntfd < 0) return;
|
if(mntfd < 0) return;
|
||||||
uint8_t byte;
|
uint8_t bytes[256];
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
//double t0 = nanotime();
|
|
||||||
//int n = 0;
|
|
||||||
do{
|
do{
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_SET(mntfd, &rfds);
|
FD_SET(mntfd, &rfds);
|
||||||
@@ -270,12 +233,11 @@ static void clrmntbuf(){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(FD_ISSET(mntfd, &rfds)){
|
if(FD_ISSET(mntfd, &rfds)){
|
||||||
ssize_t l = read(mntfd, &byte, 1);
|
ssize_t l = read(mntfd, &bytes, 256);
|
||||||
if(l != 1) break;
|
if(l < 1) break;
|
||||||
//++n;
|
DBG("clr got %zd bytes: %s", l, bytes);
|
||||||
} else break;
|
}else break;
|
||||||
}while(1);
|
}while(1);
|
||||||
//DBG("Cleared by %g (got %d bytes)", nanotime() - t0, n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// main encoder thread (for separate encoder): read next data and make parsing
|
// main encoder thread (for separate encoder): read next data and make parsing
|
||||||
@@ -283,8 +245,8 @@ static void *encoderthread1(void _U_ *u){
|
|||||||
if(Conf.SepEncoder != 1) return NULL;
|
if(Conf.SepEncoder != 1) return NULL;
|
||||||
uint8_t databuf[ENC_DATALEN];
|
uint8_t databuf[ENC_DATALEN];
|
||||||
int wridx = 0, errctr = 0;
|
int wridx = 0, errctr = 0;
|
||||||
double t = 0.;
|
struct timespec tcur;
|
||||||
while(encfd[0] > -1 && errctr < MAX_ERR_CTR){
|
while(encfd[0] > -1 && errctr < MAX_ERR_CTR && !GlobExit){
|
||||||
int b = getencbyte();
|
int b = getencbyte();
|
||||||
if(b == -2) ++errctr;
|
if(b == -2) ++errctr;
|
||||||
if(b < 0) continue;
|
if(b < 0) continue;
|
||||||
@@ -294,13 +256,14 @@ static void *encoderthread1(void _U_ *u){
|
|||||||
if((uint8_t)b == ENC_MAGICK){
|
if((uint8_t)b == ENC_MAGICK){
|
||||||
// DBG("Got magic -> start filling packet");
|
// DBG("Got magic -> start filling packet");
|
||||||
databuf[wridx++] = (uint8_t) b;
|
databuf[wridx++] = (uint8_t) b;
|
||||||
t = nanotime();
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}else databuf[wridx++] = (uint8_t) b;
|
}else databuf[wridx++] = (uint8_t) b;
|
||||||
if(wridx == ENC_DATALEN){
|
if(wridx == ENC_DATALEN){
|
||||||
parse_encbuf(databuf, t);
|
if(curtime(&tcur)){
|
||||||
wridx = 0;
|
parse_encbuf(databuf, &tcur);
|
||||||
|
wridx = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(encfd[0] > -1){
|
if(encfd[0] > -1){
|
||||||
@@ -310,49 +273,172 @@ static void *encoderthread1(void _U_ *u){
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define XYBUFSZ (128)
|
||||||
|
typedef struct{
|
||||||
|
char buf[XYBUFSZ+1];
|
||||||
|
int len;
|
||||||
|
} buf_t;
|
||||||
|
|
||||||
|
// write to buffer next data portion; return FALSE in case of error
|
||||||
|
static int readstrings(buf_t *buf, int fd){
|
||||||
|
if(!buf){DBG("Empty buffer"); return FALSE;}
|
||||||
|
int L = XYBUFSZ - buf->len;
|
||||||
|
if(L < 0){
|
||||||
|
DBG("buf not initialized!");
|
||||||
|
buf->len = 0;
|
||||||
|
}
|
||||||
|
if(L == 0){
|
||||||
|
DBG("buffer overfull: %d!", buf->len);
|
||||||
|
char *lastn = strrchr(buf->buf, '\n');
|
||||||
|
if(lastn){
|
||||||
|
fprintf(stderr, "BUFOVR: _%s_", buf->buf);
|
||||||
|
++lastn;
|
||||||
|
buf->len = XYBUFSZ - (lastn - buf->buf);
|
||||||
|
DBG("Memmove %d", buf->len);
|
||||||
|
memmove(lastn, buf->buf, buf->len);
|
||||||
|
buf->buf[buf->len] = 0;
|
||||||
|
}else buf->len = 0;
|
||||||
|
L = XYBUFSZ - buf->len;
|
||||||
|
}
|
||||||
|
//DBG("read %d bytes from %d", L, fd);
|
||||||
|
int got = read(fd, &buf->buf[buf->len], L);
|
||||||
|
if(got < 0){
|
||||||
|
DBG("read()");
|
||||||
|
return FALSE;
|
||||||
|
}else if(got == 0){ DBG("NO data"); return TRUE; }
|
||||||
|
buf->len += got;
|
||||||
|
buf->buf[buf->len] = 0;
|
||||||
|
//DBG("buf[%d]: %s", buf->len, buf->buf);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return TRUE if got, FALSE if no data found
|
||||||
|
static int getdata(buf_t *buf, long *out){
|
||||||
|
if(!buf || buf->len < 1 || buf->len > (XYBUFSZ+1)){
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// DBG("got data");
|
||||||
|
// read record between last '\n' and previous (or start of string)
|
||||||
|
char *last = &buf->buf[buf->len - 1];
|
||||||
|
//DBG("buf: _%s_", buf->buf);
|
||||||
|
if(*last != '\n') return FALSE;
|
||||||
|
*last = 0;
|
||||||
|
//DBG("buf: _%s_", buf->buf);
|
||||||
|
char *prev = strrchr(buf->buf, '\n');
|
||||||
|
if(!prev) prev = buf->buf;
|
||||||
|
else{
|
||||||
|
fprintf(stderr, "MORETHANONE: _%s_", buf->buf);
|
||||||
|
++prev; // after last '\n'
|
||||||
|
}
|
||||||
|
if(out) *out = atol(prev);
|
||||||
|
// clear buffer
|
||||||
|
buf->len = 0;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to write '\n' asking new data portion; return FALSE if failed
|
||||||
|
static int asknext(int fd){
|
||||||
|
//FNAME();
|
||||||
|
if(fd < 0) return FALSE;
|
||||||
|
int i = 0;
|
||||||
|
for(; i < 5; ++i){
|
||||||
|
int l = write(fd, "\n", 1);
|
||||||
|
//DBG("l=%d", l);
|
||||||
|
if(1 == l) return TRUE;
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
DBG("5 tries... failed!");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
// main encoder thread for separate encoders as USB devices /dev/encoder_X0 and /dev/encoder_Y0
|
// main encoder thread for separate encoders as USB devices /dev/encoder_X0 and /dev/encoder_Y0
|
||||||
static void *encoderthread2(void _U_ *u){
|
static void *encoderthread2(void _U_ *u){
|
||||||
if(Conf.SepEncoder != 2) return NULL;
|
if(Conf.SepEncoder != 2) return NULL;
|
||||||
DBG("Thread started");
|
DBG("Thread started");
|
||||||
|
struct pollfd pfds[2];
|
||||||
|
pfds[0].fd = encfd[0]; pfds[0].events = POLLIN;
|
||||||
|
pfds[1].fd = encfd[1]; pfds[1].events = POLLIN;
|
||||||
|
double t0[2], tstart;
|
||||||
|
buf_t strbuf[2] = {0};
|
||||||
|
long msrlast[2]; // last encoder data
|
||||||
|
double mtlast[2]; // last measurement time
|
||||||
|
asknext(encfd[0]); asknext(encfd[1]);
|
||||||
|
t0[0] = t0[1] = tstart = timefromstart();
|
||||||
int errctr = 0;
|
int errctr = 0;
|
||||||
double t0 = nanotime();
|
|
||||||
const char *req = "\n";
|
// init Kalman for both axes
|
||||||
int need2ask = 0; // need or not to ask encoder for new data
|
Kalman3 kf[2];
|
||||||
while(encfd[0] > -1 && encfd[1] > -1 && errctr < MAX_ERR_CTR){
|
double dt = Conf.EncoderReqInterval; // 1ms encoders step
|
||||||
if(need2ask){
|
double sigma_jx = 1e-6, sigma_jy = 1e-6; // "jerk" sigma
|
||||||
if(1 != write(encfd[0], req, 1)) { ++errctr; continue; }
|
double xnoice = encoder_noise(X_ENC_STEPSPERREV);
|
||||||
else if(1 != write(encfd[1], req, 1)) { ++errctr; continue; }
|
double ynoice = encoder_noise(Y_ENC_STEPSPERREV);
|
||||||
|
kalman3_init(&kf[0], dt, xnoice);
|
||||||
|
kalman3_init(&kf[1], dt, ynoice);
|
||||||
|
kalman3_set_jerk_noise(&kf[0], sigma_jx);
|
||||||
|
kalman3_set_jerk_noise(&kf[1], sigma_jy);
|
||||||
|
|
||||||
|
do{ // main cycle
|
||||||
|
if(poll(pfds, 2, 0) < 0){
|
||||||
|
DBG("poll()");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
double v, t;
|
int got = 0;
|
||||||
if(getencval(encfd[0], &v, &t)){
|
for(int i = 0; i < 2; ++i){
|
||||||
mountdata.encXposition.val = X_ENC2RAD(v);
|
if(pfds[i].revents && POLLIN){
|
||||||
//DBG("encX(%g) = %g", t, mountdata.encXposition.val);
|
if(!readstrings(&strbuf[i], encfd[i])){
|
||||||
mountdata.encXposition.t = t;
|
DBG("ERR");
|
||||||
//if(t - lastXenc.t > Conf.EncoderSpeedInterval) getXspeed();
|
++errctr;
|
||||||
getXspeed();
|
break;
|
||||||
if(getencval(encfd[1], &v, &t)){
|
}
|
||||||
mountdata.encYposition.val = Y_ENC2RAD(v);
|
}
|
||||||
//DBG("encY(%g) = %g", t, mountdata.encYposition.val);
|
double curt = timefromstart();
|
||||||
mountdata.encYposition.t = t;
|
if(getdata(&strbuf[i], &msrlast[i])) mtlast[i] = curt;
|
||||||
//if(t - lastYenc.t > Conf.EncoderSpeedInterval) getYspeed();
|
if(curt - t0[i] >= Conf.EncoderReqInterval){ // get last records
|
||||||
getYspeed();
|
//DBG("last rec %d, curt=%g, t0=%g, mtlast=%g", i, curt, t0[i], mtlast[i]);
|
||||||
errctr = 0;
|
if(curt - mtlast[i] < 1.5*Conf.EncoderReqInterval){
|
||||||
need2ask = 0;
|
//DBG("time OK");
|
||||||
} else {
|
pthread_mutex_lock(&datamutex);
|
||||||
if(need2ask) ++errctr;
|
double pos = (double)msrlast[i];
|
||||||
else need2ask = 1;
|
if(i == 0){
|
||||||
continue;
|
pos = Xenc2rad(pos);
|
||||||
|
// Kalman filtering
|
||||||
|
kalman3_predict(&kf[i]);
|
||||||
|
kalman3_update(&kf[i], pos);
|
||||||
|
//DBG("Got pos=%g, kalman: angle=%g, vel=%g, acc=%g",
|
||||||
|
// pos, kf[i].x[0], kf[i].x[1], kf[i].x[2]);
|
||||||
|
mountdata.encXposition.val = kf[i].x[0];
|
||||||
|
curtime(&mountdata.encXposition.t);
|
||||||
|
/*DBG("msrlast=%ld, Xpos.val=%g, t=%zd; XEzero=%d, SPR=%g",
|
||||||
|
msrlast[i], mountdata.encXposition.val, mountdata.encXposition.t.tv_sec,
|
||||||
|
X_ENC_ZERO, X_ENC_STEPSPERREV);*/
|
||||||
|
getXspeed();
|
||||||
|
//mountdata.encXspeed.val = kf[i].x[1];
|
||||||
|
//mountdata.encXspeed.t = mountdata.encXposition.t;
|
||||||
|
}else{
|
||||||
|
pos = Yenc2rad(pos);
|
||||||
|
kalman3_predict(&kf[i]);
|
||||||
|
kalman3_update(&kf[i], pos);
|
||||||
|
//DBG("Got pos=%g, kalman: angle=%g, vel=%g, acc=%g",
|
||||||
|
// pos, kf[i].x[0], kf[i].x[1], kf[i].x[2]);
|
||||||
|
mountdata.encYposition.val = kf[i].x[0];
|
||||||
|
curtime(&mountdata.encYposition.t);
|
||||||
|
getYspeed();
|
||||||
|
//mountdata.encYspeed.val = kf[i].x[1];
|
||||||
|
//mountdata.encYspeed.t = mountdata.encYposition.t;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&datamutex);
|
||||||
|
}
|
||||||
|
if(!asknext(encfd[i])){
|
||||||
|
++errctr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t0[i] = (curt - t0[i] < 2.*Conf.EncoderReqInterval) ? t0[i] + Conf.EncoderReqInterval : curt;
|
||||||
|
++got;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if(need2ask) ++errctr;
|
|
||||||
else need2ask = 1;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
while(nanotime() - t0 < Conf.EncoderReqInterval){ usleep(50); }
|
if(got == 2) errctr = 0;
|
||||||
//DBG("DT=%g (RI=%g)", nanotime()-t0, Conf.EncoderReqInterval);
|
}while(encfd[0] > -1 && encfd[1] > -1 && errctr < MAX_ERR_CTR && !GlobExit);
|
||||||
t0 = nanotime();
|
DBG("\n\nEXIT: ERRCTR=%d", errctr);
|
||||||
}
|
|
||||||
DBG("ERRCTR=%d", errctr);
|
|
||||||
for(int i = 0; i < 2; ++i){
|
for(int i = 0; i < 2; ++i){
|
||||||
if(encfd[i] > -1){
|
if(encfd[i] > -1){
|
||||||
close(encfd[i]);
|
close(encfd[i]);
|
||||||
@@ -378,53 +464,77 @@ void data_free(data_t **x){
|
|||||||
*x = NULL;
|
*x = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void chkModStopped(double *prev, double cur, int *nstopped, axis_status_t *stat){
|
||||||
|
if(!prev || !nstopped || !stat) return;
|
||||||
|
if(isnan(*prev)){
|
||||||
|
*stat = AXIS_STOPPED;
|
||||||
|
DBG("START");
|
||||||
|
}else if(*stat != AXIS_STOPPED){
|
||||||
|
if(fabs(*prev - cur) < DBL_EPSILON && ++(*nstopped) > MOTOR_STOPPED_CNT){
|
||||||
|
*stat = AXIS_STOPPED;
|
||||||
|
DBG("AXIS stopped; prev=%g, cur=%g; nstopped=%d", *prev/M_PI*180., cur/M_PI*180., *nstopped);
|
||||||
|
}
|
||||||
|
}else if(*prev != cur){
|
||||||
|
DBG("AXIS moving");
|
||||||
|
*nstopped = 0;
|
||||||
|
}
|
||||||
|
*prev = cur;
|
||||||
|
}
|
||||||
|
|
||||||
// main mount thread
|
// main mount thread
|
||||||
static void *mountthread(void _U_ *u){
|
static void *mountthread(void _U_ *u){
|
||||||
int errctr = 0;
|
int errctr = 0;
|
||||||
uint8_t buf[2*sizeof(SSstat)];
|
uint8_t buf[sizeof(SSstat)];
|
||||||
SSstat *status = (SSstat*) buf;
|
SSstat *status = (SSstat*) buf;
|
||||||
bzero(&mountdata, sizeof(mountdata));
|
bzero(&mountdata, sizeof(mountdata));
|
||||||
if(Conf.RunModel) while(1){
|
double t0 = timefromstart(), tstart = t0, tcur = t0;
|
||||||
pthread_mutex_lock(&datamutex);
|
double oldmt = -100.; // old `millis measurement` time
|
||||||
// now change data
|
static uint32_t oldmillis = 0;
|
||||||
getModData(&mountdata);
|
if(Conf.RunModel){
|
||||||
pthread_mutex_unlock(&datamutex);
|
double Xprev = NAN, Yprev = NAN; // previous coordinates
|
||||||
double t0 = nanotime();
|
int xcnt = 0, ycnt = 0;
|
||||||
while(nanotime() - t0 < Conf.EncoderReqInterval) usleep(50);
|
while(!GlobExit){
|
||||||
t0 = nanotime();
|
coordpair_t c;
|
||||||
|
movestate_t xst, yst;
|
||||||
|
// now change data
|
||||||
|
getModData(&c, &xst, &yst);
|
||||||
|
struct timespec tnow;
|
||||||
|
if(!curtime(&tnow) || (tcur = timefromstart()) < 0.) continue;
|
||||||
|
pthread_mutex_lock(&datamutex);
|
||||||
|
mountdata.encXposition.t = mountdata.encYposition.t = tnow;
|
||||||
|
mountdata.encXposition.val = c.X + (drand48() - 0.5)*1e-6; // .2arcsec error
|
||||||
|
mountdata.encYposition.val = c.Y + (drand48() - 0.5)*1e-6;
|
||||||
|
//DBG("t=%g, X=%g, Y=%g", tnow, c.X.val, c.Y.val);
|
||||||
|
if(tcur - oldmt > Conf.MountReqInterval){
|
||||||
|
oldmillis = mountdata.millis = (uint32_t)((tcur - tstart) * 1e3);
|
||||||
|
mountdata.motYposition.t = mountdata.motXposition.t = tnow;
|
||||||
|
if(xst == ST_MOVE)
|
||||||
|
mountdata.motXposition.val = c.X + (c.X - mountdata.motXposition.val)*(drand48() - 0.5)/100.;
|
||||||
|
//else
|
||||||
|
// mountdata.motXposition.val = c.X;
|
||||||
|
if(yst == ST_MOVE)
|
||||||
|
mountdata.motYposition.val = c.Y + (c.Y - mountdata.motYposition.val)*(drand48() - 0.5)/100.;
|
||||||
|
//else
|
||||||
|
// mountdata.motYposition.val = c.Y;
|
||||||
|
oldmt = tcur;
|
||||||
|
}else mountdata.millis = oldmillis;
|
||||||
|
chkModStopped(&Xprev, c.X, &xcnt, &mountdata.Xstate);
|
||||||
|
chkModStopped(&Yprev, c.Y, &ycnt, &mountdata.Ystate);
|
||||||
|
getXspeed(); getYspeed();
|
||||||
|
pthread_mutex_unlock(&datamutex);
|
||||||
|
while(timefromstart() - t0 < Conf.EncoderReqInterval) usleep(50);
|
||||||
|
t0 = timefromstart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// data to get
|
// data to get
|
||||||
data_t d = {.buf = buf, .maxlen = sizeof(buf)};
|
data_t d = {.buf = buf, .maxlen = sizeof(buf)};
|
||||||
// cmd to send
|
// cmd to send
|
||||||
data_t *cmd_getstat = cmd2dat(CMD_GETSTAT);
|
data_t *cmd_getstat = cmd2dat(CMD_GETSTAT);
|
||||||
if(!cmd_getstat) goto failed;
|
if(!cmd_getstat) goto failed;
|
||||||
while(mntfd > -1 && errctr < MAX_ERR_CTR){
|
while(mntfd > -1 && errctr < MAX_ERR_CTR && !GlobExit){
|
||||||
// read data to status
|
// read data to status
|
||||||
double t0 = nanotime();
|
struct timespec tcur;
|
||||||
#if 0
|
if(!curtime(&tcur)) continue;
|
||||||
// 127 milliseconds to get answer on X/Y commands!!!
|
|
||||||
int64_t ans;
|
|
||||||
int ctr = 0;
|
|
||||||
if(SSgetint(CMD_MOTX, &ans)){
|
|
||||||
pthread_mutex_lock(&datamutex);
|
|
||||||
mountdata.motXposition.t = tgot;
|
|
||||||
mountdata.motXposition.val = X_MOT2RAD(ans);
|
|
||||||
pthread_mutex_unlock(&datamutex);
|
|
||||||
++ctr;
|
|
||||||
}
|
|
||||||
tgot = nanotime();
|
|
||||||
if(SSgetint(CMD_MOTY, &ans)){
|
|
||||||
pthread_mutex_lock(&datamutex);
|
|
||||||
mountdata.motXposition.t = tgot;
|
|
||||||
mountdata.motXposition.val = X_MOT2RAD(ans);
|
|
||||||
pthread_mutex_unlock(&datamutex);
|
|
||||||
++ctr;
|
|
||||||
}
|
|
||||||
if(ctr == 2){
|
|
||||||
mountdata.millis = (uint32_t)(1e3 * tgot);
|
|
||||||
DBG("Got both coords; millis=%d", mountdata.millis);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// 80 milliseconds to get answer on GETSTAT
|
// 80 milliseconds to get answer on GETSTAT
|
||||||
if(!MountWriteRead(cmd_getstat, &d) || d.len != sizeof(SSstat)){
|
if(!MountWriteRead(cmd_getstat, &d) || d.len != sizeof(SSstat)){
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
@@ -441,14 +551,13 @@ static void *mountthread(void _U_ *u){
|
|||||||
errctr = 0;
|
errctr = 0;
|
||||||
pthread_mutex_lock(&datamutex);
|
pthread_mutex_lock(&datamutex);
|
||||||
// now change data
|
// now change data
|
||||||
SSconvstat(status, &mountdata, t0);
|
SSconvstat(status, &mountdata, &tcur);
|
||||||
pthread_mutex_unlock(&datamutex);
|
pthread_mutex_unlock(&datamutex);
|
||||||
//DBG("GOT FULL stat by %g", nanotime() - t0);
|
|
||||||
// allow writing & getters
|
// allow writing & getters
|
||||||
do{
|
do{
|
||||||
usleep(500);
|
usleep(500);
|
||||||
}while(nanotime() - t0 < Conf.MountReqInterval);
|
}while(timefromstart() - t0 < Conf.MountReqInterval);
|
||||||
t0 = nanotime();
|
t0 = timefromstart();
|
||||||
}
|
}
|
||||||
data_free(&cmd_getstat);
|
data_free(&cmd_getstat);
|
||||||
failed:
|
failed:
|
||||||
@@ -464,17 +573,28 @@ static int ttyopen(const char *path, speed_t speed){
|
|||||||
int fd = -1;
|
int fd = -1;
|
||||||
struct termios2 tty;
|
struct termios2 tty;
|
||||||
DBG("Try to open %s @ %d", path, speed);
|
DBG("Try to open %s @ %d", path, speed);
|
||||||
if((fd = open(path, O_RDWR|O_NOCTTY)) < 0) return -1;
|
if((fd = open(path, O_RDWR|O_NOCTTY)) < 0){
|
||||||
if(ioctl(fd, TCGETS2, &tty)){ close(fd); return -1; }
|
DBG("Can't open device %s: %s", path, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(ioctl(fd, TCGETS2, &tty)){
|
||||||
|
DBG("Can't read TTY settings");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||||
tty.c_iflag = 0; // don't do any changes in input stream
|
tty.c_iflag = 0; // don't do any changes in input stream
|
||||||
tty.c_oflag = 0; // don't do any changes in output stream
|
tty.c_oflag = 0; // don't do any changes in output stream
|
||||||
tty.c_cflag = BOTHER | CS8 | CREAD | CLOCAL; // other speed, 8bit, RW, ignore line ctrl
|
tty.c_cflag = BOTHER | CS8 | CREAD | CLOCAL; // other speed, 8bit, RW, ignore line ctrl
|
||||||
tty.c_ispeed = speed;
|
tty.c_ispeed = speed;
|
||||||
tty.c_ospeed = speed;
|
tty.c_ospeed = speed;
|
||||||
//tty.c_cc[VMIN] = 0; // non-canonical mode
|
tty.c_cc[VMIN] = 0; // non-canonical mode
|
||||||
//tty.c_cc[VTIME] = 5;
|
tty.c_cc[VTIME] = 5;
|
||||||
if(ioctl(fd, TCSETS2, &tty)){ close(fd); return -1; }
|
if(ioctl(fd, TCSETS2, &tty)){
|
||||||
|
DBG("Can't set TTY settings");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
DBG("Check speed: i=%d, o=%d", tty.c_ispeed, tty.c_ospeed);
|
DBG("Check speed: i=%d, o=%d", tty.c_ispeed, tty.c_ospeed);
|
||||||
if(tty.c_ispeed != (speed_t) speed || tty.c_ospeed != (speed_t)speed){ close(fd); return -1; }
|
if(tty.c_ispeed != (speed_t) speed || tty.c_ospeed != (speed_t)speed){ close(fd); return -1; }
|
||||||
// try to set exclusive
|
// try to set exclusive
|
||||||
@@ -484,15 +604,18 @@ static int ttyopen(const char *path, speed_t speed){
|
|||||||
|
|
||||||
// return FALSE if failed
|
// return FALSE if failed
|
||||||
int openEncoder(){
|
int openEncoder(){
|
||||||
|
// TODO: open real devices in "model" mode too!
|
||||||
if(Conf.RunModel) return TRUE;
|
if(Conf.RunModel) return TRUE;
|
||||||
if(!Conf.SepEncoder) return FALSE; // try to open separate encoder when it's absent
|
if(!Conf.SepEncoder) return FALSE; // try to open separate encoder when it's absent
|
||||||
|
/*
|
||||||
|
encRtmout.tv_sec = 0;
|
||||||
|
encRtmout.tv_usec = 100000000 / Conf.EncoderDevSpeed; // 10 bytes
|
||||||
|
*/
|
||||||
if(Conf.SepEncoder == 1){ // only one device
|
if(Conf.SepEncoder == 1){ // only one device
|
||||||
DBG("One device");
|
DBG("One device");
|
||||||
if(encfd[0] > -1) close(encfd[0]);
|
if(encfd[0] > -1) close(encfd[0]);
|
||||||
encfd[0] = ttyopen(Conf.EncoderDevPath, (speed_t) Conf.EncoderDevSpeed);
|
encfd[0] = ttyopen(Conf.EncoderDevPath, (speed_t) Conf.EncoderDevSpeed);
|
||||||
if(encfd[0] < 0) return FALSE;
|
if(encfd[0] < 0) return FALSE;
|
||||||
encRtmout.tv_sec = 0;
|
|
||||||
encRtmout.tv_usec = 100000000 / Conf.EncoderDevSpeed; // 10 bytes
|
|
||||||
if(pthread_create(&encthread, NULL, encoderthread1, NULL)){
|
if(pthread_create(&encthread, NULL, encoderthread1, NULL)){
|
||||||
close(encfd[0]);
|
close(encfd[0]);
|
||||||
encfd[0] = -1;
|
encfd[0] = -1;
|
||||||
@@ -506,8 +629,6 @@ int openEncoder(){
|
|||||||
encfd[i] = ttyopen(paths[i], (speed_t) Conf.EncoderDevSpeed);
|
encfd[i] = ttyopen(paths[i], (speed_t) Conf.EncoderDevSpeed);
|
||||||
if(encfd[i] < 0) return FALSE;
|
if(encfd[i] < 0) return FALSE;
|
||||||
}
|
}
|
||||||
encRtmout.tv_sec = 0;
|
|
||||||
encRtmout.tv_usec = 1000; // 1ms
|
|
||||||
if(pthread_create(&encthread, NULL, encoderthread2, NULL)){
|
if(pthread_create(&encthread, NULL, encoderthread2, NULL)){
|
||||||
for(int i = 0; i < 2; ++i){
|
for(int i = 0; i < 2; ++i){
|
||||||
close(encfd[i]);
|
close(encfd[i]);
|
||||||
@@ -522,6 +643,7 @@ int openEncoder(){
|
|||||||
|
|
||||||
// return FALSE if failed
|
// return FALSE if failed
|
||||||
int openMount(){
|
int openMount(){
|
||||||
|
// TODO: open real devices in "model" mode too!
|
||||||
if(Conf.RunModel) goto create_thread;
|
if(Conf.RunModel) goto create_thread;
|
||||||
if(mntfd > -1) close(mntfd);
|
if(mntfd > -1) close(mntfd);
|
||||||
DBG("Open mount %s @ %d", Conf.MountDevPath, Conf.MountDevSpeed);
|
DBG("Open mount %s @ %d", Conf.MountDevPath, Conf.MountDevSpeed);
|
||||||
@@ -529,16 +651,13 @@ int openMount(){
|
|||||||
if(mntfd < 0) return FALSE;
|
if(mntfd < 0) return FALSE;
|
||||||
DBG("mntfd=%d", mntfd);
|
DBG("mntfd=%d", mntfd);
|
||||||
// clear buffer
|
// clear buffer
|
||||||
while(getmntbyte() > -1);
|
clrmntbuf();
|
||||||
/*int g = write(mntfd, "XXS\r", 4);
|
/*
|
||||||
DBG("Written %d", g);
|
mnt1Rtmout.tv_sec = 0;
|
||||||
uint8_t buf[100];
|
mnt1Rtmout.tv_usec = 500000000 / Conf.MountDevSpeed; // 50 bytes * 10bits / speed
|
||||||
do{
|
|
||||||
ssize_t l = read(mntfd, buf, 100);
|
|
||||||
DBG("got %zd", l);
|
|
||||||
}while(1);*/
|
|
||||||
mntRtmout.tv_sec = 0;
|
mntRtmout.tv_sec = 0;
|
||||||
mntRtmout.tv_usec = 500000000 / Conf.MountDevSpeed; // 50 bytes * 10bits / speed
|
mntRtmout.tv_usec = mnt1Rtmout.tv_usec / 50;
|
||||||
|
*/
|
||||||
create_thread:
|
create_thread:
|
||||||
if(pthread_create(&mntthread, NULL, mountthread, NULL)){
|
if(pthread_create(&mntthread, NULL, mountthread, NULL)){
|
||||||
DBG("Can't create mount thread");
|
DBG("Can't create mount thread");
|
||||||
@@ -554,14 +673,17 @@ create_thread:
|
|||||||
|
|
||||||
// close all opened serial devices and quit threads
|
// close all opened serial devices and quit threads
|
||||||
void closeSerial(){
|
void closeSerial(){
|
||||||
if(Conf.RunModel) return;
|
GlobExit = 1;
|
||||||
|
DBG("Give 100ms to proper close");
|
||||||
|
usleep(100000);
|
||||||
|
DBG("Force closed all devices");
|
||||||
if(mntfd > -1){
|
if(mntfd > -1){
|
||||||
DBG("Cancel mount thread");
|
DBG("Cancel mount thread");
|
||||||
pthread_cancel(mntthread);
|
pthread_cancel(mntthread);
|
||||||
DBG("join mount thread");
|
DBG("join mount thread");
|
||||||
pthread_join(mntthread, NULL);
|
pthread_join(mntthread, NULL);
|
||||||
DBG("close mount fd");
|
DBG("close mount fd");
|
||||||
close(mntfd);
|
if(mntfd > -1) close(mntfd);
|
||||||
mntfd = -1;
|
mntfd = -1;
|
||||||
}
|
}
|
||||||
if(encfd[0] > -1){
|
if(encfd[0] > -1){
|
||||||
@@ -570,13 +692,14 @@ void closeSerial(){
|
|||||||
DBG("join encoder thread");
|
DBG("join encoder thread");
|
||||||
pthread_join(encthread, NULL);
|
pthread_join(encthread, NULL);
|
||||||
DBG("close encoder's fd");
|
DBG("close encoder's fd");
|
||||||
close(encfd[0]);
|
if(encfd[0] > -1) close(encfd[0]);
|
||||||
encfd[0] = -1;
|
encfd[0] = -1;
|
||||||
if(Conf.SepEncoder == 2){
|
if(Conf.SepEncoder == 2 && encfd[1] > -1){
|
||||||
close(encfd[1]);
|
close(encfd[1]);
|
||||||
encfd[1] = -1;
|
encfd[1] = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GlobExit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get fresh encoder information
|
// get fresh encoder information
|
||||||
@@ -585,6 +708,8 @@ mcc_errcodes_t getMD(mountdata_t *d){
|
|||||||
pthread_mutex_lock(&datamutex);
|
pthread_mutex_lock(&datamutex);
|
||||||
*d = mountdata;
|
*d = mountdata;
|
||||||
pthread_mutex_unlock(&datamutex);
|
pthread_mutex_unlock(&datamutex);
|
||||||
|
//DBG("ENCpos: %.10g/%.10g", d->encXposition.val, d->encYposition.val);
|
||||||
|
//DBG("millis: %u, encxt: %zd (time: %zd)", d->millis, d->encXposition.t.tv_sec, time(NULL));
|
||||||
return MCC_E_OK;
|
return MCC_E_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,32 +727,29 @@ static int wr(const data_t *out, data_t *in, int needeol){
|
|||||||
DBG("Wrong arguments or no mount fd");
|
DBG("Wrong arguments or no mount fd");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
//DBG("clrbuf");
|
||||||
clrmntbuf();
|
clrmntbuf();
|
||||||
//double t0 = nanotime();
|
|
||||||
if(out){
|
if(out){
|
||||||
|
//DBG("write %zd bytes (%s)", out->len, out->buf);
|
||||||
if(out->len != (size_t)write(mntfd, out->buf, out->len)){
|
if(out->len != (size_t)write(mntfd, out->buf, out->len)){
|
||||||
DBG("written bytes not equal to need");
|
DBG("written bytes not equal to need");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
//DBG("Send to mount %zd bytes: %s", out->len, out->buf);
|
//DBG("eol, mntfd=%d", mntfd);
|
||||||
if(needeol){
|
if(needeol){
|
||||||
int g = write(mntfd, "\r", 1); // add EOL
|
int g = write(mntfd, "\r", 1); // add EOL
|
||||||
(void) g;
|
(void) g;
|
||||||
}
|
}
|
||||||
|
//usleep(50000); // add little pause so that the idiot has time to swallow
|
||||||
}
|
}
|
||||||
//DBG("sent by %g", nanotime() - t0);
|
if(!in || in->maxlen < 1) return TRUE;
|
||||||
//uint8_t buf[256];
|
int got = readmntdata(in->buf, in->maxlen);
|
||||||
//data_t dumb = {.buf = buf, .maxlen = 256};
|
if(got < 0){
|
||||||
if(!in) return TRUE;
|
DBG("Error reading mount data!");
|
||||||
//if(!in) in = &dumb; // even if user don't ask for answer, try to read to clear trash
|
in->len = 0;
|
||||||
in->len = 0;
|
return FALSE;
|
||||||
for(size_t i = 0; i < in->maxlen; ++i){
|
|
||||||
int b = getmntbyte();
|
|
||||||
if(b < 0) break; // nothing to read -> go out
|
|
||||||
in->buf[in->len++] = (uint8_t) b;
|
|
||||||
}
|
}
|
||||||
//DBG("got %zd bytes by %g", in->len, nanotime() - t0);
|
in->len = got;
|
||||||
while(getmntbyte() > -1);
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,22 +760,24 @@ static int wr(const data_t *out, data_t *in, int needeol){
|
|||||||
* @return FALSE if failed
|
* @return FALSE if failed
|
||||||
*/
|
*/
|
||||||
int MountWriteRead(const data_t *out, data_t *in){
|
int MountWriteRead(const data_t *out, data_t *in){
|
||||||
if(Conf.RunModel) return -1;
|
if(Conf.RunModel) return FALSE;
|
||||||
|
//double t0 = timefromstart();
|
||||||
pthread_mutex_lock(&mntmutex);
|
pthread_mutex_lock(&mntmutex);
|
||||||
int ret = wr(out, in, 1);
|
int ret = wr(out, in, 1);
|
||||||
pthread_mutex_unlock(&mntmutex);
|
pthread_mutex_unlock(&mntmutex);
|
||||||
|
//DBG("Got %gus", (timefromstart()-t0)*1e6);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
// send binary data - without EOL
|
// send binary data - without EOL
|
||||||
int MountWriteReadRaw(const data_t *out, data_t *in){
|
int MountWriteReadRaw(const data_t *out, data_t *in){
|
||||||
if(Conf.RunModel) return -1;
|
if(Conf.RunModel) return FALSE;
|
||||||
pthread_mutex_lock(&mntmutex);
|
pthread_mutex_lock(&mntmutex);
|
||||||
int ret = wr(out, in, 0);
|
int ret = wr(out, in, 0);
|
||||||
pthread_mutex_unlock(&mntmutex);
|
pthread_mutex_unlock(&mntmutex);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EBUG
|
#if 0
|
||||||
static void logscmd(SSscmd *c){
|
static void logscmd(SSscmd *c){
|
||||||
printf("Xmot=%d, Ymot=%d, Xspeed=%d, Yspeed=%d\n", c->Xmot, c->Ymot, c->Xspeed, c->Yspeed);
|
printf("Xmot=%d, Ymot=%d, Xspeed=%d, Yspeed=%d\n", c->Xmot, c->Ymot, c->Xspeed, c->Yspeed);
|
||||||
printf("xychange=0x%02X, Xbits=0x%02X, Ybits=0x%02X\n", c->xychange, c->XBits, c->YBits);
|
printf("xychange=0x%02X, Xbits=0x%02X, Ybits=0x%02X\n", c->xychange, c->XBits, c->YBits);
|
||||||
@@ -676,31 +800,37 @@ static int bincmd(uint8_t *cmd, int len){
|
|||||||
if(!dlcmd) dlcmd = cmd2dat(CMD_LONGCMD);
|
if(!dlcmd) dlcmd = cmd2dat(CMD_LONGCMD);
|
||||||
int ret = FALSE;
|
int ret = FALSE;
|
||||||
pthread_mutex_lock(&mntmutex);
|
pthread_mutex_lock(&mntmutex);
|
||||||
// dummy buffer to clear trash in input
|
|
||||||
//char ans[300];
|
|
||||||
//data_t a = {.buf = (uint8_t*)ans, .maxlen=299};
|
|
||||||
if(len == sizeof(SSscmd)){
|
if(len == sizeof(SSscmd)){
|
||||||
((SSscmd*)cmd)->checksum = SScalcChecksum(cmd, len-2);
|
((SSscmd*)cmd)->checksum = SScalcChecksum(cmd, len-2);
|
||||||
DBG("Short command");
|
//DBG("Short command");
|
||||||
#ifdef EBUG
|
#if 0
|
||||||
logscmd((SSscmd*)cmd);
|
logscmd((SSscmd*)cmd);
|
||||||
#endif
|
#endif
|
||||||
if(!wr(dscmd, NULL, 1)) goto rtn;
|
if(!wr(dscmd, NULL, 1)) goto rtn;
|
||||||
}else if(len == sizeof(SSlcmd)){
|
}else if(len == sizeof(SSlcmd)){
|
||||||
((SSlcmd*)cmd)->checksum = SScalcChecksum(cmd, len-2);
|
((SSlcmd*)cmd)->checksum = SScalcChecksum(cmd, len-2);
|
||||||
DBG("Long command");
|
// DBG("Long command");
|
||||||
#ifdef EBUG
|
#if 0
|
||||||
loglcmd((SSlcmd*)cmd);
|
loglcmd((SSlcmd*)cmd);
|
||||||
#endif
|
#endif
|
||||||
if(!wr(dlcmd, NULL, 1)) goto rtn;
|
if(!wr(dlcmd, NULL, 1)) goto rtn;
|
||||||
}else{
|
}else{
|
||||||
goto rtn;
|
goto rtn;
|
||||||
}
|
}
|
||||||
data_t d;
|
SSstat ans;
|
||||||
|
data_t d, in;
|
||||||
d.buf = cmd;
|
d.buf = cmd;
|
||||||
d.len = d.maxlen = len;
|
d.len = d.maxlen = len;
|
||||||
ret = wr(&d, NULL, 0);
|
in.buf = (uint8_t*)&ans; in.maxlen = sizeof(SSstat);
|
||||||
|
ret = wr(&d, &in, 0);
|
||||||
DBG("%s", ret ? "SUCCESS" : "FAIL");
|
DBG("%s", ret ? "SUCCESS" : "FAIL");
|
||||||
|
if(ret){
|
||||||
|
SSscmd *sc = (SSscmd*)cmd;
|
||||||
|
mountdata.Xtarget = sc->Xmot;
|
||||||
|
mountdata.Ytarget = sc->Ymot;
|
||||||
|
DBG("ANS: Xmot/Ymot: %d/%d, Ylast/Ylast: %d/%d; Xtag/Ytag: %d/%d",
|
||||||
|
ans.Xmot, ans.Ymot, ans.XLast, ans.YLast, mountdata.Xtarget, mountdata.Ytarget);
|
||||||
|
}
|
||||||
rtn:
|
rtn:
|
||||||
pthread_mutex_unlock(&mntmutex);
|
pthread_mutex_unlock(&mntmutex);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -730,16 +860,23 @@ int cmdC(SSconfig *conf, int rw){
|
|||||||
}else{ // read
|
}else{ // read
|
||||||
data_t d;
|
data_t d;
|
||||||
d.buf = (uint8_t *) conf;
|
d.buf = (uint8_t *) conf;
|
||||||
d.len = 0; d.maxlen = sizeof(SSconfig);
|
d.len = 0; d.maxlen = 0;
|
||||||
|
ret = wr(rcmd, &d, 1);
|
||||||
|
DBG("write command: %s", ret ? "TRUE" : "FALSE");
|
||||||
|
if(!ret) goto rtn;
|
||||||
|
// make a huge pause for stupid SSII
|
||||||
|
usleep(100000);
|
||||||
|
d.len = 0; d.maxlen = sizeof(SSconfig);
|
||||||
ret = wr(rcmd, &d, 1);
|
ret = wr(rcmd, &d, 1);
|
||||||
DBG("wr returned %s; got %zd bytes of %zd", ret ? "TRUE" : "FALSE", d.len, d.maxlen);
|
DBG("wr returned %s; got %zd bytes of %zd", ret ? "TRUE" : "FALSE", d.len, d.maxlen);
|
||||||
if(d.len != d.maxlen) return FALSE;
|
if(d.len != d.maxlen){ ret = FALSE; goto rtn; }
|
||||||
// simplest checksum
|
// simplest checksum
|
||||||
uint16_t sum = 0;
|
uint16_t sum = 0;
|
||||||
for(uint32_t i = 0; i < sizeof(SSconfig)-2; ++i) sum += d.buf[i];
|
for(uint32_t i = 0; i < sizeof(SSconfig)-2; ++i) sum += d.buf[i];
|
||||||
if(sum != conf->checksum){
|
if(sum != conf->checksum){
|
||||||
DBG("got sum: %u, need: %u", conf->checksum, sum);
|
DBG("got sum: %u, need: %u", conf->checksum, sum);
|
||||||
return FALSE;
|
ret = FALSE;
|
||||||
|
goto rtn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rtn:
|
rtn:
|
||||||
|
|||||||
23
Auxiliary_utils/LibSidServo/servo_real.conf
Normal file
23
Auxiliary_utils/LibSidServo/servo_real.conf
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
MountDevPath=/dev/ttyUSB0
|
||||||
|
MountDevSpeed=19200
|
||||||
|
EncoderDevSpeed=1000000
|
||||||
|
MountReqInterval=0.1
|
||||||
|
EncoderReqInterval=0.001
|
||||||
|
SepEncoder=2
|
||||||
|
EncoderXDevPath=/dev/encoder_X0
|
||||||
|
EncoderYDevPath=/dev/encoder_Y0
|
||||||
|
EncoderSpeedInterval=0.05
|
||||||
|
RunModel=0
|
||||||
|
# telescope is in "pointing state" when coordinate error less than MaxFinePointingErr and goes to "slewing state"
|
||||||
|
# when this error greater than MaxPointingErr
|
||||||
|
MaxPointingErr = 0.3490658504 # "pointing zone" - 20 degr
|
||||||
|
MaxFinePointingErr = 0.1745329252 # "guiding zone" - 10 degr
|
||||||
|
MaxGuidingErr = 4.8481368e-6 # "on target zone" - 1 arcsec
|
||||||
|
XPIDVP=0.9
|
||||||
|
XPIDVI=0.0005
|
||||||
|
XPIDVD=0.0
|
||||||
|
YPIDVP=0.5
|
||||||
|
YPIDVI=0.005
|
||||||
|
YPIDVD=0.
|
||||||
|
XEncZero=36627112
|
||||||
|
YEncZero=36067741
|
||||||
@@ -32,37 +32,13 @@ extern "C"
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
// acceptable position error - 0.1''
|
// minimal serial speed of mount device
|
||||||
#define MCC_POSITION_ERROR (5e-7)
|
#define MOUNT_BAUDRATE_MIN (1200)
|
||||||
// acceptable disagreement between motor and axis encoders - 2''
|
|
||||||
#define MCC_ENCODERS_ERROR (1e-7)
|
|
||||||
|
|
||||||
// max speeds (rad/s): xs=10 deg/s, ys=8 deg/s
|
|
||||||
#define MCC_MAX_X_SPEED (0.174533)
|
|
||||||
#define MCC_MAX_Y_SPEED (0.139626)
|
|
||||||
// accelerations by both axis (for model); TODO: move speeds/accelerations into config?
|
|
||||||
// xa=12.6 deg/s^2, ya= 9.5 deg/s^2
|
|
||||||
#define MCC_X_ACCELERATION (0.219911)
|
|
||||||
#define MCC_Y_ACCELERATION (0.165806)
|
|
||||||
|
|
||||||
// max speed interval, seconds
|
// max speed interval, seconds
|
||||||
#define MCC_CONF_MAX_SPEEDINT (2.)
|
#define MCC_CONF_MAX_SPEEDINT (2.)
|
||||||
// minimal speed interval in parts of EncoderReqInterval
|
// minimal speed interval in parts of EncoderReqInterval
|
||||||
#define MCC_CONF_MIN_SPEEDC (3.)
|
#define MCC_CONF_MIN_SPEEDC (3.)
|
||||||
// PID I cycle time (analog of "RC" for PID on opamps)
|
|
||||||
#define MCC_PID_CYCLE_TIME (5.)
|
|
||||||
// maximal PID refresh time interval (if larger all old data will be cleared)
|
|
||||||
#define MCC_PID_MAX_DT (1.)
|
|
||||||
// normal PID refresh interval
|
|
||||||
#define MCC_PID_REFRESH_DT (0.1)
|
|
||||||
// boundary conditions for axis state: "slewing/pointing/guiding"
|
|
||||||
// if angle < MCC_MAX_POINTING_ERR, change state from "slewing" to "pointing": 5 degrees
|
|
||||||
//#define MCC_MAX_POINTING_ERR (0.20943951)
|
|
||||||
#define MCC_MAX_POINTING_ERR (0.08726646)
|
|
||||||
// if angle < MCC_MAX_GUIDING_ERR, chane state from "pointing" to "guiding": 1.5 deg
|
|
||||||
#define MCC_MAX_GUIDING_ERR (0.026179939)
|
|
||||||
// if error less than this value we suppose that target is captured and guiding is good: 0.1''
|
|
||||||
#define MCC_MAX_ATTARGET_ERR (4.8481368e-7)
|
|
||||||
|
|
||||||
// error codes
|
// error codes
|
||||||
typedef enum{
|
typedef enum{
|
||||||
@@ -72,6 +48,7 @@ typedef enum{
|
|||||||
MCC_E_ENCODERDEV, // encoder device error or can't open
|
MCC_E_ENCODERDEV, // encoder device error or can't open
|
||||||
MCC_E_MOUNTDEV, // mount device error or can't open
|
MCC_E_MOUNTDEV, // mount device error or can't open
|
||||||
MCC_E_FAILED, // failed to run command - protocol error
|
MCC_E_FAILED, // failed to run command - protocol error
|
||||||
|
MCC_E_AMOUNT // Just amount of errors
|
||||||
} mcc_errcodes_t;
|
} mcc_errcodes_t;
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
@@ -86,14 +63,23 @@ typedef struct{
|
|||||||
int SepEncoder; // ==1 if encoder works as separate serial device, ==2 if there's new version with two devices
|
int SepEncoder; // ==1 if encoder works as separate serial device, ==2 if there's new version with two devices
|
||||||
char* EncoderXDevPath; // paths to new controller devices
|
char* EncoderXDevPath; // paths to new controller devices
|
||||||
char* EncoderYDevPath;
|
char* EncoderYDevPath;
|
||||||
|
double EncodersDisagreement; // acceptable disagreement between motor and axis encoders
|
||||||
double MountReqInterval; // interval between subsequent mount requests (seconds)
|
double MountReqInterval; // interval between subsequent mount requests (seconds)
|
||||||
double EncoderReqInterval; // interval between subsequent encoder requests (seconds)
|
double EncoderReqInterval; // interval between subsequent encoder requests (seconds)
|
||||||
double EncoderSpeedInterval; // interval between speed calculations
|
double EncoderSpeedInterval; // interval between speed calculations
|
||||||
int RunModel; // == 1 if you want to use model instead of real mount
|
int RunModel; // == 1 if you want to use model instead of real mount
|
||||||
|
double PIDMaxDt; // maximal PID refresh time interval (if larger all old data will be cleared)
|
||||||
|
double PIDRefreshDt; // normal PID refresh interval
|
||||||
|
double PIDCycleDt; // PID I cycle time (analog of "RC" for PID on opamps)
|
||||||
PIDpar_t XPIDC; // gain parameters of PID for both axiss (C - coordinate driven, V - velocity driven)
|
PIDpar_t XPIDC; // gain parameters of PID for both axiss (C - coordinate driven, V - velocity driven)
|
||||||
PIDpar_t XPIDV;
|
PIDpar_t XPIDV;
|
||||||
PIDpar_t YPIDC;
|
PIDpar_t YPIDC;
|
||||||
PIDpar_t YPIDV;
|
PIDpar_t YPIDV;
|
||||||
|
double MaxPointingErr; // if angle < this, change state from "slewing" to "pointing" (coarse pointing): 8 degrees
|
||||||
|
double MaxFinePointingErr; // if angle < this, chane state from "pointing" to "guiding" (fine poinging): 1.5 deg
|
||||||
|
double MaxGuidingErr; // if error less than this value we suppose that target is captured and guiding is good (true guiding): 0.1''
|
||||||
|
int XEncZero; // encoders' zero position
|
||||||
|
int YEncZero;
|
||||||
} conf_t;
|
} conf_t;
|
||||||
|
|
||||||
// coordinates/speeds in degrees or d/s: X, Y
|
// coordinates/speeds in degrees or d/s: X, Y
|
||||||
@@ -104,7 +90,7 @@ typedef struct{
|
|||||||
// coordinate/speed and time of last measurement
|
// coordinate/speed and time of last measurement
|
||||||
typedef struct{
|
typedef struct{
|
||||||
double val;
|
double val;
|
||||||
double t;
|
struct timespec t;
|
||||||
} coordval_t;
|
} coordval_t;
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
@@ -150,10 +136,11 @@ typedef struct{
|
|||||||
} extradata_t;
|
} extradata_t;
|
||||||
|
|
||||||
typedef enum{
|
typedef enum{
|
||||||
AXIS_STOPPED,
|
AXIS_STOPPED, // stop
|
||||||
AXIS_SLEWING,
|
AXIS_GONNASTOP, // stop command run
|
||||||
AXIS_POINTING,
|
AXIS_SLEWING, // go to target with maximal speed
|
||||||
AXIS_GUIDING,
|
AXIS_POINTING, // axis is in pointing zone, use PID
|
||||||
|
AXIS_GUIDING, // near target
|
||||||
AXIS_ERROR,
|
AXIS_ERROR,
|
||||||
} axis_status_t;
|
} axis_status_t;
|
||||||
|
|
||||||
@@ -171,6 +158,9 @@ typedef struct{
|
|||||||
uint32_t millis;
|
uint32_t millis;
|
||||||
double temperature;
|
double temperature;
|
||||||
double voltage;
|
double voltage;
|
||||||
|
// target X/Y position by last `short` or `long` command
|
||||||
|
int32_t Xtarget; // in SidServo's counts
|
||||||
|
int32_t Ytarget; // -//-
|
||||||
} mountdata_t;
|
} mountdata_t;
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
@@ -205,6 +195,9 @@ typedef struct{
|
|||||||
double outplimit; // Output Limit, percent (0..100)
|
double outplimit; // Output Limit, percent (0..100)
|
||||||
double currlimit; // Current Limit (A)
|
double currlimit; // Current Limit (A)
|
||||||
double intlimit; // Integral Limit (???)
|
double intlimit; // Integral Limit (???)
|
||||||
|
// these params are taken from mount by text commands (don't save negative values - better save these marks in xybits
|
||||||
|
double motor_stepsperrev;// encoder's steps per revolution: motor and axis
|
||||||
|
double axis_stepsperrev; // negative sign of these values means reverse direction
|
||||||
} __attribute__((packed)) axis_config_t;
|
} __attribute__((packed)) axis_config_t;
|
||||||
|
|
||||||
// hardware configuration
|
// hardware configuration
|
||||||
@@ -246,7 +239,7 @@ typedef struct{
|
|||||||
void (*quit)(); // deinit
|
void (*quit)(); // deinit
|
||||||
mcc_errcodes_t (*getMountData)(mountdata_t *d); // get last data
|
mcc_errcodes_t (*getMountData)(mountdata_t *d); // get last data
|
||||||
// mcc_errcodes_t (*slewTo)(const coordpair_t *target, slewflags_t flags);
|
// mcc_errcodes_t (*slewTo)(const coordpair_t *target, slewflags_t flags);
|
||||||
mcc_errcodes_t (*correctTo)(const coordval_pair_t *target, const coordpair_t *endpoint);
|
mcc_errcodes_t (*correctTo)(const coordval_pair_t *target);
|
||||||
mcc_errcodes_t (*moveTo)(const coordpair_t *target); // move to given position and stop
|
mcc_errcodes_t (*moveTo)(const coordpair_t *target); // move to given position and stop
|
||||||
mcc_errcodes_t (*moveWspeed)(const coordpair_t *target, const coordpair_t *speed); // move with given max speed
|
mcc_errcodes_t (*moveWspeed)(const coordpair_t *target, const coordpair_t *speed); // move with given max speed
|
||||||
mcc_errcodes_t (*setSpeed)(const coordpair_t *tagspeed); // set speed
|
mcc_errcodes_t (*setSpeed)(const coordpair_t *tagspeed); // set speed
|
||||||
@@ -256,7 +249,13 @@ typedef struct{
|
|||||||
mcc_errcodes_t (*longCmd)(long_command_t *cmd); // send/get long command
|
mcc_errcodes_t (*longCmd)(long_command_t *cmd); // send/get long command
|
||||||
mcc_errcodes_t (*getHWconfig)(hardware_configuration_t *c); // get hardware configuration
|
mcc_errcodes_t (*getHWconfig)(hardware_configuration_t *c); // get hardware configuration
|
||||||
mcc_errcodes_t (*saveHWconfig)(hardware_configuration_t *c); // save hardware configuration
|
mcc_errcodes_t (*saveHWconfig)(hardware_configuration_t *c); // save hardware configuration
|
||||||
double (*currentT)(); // current time
|
int (*currentT)(struct timespec *t); // current time
|
||||||
|
double (*timeFromStart)(); // amount of seconds from last init
|
||||||
|
double (*timeDiff)(const struct timespec *time1, const struct timespec *time0); // difference of times
|
||||||
|
double (*timeDiff0)(const struct timespec *time1); // difference between current time and last init time
|
||||||
|
mcc_errcodes_t (*getMaxSpeed)(coordpair_t *v); // maximal speed by both axis
|
||||||
|
mcc_errcodes_t (*getMinSpeed)(coordpair_t *v); // minimal -//-
|
||||||
|
mcc_errcodes_t (*getAcceleration)(coordpair_t *a); // acceleration/deceleration
|
||||||
} mount_t;
|
} mount_t;
|
||||||
|
|
||||||
extern mount_t Mount;
|
extern mount_t Mount;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@@ -25,6 +26,13 @@
|
|||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "ssii.h"
|
#include "ssii.h"
|
||||||
|
|
||||||
|
int X_ENC_ZERO = 0, Y_ENC_ZERO = 0; // will be filled later from config
|
||||||
|
// defaults until read from controller
|
||||||
|
double X_MOT_STEPSPERREV = 13312000.,
|
||||||
|
Y_MOT_STEPSPERREV = 17578668.,
|
||||||
|
X_ENC_STEPSPERREV = 67108864.,
|
||||||
|
Y_ENC_STEPSPERREV = 67108864.;
|
||||||
|
|
||||||
uint16_t SScalcChecksum(uint8_t *buf, int len){
|
uint16_t SScalcChecksum(uint8_t *buf, int len){
|
||||||
uint16_t checksum = 0;
|
uint16_t checksum = 0;
|
||||||
for(int i = 0; i < len; i++){
|
for(int i = 0; i < len; i++){
|
||||||
@@ -37,27 +45,36 @@ uint16_t SScalcChecksum(uint8_t *buf, int len){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next three functions runs under locked mountdata_t mutex and shouldn't call locked it again!!
|
// Next three functions runs under locked mountdata_t mutex and shouldn't call locked it again!!
|
||||||
static void chkstopstat(int32_t *prev, int32_t cur, int *nstopped, axis_status_t *stat){
|
static axis_status_t chkstopstat(int32_t *prev, int32_t cur, int32_t tag, int *nstopped, axis_status_t stat){
|
||||||
if(*prev == INT32_MAX){
|
if(*prev == INT32_MAX){
|
||||||
*stat = AXIS_STOPPED;
|
stat = AXIS_STOPPED;
|
||||||
DBG("START");
|
DBG("START");
|
||||||
}else if(*stat != AXIS_STOPPED){
|
}else if(stat == AXIS_GONNASTOP || (stat != AXIS_STOPPED && cur == tag)){ // got command "stop" or motor is on target
|
||||||
if(*prev == cur && ++(*nstopped) > MOTOR_STOPPED_CNT){
|
if(*prev == cur){
|
||||||
*stat = AXIS_STOPPED;
|
DBG("Test for stop, nstopped=%d", *nstopped);
|
||||||
DBG("AXIS stopped");
|
if(++(*nstopped) > MOTOR_STOPPED_CNT){
|
||||||
}
|
stat = AXIS_STOPPED;
|
||||||
|
DBG("AXIS stopped");
|
||||||
|
}
|
||||||
|
}else *nstopped = 0;
|
||||||
}else if(*prev != cur){
|
}else if(*prev != cur){
|
||||||
DBG("AXIS moving");
|
DBG("AXIS moving");
|
||||||
*nstopped = 0;
|
*nstopped = 0;
|
||||||
}
|
}
|
||||||
*prev = cur;
|
*prev = cur;
|
||||||
|
return stat;
|
||||||
}
|
}
|
||||||
// check for stopped/pointing states
|
// check for stopped/pointing states
|
||||||
static void ChkStopped(const SSstat *s, mountdata_t *m){
|
static void ChkStopped(const SSstat *s, mountdata_t *m){
|
||||||
static int32_t Xmot_prev = INT32_MAX, Ymot_prev = INT32_MAX; // previous coordinates
|
static int32_t Xmot_prev = INT32_MAX, Ymot_prev = INT32_MAX; // previous coordinates
|
||||||
static int Xnstopped = 0, Ynstopped = 0; // counters to get STOPPED state
|
static int Xnstopped = 0, Ynstopped = 0; // counters to get STOPPED state
|
||||||
chkstopstat(&Xmot_prev, s->Xmot, &Xnstopped, &m->Xstate);
|
axis_status_t Xstat, Ystat;
|
||||||
chkstopstat(&Ymot_prev, s->Ymot, &Ynstopped, &m->Ystate);
|
Xstat = chkstopstat(&Xmot_prev, s->Xmot, m->Xtarget, &Xnstopped, m->Xstate);
|
||||||
|
Ystat = chkstopstat(&Ymot_prev, s->Ymot, m->Ytarget, &Ynstopped, m->Ystate);
|
||||||
|
if(Xstat != m->Xstate || Ystat != m->Ystate){
|
||||||
|
DBG("Status changed");
|
||||||
|
setStat(Xstat, Ystat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,17 +83,19 @@ static void ChkStopped(const SSstat *s, mountdata_t *m){
|
|||||||
* @param m (o) - output
|
* @param m (o) - output
|
||||||
* @param t - measurement time
|
* @param t - measurement time
|
||||||
*/
|
*/
|
||||||
void SSconvstat(const SSstat *s, mountdata_t *m, double t){
|
void SSconvstat(const SSstat *s, mountdata_t *m, struct timespec *t){
|
||||||
if(!s || !m) return;
|
if(!s || !m || !t) return;
|
||||||
m->motXposition.val = X_MOT2RAD(s->Xmot);
|
m->motXposition.val = X_MOT2RAD(s->Xmot);
|
||||||
m->motYposition.val = Y_MOT2RAD(s->Ymot);
|
m->motYposition.val = Y_MOT2RAD(s->Ymot);
|
||||||
ChkStopped(s, m);
|
ChkStopped(s, m);
|
||||||
m->motXposition.t = m->motYposition.t = t;
|
m->motXposition.t = m->motYposition.t = *t;
|
||||||
// fill encoder data from here, as there's no separate enc thread
|
// fill encoder data from here, as there's no separate enc thread
|
||||||
if(!Conf.SepEncoder){
|
if(!Conf.SepEncoder){
|
||||||
m->encXposition.val = X_ENC2RAD(s->Xenc);
|
DBG("ENCODER from SSII");
|
||||||
m->encYposition.val = Y_ENC2RAD(s->Yenc);
|
m->encXposition.val = Xenc2rad(s->Xenc);
|
||||||
m->encXposition.t = m->encYposition.t = t;
|
DBG("encx: %g", m->encXposition.val);
|
||||||
|
m->encYposition.val = Yenc2rad(s->Yenc);
|
||||||
|
m->encXposition.t = m->encYposition.t = *t;
|
||||||
getXspeed(); getYspeed();
|
getXspeed(); getYspeed();
|
||||||
}
|
}
|
||||||
m->keypad = s->keypad;
|
m->keypad = s->keypad;
|
||||||
@@ -160,14 +179,17 @@ int SSsetterI(const char *cmd, int32_t ival){
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SSstop(int emerg){
|
int SSstop(int emerg){
|
||||||
|
FNAME();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
const char *cmdx = (emerg) ? CMD_EMSTOPX : CMD_STOPX;
|
const char *cmdx = (emerg) ? CMD_EMSTOPX : CMD_STOPX;
|
||||||
const char *cmdy = (emerg) ? CMD_EMSTOPY : CMD_STOPY;
|
const char *cmdy = (emerg) ? CMD_EMSTOPY : CMD_STOPY;
|
||||||
|
setStat(AXIS_GONNASTOP, AXIS_GONNASTOP);
|
||||||
for(; i < 10; ++i){
|
for(; i < 10; ++i){
|
||||||
if(!SStextcmd(cmdx, NULL)) continue;
|
if(!SStextcmd(cmdx, NULL)) continue;
|
||||||
if(SStextcmd(cmdy, NULL)) break;
|
if(SStextcmd(cmdy, NULL)) break;
|
||||||
}
|
}
|
||||||
if(i == 10) return FALSE;
|
if(i == 10) return FALSE;
|
||||||
|
DBG("Stopped");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,33 +197,39 @@ int SSstop(int emerg){
|
|||||||
mcc_errcodes_t updateMotorPos(){
|
mcc_errcodes_t updateMotorPos(){
|
||||||
mountdata_t md = {0};
|
mountdata_t md = {0};
|
||||||
if(Conf.RunModel) return MCC_E_OK;
|
if(Conf.RunModel) return MCC_E_OK;
|
||||||
double t0 = nanotime(), t = 0.;
|
double t0 = timefromstart(), t = 0.;
|
||||||
|
struct timespec curt;
|
||||||
DBG("start @ %g", t0);
|
DBG("start @ %g", t0);
|
||||||
do{
|
do{
|
||||||
t = nanotime();
|
t = timefromstart();
|
||||||
|
if(!curtime(&curt)){
|
||||||
|
usleep(10000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//DBG("XENC2RAD: %g (xez=%d, xesr=%.10g)", Xenc2rad(32424842), X_ENC_ZERO, X_ENC_STEPSPERREV);
|
||||||
if(MCC_E_OK == getMD(&md)){
|
if(MCC_E_OK == getMD(&md)){
|
||||||
if(md.encXposition.t == 0 || md.encYposition.t == 0){
|
if(md.encXposition.t.tv_sec == 0 || md.encYposition.t.tv_sec == 0){
|
||||||
DBG("Just started, t-t0 = %g!", t - t0);
|
DBG("Just started? t-t0 = %g!", t - t0);
|
||||||
sleep(1);
|
usleep(10000);
|
||||||
DBG("t-t0 = %g", nanotime() - t0);
|
|
||||||
//usleep(10000);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DBG("got; t pos x/y: %g/%g; tnow: %g", md.encXposition.t, md.encYposition.t, t);
|
if(md.Xstate != AXIS_STOPPED || md.Ystate != AXIS_STOPPED) return MCC_E_OK;
|
||||||
|
DBG("got; t pos x/y: %ld/%ld; tnow: %ld", md.encXposition.t.tv_sec, md.encYposition.t.tv_sec, curt.tv_sec);
|
||||||
mcc_errcodes_t OK = MCC_E_OK;
|
mcc_errcodes_t OK = MCC_E_OK;
|
||||||
if(fabs(md.motXposition.val - md.encXposition.val) > MCC_ENCODERS_ERROR && md.Xstate == AXIS_STOPPED){
|
if(fabs(md.motXposition.val - md.encXposition.val) > Conf.EncodersDisagreement && md.Xstate == AXIS_STOPPED){
|
||||||
DBG("NEED to sync X: motors=%g, axiss=%g", md.motXposition.val, md.encXposition.val);
|
DBG("NEED to sync X: motors=%g, axis=%g", md.motXposition.val, md.encXposition.val);
|
||||||
|
DBG("new motsteps: %d", X_RAD2MOT(md.encXposition.val));
|
||||||
if(!SSsetterI(CMD_MOTXSET, X_RAD2MOT(md.encXposition.val))){
|
if(!SSsetterI(CMD_MOTXSET, X_RAD2MOT(md.encXposition.val))){
|
||||||
DBG("Xpos sync failed!");
|
DBG("Xpos sync failed!");
|
||||||
OK = MCC_E_FAILED;
|
OK = MCC_E_FAILED;
|
||||||
}else DBG("Xpos sync OK, Dt=%g", nanotime() - t0);
|
}else DBG("Xpos sync OK, Dt=%g", t - t0);
|
||||||
}
|
}
|
||||||
if(fabs(md.motYposition.val - md.encYposition.val) > MCC_ENCODERS_ERROR && md.Xstate == AXIS_STOPPED){
|
if(fabs(md.motYposition.val - md.encYposition.val) > Conf.EncodersDisagreement && md.Ystate == AXIS_STOPPED){
|
||||||
DBG("NEED to sync Y: motors=%g, axiss=%g", md.motYposition.val, md.encYposition.val);
|
DBG("NEED to sync Y: motors=%g, axis=%g", md.motYposition.val, md.encYposition.val);
|
||||||
if(!SSsetterI(CMD_MOTYSET, Y_RAD2MOT(md.encYposition.val))){
|
if(!SSsetterI(CMD_MOTYSET, Y_RAD2MOT(md.encYposition.val))){
|
||||||
DBG("Ypos sync failed!");
|
DBG("Ypos sync failed!");
|
||||||
OK = MCC_E_FAILED;
|
OK = MCC_E_FAILED;
|
||||||
}else DBG("Ypos sync OK, Dt=%g", nanotime() - t0);
|
}else DBG("Ypos sync OK, Dt=%g", t - t0);
|
||||||
}
|
}
|
||||||
if(MCC_E_OK == OK){
|
if(MCC_E_OK == OK){
|
||||||
DBG("Encoders synced");
|
DBG("Encoders synced");
|
||||||
|
|||||||
@@ -173,64 +173,74 @@
|
|||||||
#define SITECH_LOOP_FREQUENCY (1953.)
|
#define SITECH_LOOP_FREQUENCY (1953.)
|
||||||
|
|
||||||
// amount of consequent same coordinates to detect stop
|
// amount of consequent same coordinates to detect stop
|
||||||
#define MOTOR_STOPPED_CNT (4)
|
#define MOTOR_STOPPED_CNT (19)
|
||||||
|
|
||||||
|
// replace macros with global variables inited when config read
|
||||||
|
extern int X_ENC_ZERO, Y_ENC_ZERO;
|
||||||
|
extern double X_MOT_STEPSPERREV, Y_MOT_STEPSPERREV, X_ENC_STEPSPERREV, Y_ENC_STEPSPERREV;
|
||||||
|
|
||||||
// TODO: take it from settings?
|
// TODO: take it from settings?
|
||||||
// steps per revolution (SSI - x4 - for SSI)
|
// steps per revolution (SSI - x4 - for SSI)
|
||||||
#define X_MOT_STEPSPERREV_SSI (13312000.)
|
// -> hwconf.Xconf.mot/enc_stepsperrev
|
||||||
|
//#define X_MOT_STEPSPERREV_SSI (13312000.)
|
||||||
// 13312000 / 4 = 3328000
|
// 13312000 / 4 = 3328000
|
||||||
#define X_MOT_STEPSPERREV (3328000.)
|
//#define X_MOT_STEPSPERREV (3328000.)
|
||||||
#define Y_MOT_STEPSPERREV_SSI (17578668.)
|
//#define Y_MOT_STEPSPERREV_SSI (17578668.)
|
||||||
// 17578668 / 4 = 4394667
|
// 17578668 / 4 = 4394667
|
||||||
#define Y_MOT_STEPSPERREV (4394667.)
|
//#define Y_MOT_STEPSPERREV (4394667.)
|
||||||
|
|
||||||
// encoder per revolution
|
// encoder per revolution
|
||||||
#define X_ENC_STEPSPERREV (67108864.)
|
//#define X_ENC_STEPSPERREV (67108864.)
|
||||||
#define Y_ENC_STEPSPERREV (67108864.)
|
//#define Y_ENC_STEPSPERREV (67108864.)
|
||||||
// encoder zero position
|
// encoder zero position
|
||||||
#define X_ENC_ZERO (61245239)
|
// -> conf.XEncZero/YEncZero
|
||||||
#define Y_ENC_ZERO (36999830)
|
//#define X_ENC_ZERO (61245239)
|
||||||
// encoder reversed (no: +1)
|
//#define Y_ENC_ZERO (36999830)
|
||||||
#define X_ENC_SIGN (-1.)
|
// encoder reversed (no: +1) -> sign of ...stepsperrev
|
||||||
#define Y_ENC_SIGN (-1.)
|
//#define X_ENC_SIGN (-1.)
|
||||||
|
//#define Y_ENC_SIGN (-1.)
|
||||||
|
|
||||||
|
|
||||||
// encoder position to radians and back
|
// encoder position to radians and back
|
||||||
#define X_ENC2RAD(n) ang2half(X_ENC_SIGN * 2.*M_PI * ((double)((n)-X_ENC_ZERO)) / X_ENC_STEPSPERREV)
|
#define Xenc2rad(n) ang2half(2.*M_PI * ((double)((n)-(X_ENC_ZERO))) / (X_ENC_STEPSPERREV))
|
||||||
#define Y_ENC2RAD(n) ang2half(Y_ENC_SIGN * 2.*M_PI * ((double)((n)-Y_ENC_ZERO)) / Y_ENC_STEPSPERREV)
|
#define Yenc2rad(n) ang2half(2.*M_PI * ((double)((n)-(Y_ENC_ZERO))) / (Y_ENC_STEPSPERREV))
|
||||||
#define X_RAD2ENC(r) ((uint32_t)((r) / 2./M_PI * X_ENC_STEPSPERREV))
|
#define Xrad2enc(r) ((uint32_t)((r) / 2./M_PI * (X_ENC_STEPSPERREV)))
|
||||||
#define Y_RAD2ENC(r) ((uint32_t)((r) / 2./M_PI * Y_ENC_STEPSPERREV))
|
#define Yrad2enc(r) ((uint32_t)((r) / 2./M_PI * (Y_ENC_STEPSPERREV)))
|
||||||
|
|
||||||
// convert angle in radians to +-pi
|
// convert angle in radians to +-pi
|
||||||
static inline double ang2half(double ang){
|
static inline __attribute__((always_inline)) double ang2half(double ang){
|
||||||
|
ang = fmod(ang, 2.*M_PI);
|
||||||
if(ang < -M_PI) ang += 2.*M_PI;
|
if(ang < -M_PI) ang += 2.*M_PI;
|
||||||
else if(ang > M_PI) ang -= 2.*M_PI;
|
else if(ang > M_PI) ang -= 2.*M_PI;
|
||||||
return ang;
|
return ang;
|
||||||
}
|
}
|
||||||
// convert to only positive: 0..2pi
|
// convert to only positive: 0..2pi
|
||||||
static inline double ang2full(double ang){
|
static inline __attribute__((always_inline)) double ang2full(double ang){
|
||||||
|
ang = fmod(ang, 2.*M_PI);
|
||||||
if(ang < 0.) ang += 2.*M_PI;
|
if(ang < 0.) ang += 2.*M_PI;
|
||||||
else if(ang > 2.*M_PI) ang -= 2.*M_PI;
|
else if(ang > 2.*M_PI) ang -= 2.*M_PI;
|
||||||
return ang;
|
return ang;
|
||||||
}
|
}
|
||||||
|
|
||||||
// motor position to radians and back
|
// motor position to radians and back
|
||||||
#define X_MOT2RAD(n) ang2half(2. * M_PI * ((double)(n)) / X_MOT_STEPSPERREV)
|
#define X_MOT2RAD(n) ang2half(2. * M_PI * ((double)(n)) / (X_MOT_STEPSPERREV))
|
||||||
#define Y_MOT2RAD(n) ang2half(2. * M_PI * ((double)(n)) / Y_MOT_STEPSPERREV)
|
#define Y_MOT2RAD(n) ang2half(2. * M_PI * ((double)(n)) / (Y_MOT_STEPSPERREV))
|
||||||
#define X_RAD2MOT(r) ((int32_t)((r) / (2. * M_PI) * X_MOT_STEPSPERREV))
|
#define X_RAD2MOT(r) ((int32_t)((r) / (2. * M_PI) * (X_MOT_STEPSPERREV)))
|
||||||
#define Y_RAD2MOT(r) ((int32_t)((r) / (2. * M_PI) * Y_MOT_STEPSPERREV))
|
#define Y_RAD2MOT(r) ((int32_t)((r) / (2. * M_PI) * (Y_MOT_STEPSPERREV)))
|
||||||
// motor speed in rad/s and back
|
// motor speed in rad/s and back
|
||||||
#define X_MOTSPD2RS(n) (X_MOT2RAD(n) / 65536. * SITECH_LOOP_FREQUENCY)
|
#define X_MOTSPD2RS(n) (X_MOT2RAD(n) / 65536. * (SITECH_LOOP_FREQUENCY))
|
||||||
#define Y_MOTSPD2RS(n) (Y_MOT2RAD(n) / 65536. * SITECH_LOOP_FREQUENCY)
|
#define Y_MOTSPD2RS(n) (Y_MOT2RAD(n) / 65536. * (SITECH_LOOP_FREQUENCY))
|
||||||
#define X_RS2MOTSPD(r) ((int32_t)(X_RAD2MOT(r) * 65536. / SITECH_LOOP_FREQUENCY))
|
#define X_RS2MOTSPD(r) ((int32_t)(X_RAD2MOT(r) * 65536. / (SITECH_LOOP_FREQUENCY)))
|
||||||
#define Y_RS2MOTSPD(r) ((int32_t)(Y_RAD2MOT(r) * 65536. / SITECH_LOOP_FREQUENCY))
|
#define Y_RS2MOTSPD(r) ((int32_t)(Y_RAD2MOT(r) * 65536. / (SITECH_LOOP_FREQUENCY)))
|
||||||
// motor acceleration -//-
|
// motor acceleration -//-
|
||||||
#define X_MOTACC2RS(n) (X_MOT2RAD(n) / 65536. * SITECH_LOOP_FREQUENCY * SITECH_LOOP_FREQUENCY)
|
#define X_MOTACC2RS(n) (X_MOT2RAD(n) / 65536. * (SITECH_LOOP_FREQUENCY) * (SITECH_LOOP_FREQUENCY))
|
||||||
#define Y_MOTACC2RS(n) (Y_MOT2RAD(n) / 65536. * SITECH_LOOP_FREQUENCY * SITECH_LOOP_FREQUENCY)
|
#define Y_MOTACC2RS(n) (Y_MOT2RAD(n) / 65536. * (SITECH_LOOP_FREQUENCY) * (SITECH_LOOP_FREQUENCY))
|
||||||
#define X_RS2MOTACC(r) ((int32_t)(X_RAD2MOT(r) * 65536. / SITECH_LOOP_FREQUENCY / SITECH_LOOP_FREQUENCY))
|
#define X_RS2MOTACC(r) ((int32_t)(X_RAD2MOT(r) * 65536. / (SITECH_LOOP_FREQUENCY) / (SITECH_LOOP_FREQUENCY)))
|
||||||
#define Y_RS2MOTACC(r) ((int32_t)(Y_RAD2MOT(r) * 65536. / SITECH_LOOP_FREQUENCY / SITECH_LOOP_FREQUENCY))
|
#define Y_RS2MOTACC(r) ((int32_t)(Y_RAD2MOT(r) * 65536. / (SITECH_LOOP_FREQUENCY) / (SITECH_LOOP_FREQUENCY)))
|
||||||
|
|
||||||
// adder time to seconds vice versa
|
// adder time to seconds vice versa
|
||||||
#define ADDER2S(a) ((a) / SITECH_LOOP_FREQUENCY)
|
#define ADDER2S(a) ((a) / (SITECH_LOOP_FREQUENCY))
|
||||||
#define S2ADDER(s) ((s) * SITECH_LOOP_FREQUENCY)
|
#define S2ADDER(s) ((s) * (SITECH_LOOP_FREQUENCY))
|
||||||
|
|
||||||
// encoder's tolerance (ticks)
|
// encoder's tolerance (ticks)
|
||||||
#define YencTOL (25.)
|
#define YencTOL (25.)
|
||||||
@@ -331,7 +341,7 @@ typedef struct{
|
|||||||
} __attribute__((packed)) SSconfig;
|
} __attribute__((packed)) SSconfig;
|
||||||
|
|
||||||
uint16_t SScalcChecksum(uint8_t *buf, int len);
|
uint16_t SScalcChecksum(uint8_t *buf, int len);
|
||||||
void SSconvstat(const SSstat *status, mountdata_t *mountdata, double t);
|
void SSconvstat(const SSstat *status, mountdata_t *mountdata, struct timespec *t);
|
||||||
int SStextcmd(const char *cmd, data_t *answer);
|
int SStextcmd(const char *cmd, data_t *answer);
|
||||||
int SSrawcmd(const char *cmd, data_t *answer);
|
int SSrawcmd(const char *cmd, data_t *answer);
|
||||||
int SSgetint(const char *cmd, int64_t *ans);
|
int SSgetint(const char *cmd, int64_t *ans);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.30)
|
||||||
set(PROJ PCS_create)
|
set(PROJ PCS_create)
|
||||||
set(MINOR_VERSION "0")
|
set(MINOR_VERSION "0")
|
||||||
set(MID_VERSION "1")
|
set(MID_VERSION "1")
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ static void printheader(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
initial_setup();
|
sl_init();
|
||||||
G = parse_args(argc, argv);
|
G = parse_args(argc, argv);
|
||||||
if(G->pressure < 0.) ERRX("Pressure should be greater than zero");
|
if(G->pressure < 0.) ERRX("Pressure should be greater than zero");
|
||||||
if(G->temperature < -100. || G->temperature > 100.) ERRX("Temperature over the range -100..+100");
|
if(G->temperature < -100. || G->temperature > 100.) ERRX("Temperature over the range -100..+100");
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ glob_pars const Gdefault = {
|
|||||||
* Define command line options by filling structure:
|
* Define command line options by filling structure:
|
||||||
* name has_arg flag val type argptr help
|
* name has_arg flag val type argptr help
|
||||||
*/
|
*/
|
||||||
static myoption cmdlnopts[] = {
|
static sl_option_t cmdlnopts[] = {
|
||||||
// set 1 to param despite of its repeating number:
|
// set 1 to param despite of its repeating number:
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||||
{"10m", NO_ARGS, NULL, 't', arg_int, APTR(&G.for10m), _("make output suitable for 10-micron mount")},
|
{"10m", NO_ARGS, NULL, 't', arg_int, APTR(&G.for10m), _("make output suitable for 10-micron mount")},
|
||||||
@@ -68,10 +68,10 @@ static myoption cmdlnopts[] = {
|
|||||||
glob_pars *parse_args(int argc, char **argv){
|
glob_pars *parse_args(int argc, char **argv){
|
||||||
void *ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
void *ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||||
// format of help: "Usage: progname [args]\n"
|
// format of help: "Usage: progname [args]\n"
|
||||||
change_helpstring(_("Version: " PACKAGE_VERSION "\nUsage: %s [args] FITS_files\nMake PCS list for equatorial mount\n\tWhere args are:\n"));
|
sl_helpstring(_("Version: " PACKAGE_VERSION "\nUsage: %s [args] FITS_files\nMake PCS list for equatorial mount\n\tWhere args are:\n"));
|
||||||
// parse arguments
|
// parse arguments
|
||||||
parseargs(&argc, &argv, cmdlnopts);
|
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||||
if(help) showhelp(-1, cmdlnopts);
|
if(help) sl_showhelp(-1, cmdlnopts);
|
||||||
G.nfiles = argc;
|
G.nfiles = argc;
|
||||||
G.infiles = MALLOC(char*, argc);
|
G.infiles = MALLOC(char*, argc);
|
||||||
for(int i = 0; i < argc; i++){
|
for(int i = 0; i < argc; i++){
|
||||||
|
|||||||
59
Auxiliary_utils/SILA_inverter/Makefile
Normal file
59
Auxiliary_utils/SILA_inverter/Makefile
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# run `make DEF=...` to add extra defines
|
||||||
|
PROGRAM := invertercmd
|
||||||
|
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
|
||||||
|
LDFLAGS += -lusefull_macros
|
||||||
|
SRCS := $(wildcard *.c)
|
||||||
|
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
|
||||||
|
OBJDIR := mk
|
||||||
|
CFLAGS += -O2 -Wall -Wextra -Wno-trampolines
|
||||||
|
OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o))
|
||||||
|
DEPS := $(OBJS:.o=.d)
|
||||||
|
TARGFILE := $(OBJDIR)/TARGET
|
||||||
|
CC = gcc
|
||||||
|
#TARGET := RELEASE
|
||||||
|
|
||||||
|
ifeq ($(shell test -e $(TARGFILE) && echo -n yes),yes)
|
||||||
|
TARGET := $(file < $(TARGFILE))
|
||||||
|
else
|
||||||
|
TARGET := RELEASE
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(TARGET), DEBUG)
|
||||||
|
.DEFAULT_GOAL := debug
|
||||||
|
endif
|
||||||
|
|
||||||
|
release: CFLAGS += -flto
|
||||||
|
release: LDFLAGS += -flto
|
||||||
|
release: $(PROGRAM)
|
||||||
|
|
||||||
|
debug: CFLAGS += -DEBUG -Werror
|
||||||
|
debug: TARGET := DEBUG
|
||||||
|
debug: $(PROGRAM)
|
||||||
|
|
||||||
|
$(TARGFILE): $(OBJDIR)
|
||||||
|
@echo -e "\t\tTARGET: $(TARGET)"
|
||||||
|
@echo "$(TARGET)" > $(TARGFILE)
|
||||||
|
|
||||||
|
$(PROGRAM) : $(TARGFILE) $(OBJS)
|
||||||
|
@echo -e "\t\tLD $(PROGRAM)"
|
||||||
|
$(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
$(OBJDIR):
|
||||||
|
@mkdir $(OBJDIR)
|
||||||
|
|
||||||
|
ifneq ($(MAKECMDGOALS),clean)
|
||||||
|
-include $(DEPS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OBJDIR)/%.o: %.c
|
||||||
|
@echo -e "\t\tCC $<"
|
||||||
|
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo -e "\t\tCLEAN"
|
||||||
|
@rm -rf $(OBJDIR) 2>/dev/null || true
|
||||||
|
|
||||||
|
xclean: clean
|
||||||
|
@rm -f $(PROGRAM)
|
||||||
|
|
||||||
|
.PHONY: clean xclean
|
||||||
BIN
Auxiliary_utils/SILA_inverter/Protocol.pdf
Normal file
BIN
Auxiliary_utils/SILA_inverter/Protocol.pdf
Normal file
Binary file not shown.
1
Auxiliary_utils/SILA_inverter/Readme.md
Normal file
1
Auxiliary_utils/SILA_inverter/Readme.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Serial communications with SILA inverters
|
||||||
414
Auxiliary_utils/SILA_inverter/getparams.c
Normal file
414
Auxiliary_utils/SILA_inverter/getparams.c
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
static sl_tty_t *dev = NULL;
|
||||||
|
static const char *wro = "(wrong parameter)";
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
int help;
|
||||||
|
char *path;
|
||||||
|
char *customcmd;
|
||||||
|
char *getstatus;
|
||||||
|
int baudrate;
|
||||||
|
int status;
|
||||||
|
int settershelp;
|
||||||
|
} globopts;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
int all;
|
||||||
|
int help;
|
||||||
|
int rating;
|
||||||
|
int flag;
|
||||||
|
int status;
|
||||||
|
int mode;
|
||||||
|
int warning;
|
||||||
|
int deflt;
|
||||||
|
int bateq;
|
||||||
|
} statusinfo;
|
||||||
|
|
||||||
|
typedef void (*parsefn)(const char *answer);
|
||||||
|
|
||||||
|
static globopts G = {
|
||||||
|
.path = "/dev/ttyS0",
|
||||||
|
.baudrate = 2400,
|
||||||
|
};
|
||||||
|
|
||||||
|
static statusinfo S = {0};
|
||||||
|
|
||||||
|
static sl_option_t opts[] = {
|
||||||
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||||
|
{"devpath", NEED_ARG, NULL, 'd', arg_string, APTR(&G.path), "path to device (default: /dev/ttyS0)"},
|
||||||
|
{"baudrate",NEED_ARG, NULL, 'b', arg_int, APTR(&G.baudrate), "baudrate (default: 2400)"},
|
||||||
|
{"cmd", NEED_ARG, NULL, 'c', arg_string, APTR(&G.customcmd), "custom command to send"},
|
||||||
|
{"status", NEED_ARG, NULL, 's', arg_string, APTR(&G.getstatus), "get status: comma-separated options (type 'help' for options)"},
|
||||||
|
{"helpsetters",NO_ARGS, NULL, 0, arg_int, APTR(&G.settershelp),"show help about setters"},
|
||||||
|
end_option
|
||||||
|
};
|
||||||
|
|
||||||
|
static sl_suboption_t sopts[] = {
|
||||||
|
{"all", NO_ARGS, arg_int, APTR(&S.all)},
|
||||||
|
{"help", NO_ARGS, arg_int, APTR(&S.help)},
|
||||||
|
{"rating", NO_ARGS, arg_int, APTR(&S.rating)},
|
||||||
|
{"flag", NO_ARGS, arg_int, APTR(&S.flag)},
|
||||||
|
{"status", NO_ARGS, arg_int, APTR(&S.status)},
|
||||||
|
{"mode", NO_ARGS, arg_int, APTR(&S.mode)},
|
||||||
|
{"warning", NO_ARGS, arg_int, APTR(&S.warning)},
|
||||||
|
{"default", NO_ARGS, arg_int, APTR(&S.deflt)},
|
||||||
|
{"bateq", NO_ARGS, arg_int, APTR(&S.bateq)},
|
||||||
|
end_suboption
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gettershelp(){
|
||||||
|
fprintf(stderr, "Status parameters:\n");
|
||||||
|
fprintf(stderr, "all - show all information available\n");
|
||||||
|
fprintf(stderr, "rating - device rating information (QPIRI)\n");
|
||||||
|
fprintf(stderr, "flag - device flag status (QFLAG)\n");
|
||||||
|
fprintf(stderr, "status - device general status parameters (QPIGS)\n");
|
||||||
|
fprintf(stderr, "mode - device mode (QMOD)\n");
|
||||||
|
fprintf(stderr, "warning - warning status (QPIWS)\n");
|
||||||
|
fprintf(stderr, "default - default settings (QDI)\n");
|
||||||
|
fprintf(stderr, "bateq - battery equalization parameters (QBEQI)\n");
|
||||||
|
// fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *cal_crc(const char *cmd, int len){
|
||||||
|
static uint8_t CRC[4]; // 0 - hi, 1 - low, four bytes for quick zeroing
|
||||||
|
if(!cmd || len < 1) return 0;
|
||||||
|
const uint16_t crc_table[16] = {
|
||||||
|
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
|
||||||
|
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef
|
||||||
|
};
|
||||||
|
*((uint32_t*)CRC) = 0;
|
||||||
|
uint16_t crc = 0;
|
||||||
|
uint8_t *ptr = (uint8_t*)cmd;
|
||||||
|
while(len--){
|
||||||
|
uint8_t da = ((uint8_t)(crc >> 8)) >> 4;
|
||||||
|
crc <<= 4;
|
||||||
|
crc ^= crc_table[da ^ (*ptr >> 4)];
|
||||||
|
da = ((uint8_t)(crc >> 8)) >> 4;
|
||||||
|
crc <<= 4;
|
||||||
|
crc ^= crc_table[da ^ (*ptr & 0x0f)];
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
uint8_t bCRCLow = crc;
|
||||||
|
uint8_t bCRCHign= (uint8_t)(crc >> 8);
|
||||||
|
if(bCRCLow==0x28 || bCRCLow==0x0d || bCRCLow==0x0a) ++bCRCLow;
|
||||||
|
if(bCRCHign==0x28 || bCRCHign==0x0d || bCRCHign==0x0a) ++bCRCHign;
|
||||||
|
CRC[0] = bCRCHign; CRC[1] = bCRCLow; CRC[2] = '\r';
|
||||||
|
DBG("CRC: 0x%02X 0x%02X 0x%02X", CRC[0], CRC[1], CRC[2]);
|
||||||
|
return CRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sendcmd(const char *cmd){
|
||||||
|
if(!cmd || !*cmd || !dev) return 0;
|
||||||
|
int len = strlen(cmd);
|
||||||
|
uint8_t *CRC = cal_crc(cmd, len);
|
||||||
|
#ifdef EBUG
|
||||||
|
printf("COMMAND: ");
|
||||||
|
for(int i = 0; i < len; ++i) printf("0x%02X ", cmd[i]);
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
if(sl_tty_write(dev->comfd, cmd, len)){
|
||||||
|
WARN("Can't write command");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(sl_tty_write(dev->comfd, (const char*) CRC, 3)){
|
||||||
|
WARN("Can't write CRC & STREND");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DBG("Command %s sent", cmd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *rd(){
|
||||||
|
if(!dev) return NULL;
|
||||||
|
int got = sl_tty_read(dev);
|
||||||
|
if(got < 0) ERR("Can't read");
|
||||||
|
DBG("got %d bytes, buflen: %zd", got, dev->buflen);
|
||||||
|
if(dev->buflen < 3) return NULL;
|
||||||
|
uint8_t *CRC = cal_crc(dev->buf, dev->buflen - 3);
|
||||||
|
DBG("GOT CRC: 0x%02X 0x%02X", (uint8_t)dev->buf[dev->buflen-3], (uint8_t)dev->buf[dev->buflen-2]);
|
||||||
|
if(CRC[0] != (uint8_t)dev->buf[dev->buflen-3] || CRC[1] != (uint8_t)dev->buf[dev->buflen-2]){
|
||||||
|
WARNX("Bad CRC");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char *r = dev->buf + 1;
|
||||||
|
dev->buf[dev->buflen-3] = 0;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
// 230.0 13.0 230.0 50.0 13.0 3000 3000 24.0 23.0 21.0 28.2 27.0 0 25 50 0 0 2 - 01 1 0 27.0 0 0
|
||||||
|
// b c d e f h i j1 k1 j2 k2 l o1 p1q0 o2p2q2r s t u v w x
|
||||||
|
static void ratingparsing(const char *str){
|
||||||
|
float b, c, d, e, f, j1, k1, j2, k2, l, v;
|
||||||
|
int h, i, o1, p1, q, o2, p2, q2, s, t, u, w, x;
|
||||||
|
char r;
|
||||||
|
int N = sscanf(str, "%f %f %f %f %f %d %d %f %f %f %f %f %d %d %d %d %d %d %c %d %d %d %f %d %d",
|
||||||
|
&b, &c, &d, &e, &f, &h, &i, &j1, &k1, &j2, &k2, &l, &o1, &p1, &q, &o2, &p2, &q2, &r, &s, &t,
|
||||||
|
&u, &v, &w, &x);
|
||||||
|
if(N != 25){ WARNX("Got not full answer (%d instead of 25): '%s'", N, str); return; }
|
||||||
|
printf("Grid rating voltage: %g\nGrid rating current: %g\nAC optuput rating voltage: %g\n", b, c, d);
|
||||||
|
printf("AC output rating frequency: %g\nAC output rating current: %g\nAC output rating apparent power: %d\n", e, f, h);
|
||||||
|
printf("AC output rating active power: %d\nBattery rating voltage: %g\nBattery recharge voltage: %g\n", i, j1, k1);
|
||||||
|
printf("Battery undervoltage: %g\nBattery bulk voltage: %g\nBattery float voltage: %g\n", j2, k2, l);
|
||||||
|
static const char *types[] = {"AGM", "Flooded", "User"};
|
||||||
|
printf("Battery type: %s\n", (o1 > -1 && o1 < 3) ? types[o1] : wro);
|
||||||
|
printf("Current max AC charging current: %d\nCurrent max charging current: %d\n", p1, q);
|
||||||
|
printf("Input voltage range: %s\n", o2 ? "UPS" : "Appliance");
|
||||||
|
static const char *oprio[] = {"Utility", "Solar", "SBU"};
|
||||||
|
printf("Output source priority: %s\n", (p2 > -1 && p2 < 3) ? oprio[p2] : wro);
|
||||||
|
static const char *sprio[] = {"Utility", "Solar", "Solar+Utility", "Only solar charging"};
|
||||||
|
printf("Charger source priority: %s\n", (q2 > -1 && q2 < 4) ? sprio[q2] : wro);
|
||||||
|
printf("Parallel max num: %c\n", r);
|
||||||
|
printf("Machine type: ");
|
||||||
|
switch(s){
|
||||||
|
case 0: printf("Grid tie"); break;
|
||||||
|
case 1: printf("Off grid"); break;
|
||||||
|
case 10: printf("Hybrid"); break;
|
||||||
|
default: printf("%s", wro);
|
||||||
|
}
|
||||||
|
printf("\nTopology: %s\n", (t) ? "transformer" : "transformerless");
|
||||||
|
printf("Output mode: %d\nBattery redischarge voltage: %g\n", u, v);
|
||||||
|
printf("PV OK condition for parallel: %s\n", (w) ? "Only all connected" : "At least one connected");
|
||||||
|
printf("PV power balance: %s\n", (x) ? "Sum of powers" : "Max charged current");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *DEflags = "abjkuvxyz";
|
||||||
|
static const char *DEmeanings[] = {
|
||||||
|
"buzzer", "bypass", "power saving", "LCD display escape 1min", "overload restart",
|
||||||
|
"over temperature restart", "backlight on", "alarm on interrupt", "fault code record",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static void flagparsing(const char *str){
|
||||||
|
char c;
|
||||||
|
int first = 0;
|
||||||
|
while((c = *str++)){
|
||||||
|
if(c == 'D'){ green("\nDISABLED: "); first = 1; }
|
||||||
|
else if(c == 'E'){ red("ENABLED: "); first = 1; }
|
||||||
|
else{
|
||||||
|
const char *field = "unknown";
|
||||||
|
for(int i = 0; DEflags[i]; ++i){
|
||||||
|
if(DEflags[i] == c){ field = DEmeanings[i]; break; }
|
||||||
|
}
|
||||||
|
printf("%s%s", (first) ? "" : ", ", field);
|
||||||
|
first = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief showflags - display bitflags
|
||||||
|
* @param flags - string with flags ('1' / '0')
|
||||||
|
* @param meaning - array with meaning of each flag, starting from H bit, i.e. meaning[0] is lesser bit
|
||||||
|
* @param nfields - strlen of flags (or less), H first
|
||||||
|
*/
|
||||||
|
static void showflags(const char *flags, const char **meaning, int nfields){
|
||||||
|
for(int i = 0; i < nfields; ++i){
|
||||||
|
if(!meaning[i]) continue;
|
||||||
|
printf("\t%s: ", meaning[i]);
|
||||||
|
if(flags[i] == '1') red("on/yes\n");
|
||||||
|
else green("off/no\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 230.0 50.0 232.0 50.0 0000 0000 000 409 26.99 000 100 0489 0000 000.0 00.00 00000 10011101 00 03 00000 100
|
||||||
|
// b c d e f g h i j k o t e1 u w p x [unknown shit]
|
||||||
|
static void statusparsing(const char *str){
|
||||||
|
float b, c, d, e, j, u, w;
|
||||||
|
int f, g, h, i, k, o, t, e1, p, S, H, I;
|
||||||
|
char x[9], T[4];
|
||||||
|
int N = sscanf(str, "%f %f %f %f %d %d %d %d %f %d %d %d %d %f %f %d %8s %d %d %d %3s",
|
||||||
|
&b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &o, &t, &e1, &u, &w, &p, x, &S, &H, &I, T);
|
||||||
|
DBG("N=%d", N);
|
||||||
|
if(N >= 17){
|
||||||
|
printf("Grid voltage: %g\nGrid frequency: %g\nAC output voltage: %g\nAC output frequency: %g\n",
|
||||||
|
b, c, d, e);
|
||||||
|
printf("AC output apparent power: %d\nAC output active power: %d\nOutput load percent: %d\n", f, g, h);
|
||||||
|
printf("Bus voltage: %d\nBattery voltage: %g\nBattery charging current: %d\nBattery capacity: %d\n", i, j, k, o);
|
||||||
|
printf("Inverter heat sink temperature: %d\nPV input current for battery: %d\nPV input voltage 1: %g\n", t, e1, u);
|
||||||
|
printf("Battery voltage from SCC: %g\nBattery discharge current: %d\nDevice status:\n", w, p);
|
||||||
|
static const char *sf[] = {"SBU priority version", "configuration changed", "SCC firmware updated",
|
||||||
|
"Load status", "Steady batt voltage while charging", "Charging", "SCC charging", "AC charging"};
|
||||||
|
showflags(x, sf, 8);
|
||||||
|
if(N > 17){
|
||||||
|
printf("Battery offset for fans on: %d\nEEPROM version: %d\nPV charging power: %d\n", S, H, I);
|
||||||
|
printf("Inverter status:\n");
|
||||||
|
static const char *is[] = {"Charging to floating mode", "Switch", "Dustproof installed"};
|
||||||
|
showflags(T, is, 3);
|
||||||
|
}
|
||||||
|
}else WARNX("Get not full answer: %d instead of 17", N);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modeparsing(const char *str){
|
||||||
|
printf("Device mode: ");
|
||||||
|
switch(*str){
|
||||||
|
case 'B': printf("Battery"); break;
|
||||||
|
case 'F': printf("Fault"); break;
|
||||||
|
case 'H': printf("Power saving"); break;
|
||||||
|
case 'L': printf("Line"); break;
|
||||||
|
case 'P': printf("Power on"); break;
|
||||||
|
case 'S': printf("Standby"); break;
|
||||||
|
default: printf("Unknown");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void warningparsing(const char *str){
|
||||||
|
int l = strlen(str);
|
||||||
|
if(l < 32) WARNX("Non-full status! Data could be wrong");
|
||||||
|
static const char *wmsgs[] = {
|
||||||
|
NULL, "Inverter fault", "Bus over", "Bus under", "Bus soft fail", "Line fail", "OPV short",
|
||||||
|
"Inv voltage too low", "Inv voltage too high", "Over temperature", "Fan locked", "Battery voltage high",
|
||||||
|
"Battery low alarm", "Overcharge", "Battery under shutdown", "Battery derating", "Overload", "EEPROM fault",
|
||||||
|
"Inverter overcurrent", "Inverter soft fail", "Self test fail", "OP DC voltage over", "Bat open",
|
||||||
|
"Current sensor fail", "Battery short", "Power limit", "PV voltage high", "MPPT overload fault",
|
||||||
|
"MPPT overload warning", "Battery too low to charge", NULL, NULL
|
||||||
|
};
|
||||||
|
printf("Warning status:\n");
|
||||||
|
showflags(str, wmsgs, l);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *endis(int val){
|
||||||
|
return (val) ? COLOR_RED "enable" COLOR_OLD : COLOR_GREEN "disable" COLOR_OLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 230.0 50.0 0025 21.0 27.0 28.2 23.0 50 0 0 2 0 1 0 0 0 1 1 1 0 1 0 27.0 0 0 ()
|
||||||
|
// b c d e f g h i j k l m n o p q r s t u v w y x z a
|
||||||
|
static void defaultparsing(const char *str){
|
||||||
|
float b, c, e, f, g, h, y;
|
||||||
|
int d, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, z, a;
|
||||||
|
int N = sscanf(str, "%f %f %d %f %f %f %f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %f %d %d %d",
|
||||||
|
&b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &y, &x, &z, &a);
|
||||||
|
if(N < 25){WARNX("Wrong data format: %d fields instead of 25", N); return;}
|
||||||
|
printf("AC output voltage: %g\nAC output frequency: %g\nMax AC charging current: %d\n", b, c, d);
|
||||||
|
printf("Battery undervoltage: %g\nCharging float voltage: %g\nCharging bulk voltage: %g\n", e, f, g);
|
||||||
|
printf("Battery default recharge voltage: %g\nMax charging current: %d\nAC input voltage range: %s\n", h, i, (j) ? "UPS" : "appliance");
|
||||||
|
printf("Output source priority: %s\nCharger source priority: %s\n", (k) ? "solar first": "utility first", (l) ? "solar first" : "utility first");
|
||||||
|
printf("Battery type: %s\nBuzzer: %s\nPover saving: %s\n", (m) ? "other" : "AGM", endis(n), endis(o));
|
||||||
|
printf("Overload restart: %s\nOver temperature restart: %s\n", endis(p), endis(q));
|
||||||
|
printf("Backlight: %s\nAlarm on interrupt: %s\nFault code record: %s\n", endis(r), endis(s), endis(t));
|
||||||
|
printf("Overload bypass: %s\nLCD timeout escape: %s\nOutput mode: %d\n", endis(u), endis(v), w);
|
||||||
|
printf("Battery re-discharge voltage: %g\nPV OK condition for parallel: %s\n", y, (x) ? "all" : "any");
|
||||||
|
printf("PV power balance: %s\n", (z) ? "?" : "PV max current is charged current");
|
||||||
|
if(N == 26) printf("Max charging time @ CV stage: %s\n", (a) ? "?" : "automatically");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 060 030 050 030 29.20 000 120 0 0000
|
||||||
|
// b c d e f g h i j k
|
||||||
|
static void equparsing(const char *str){
|
||||||
|
float g;
|
||||||
|
int b, c, d, e, f, h, i, j, k;
|
||||||
|
int N = sscanf(str, "%d %d %d %d %d %f%d %d %d %d", &b, &c, &d, &e, &f, &g, &h, &i, &j, &k);
|
||||||
|
if(N != 10){ WARNX("Not enougn parameters: got %d instead of 10\n", N); return; }
|
||||||
|
printf("Equalization: %s\n", endis(b));
|
||||||
|
printf("Eq. time: %d minutes\nEq. period: %d days\nEq. max current: %d\n", c, d, e);
|
||||||
|
printf("Eq. voltage: %g\nEq. over time: %d minutes\nEq. active status: %d\n", g, i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printpar(const char *str){
|
||||||
|
printf("%s\n", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void runparsing(const char *cmd, parsefn f){
|
||||||
|
if(sendcmd(cmd)){
|
||||||
|
char *got = rd();
|
||||||
|
if(got && *got){
|
||||||
|
f(got);
|
||||||
|
printf("\n");
|
||||||
|
} else red("Can't get data\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showstatus(){
|
||||||
|
green("\nModel name: "); runparsing("QMN", printpar);
|
||||||
|
if(S.all || S.rating) runparsing("QPIRI", ratingparsing);
|
||||||
|
if(S.all || S.flag) runparsing("QFLAG", flagparsing);
|
||||||
|
if(S.all || S.status) runparsing("QPIGS", statusparsing);
|
||||||
|
if(S.all || S.mode) runparsing("QMOD", modeparsing);
|
||||||
|
if(S.all || S.warning) runparsing("QPIWS", warningparsing);
|
||||||
|
if(S.all || S.deflt) runparsing("QDI", defaultparsing);
|
||||||
|
if(S.all || S.bateq) runparsing("QBEQI", equparsing);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showsettershelp(){
|
||||||
|
printf("\n\n");
|
||||||
|
red("Be carefull with setters! Think twice before changing something!!!\n\n");
|
||||||
|
printf("Here are setters...\n");
|
||||||
|
printf("PEx - enable status / DEx - disable status, where 'x':\n");
|
||||||
|
for(int i = 0; DEflags[i]; ++i) printf("\t%c - %s\n", DEflags[i], DEmeanings[i]);
|
||||||
|
printf("PF - set all control parameters to default\n");
|
||||||
|
printf("MCHGCx - max charging current (Amps)\n");
|
||||||
|
printf("MUCHGCx - utility max charging current\n");
|
||||||
|
printf("Fx - invertere output frequency\n");
|
||||||
|
printf("POPx - output source priority (0 - utility, 1 - solar, 2 - SBU)\n");
|
||||||
|
printf("PBCVx - battery re-charge voltage\n");
|
||||||
|
printf("PBDVx - battery re-discharge voltage\n");
|
||||||
|
printf("PCPx - inverter charging priority (0-3: utility first/solar first/solar+utility/solar only\n");
|
||||||
|
printf("PGRx - inverter grid voltage range (0 - appliance, 1 - UPS)\n");
|
||||||
|
printf("PBTx - battery type (0 - AGM, 1 - flooded)\n");
|
||||||
|
printf("PSDVx - battery cut-off voltage\n");
|
||||||
|
printf("PCVVx - CV (constant voltage) charging voltage\n");
|
||||||
|
printf("PBFTx - battery float charging voltage\n");
|
||||||
|
printf("PBEQEx - enable (1) or disable (0) battery equalization\n");
|
||||||
|
printf("PBEQT x - battery equalization time (minutes)\n");
|
||||||
|
printf("PBEQPx - battery equalization period (days)\n");
|
||||||
|
printf("PBEQVx - battery equalization voltage\n");
|
||||||
|
printf("PBEQOTx - battery equalization overtime (minutes)\n");
|
||||||
|
printf("PBEQAx - activate (1) or disactivate (0) battery equalization now\n");
|
||||||
|
printf("PCVTx - max charging time an CV stage\n");
|
||||||
|
printf("\n\nAnd some getters that aren't in `status` variants:\n");
|
||||||
|
printf("QID - inverter's serial\nQVFW - firmware version\nQMCHGCR - max charging currents available\n");
|
||||||
|
printf("QMUCHGCR - max utility charging currents available\n");
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
sl_init();
|
||||||
|
sl_parseargs(&argc, &argv, opts);
|
||||||
|
if(G.help) sl_showhelp(-1, opts);
|
||||||
|
if(!G.path || G.baudrate < 1) ERRX("Need device path and baudrate");
|
||||||
|
dev = sl_tty_new(G.path, G.baudrate, 128);
|
||||||
|
if(!dev) ERR("Can't init serial device");
|
||||||
|
dev = sl_tty_open(dev, 1);
|
||||||
|
if(!dev) ERR("Can't open %s", G.path);
|
||||||
|
if(sl_tty_tmout(100000)) WARNX("Can't set timeout");
|
||||||
|
DBG("bufsz: %zd", dev->bufsz);
|
||||||
|
if(G.getstatus){
|
||||||
|
if(!sl_get_suboption(G.getstatus, sopts) || S.help){
|
||||||
|
gettershelp();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
showstatus();
|
||||||
|
}
|
||||||
|
if(G.customcmd){
|
||||||
|
green("Try to send '%s'\n", G.customcmd);
|
||||||
|
if(sendcmd(G.customcmd)){
|
||||||
|
char *got = rd();
|
||||||
|
if(got) printf("Get data: '%s'\n", got);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sl_tty_close(&dev);
|
||||||
|
if(G.settershelp) showsettershelp();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.30)
|
||||||
set(PROJ chkweather)
|
set(PROJ chkweather)
|
||||||
set(MINOR_VERSION "1")
|
set(MINOR_VERSION "1")
|
||||||
set(MID_VERSION "0")
|
set(MID_VERSION "0")
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ glob_pars const Gdefault = {
|
|||||||
* Define command line options by filling structure:
|
* Define command line options by filling structure:
|
||||||
* name has_arg flag val type argptr help
|
* name has_arg flag val type argptr help
|
||||||
*/
|
*/
|
||||||
static myoption cmdlnopts[] = {
|
static sl_option_t cmdlnopts[] = {
|
||||||
// set 1 to param despite of its repeating number:
|
// set 1 to param despite of its repeating number:
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||||
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("baudrate (default: 9600)")},
|
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("baudrate (default: 9600)")},
|
||||||
@@ -59,10 +59,10 @@ static myoption cmdlnopts[] = {
|
|||||||
glob_pars *parse_args(int argc, char **argv){
|
glob_pars *parse_args(int argc, char **argv){
|
||||||
void *ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
void *ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||||
// format of help: "Usage: progname [args]\n"
|
// format of help: "Usage: progname [args]\n"
|
||||||
change_helpstring(_("Usage: %s [args]\n\n\tWhere args are:\n"));
|
sl_helpstring(_("Usage: %s [args]\n\n\tWhere args are:\n"));
|
||||||
// parse arguments
|
// parse arguments
|
||||||
parseargs(&argc, &argv, cmdlnopts);
|
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||||
if(help) showhelp(-1, cmdlnopts);
|
if(help) sl_showhelp(-1, cmdlnopts);
|
||||||
if(argc > 0){
|
if(argc > 0){
|
||||||
WARNX("Wrong arguments:\n");
|
WARNX("Wrong arguments:\n");
|
||||||
for(int i = 0; i < argc; i++)
|
for(int i = 0; i < argc; i++)
|
||||||
|
|||||||
@@ -51,28 +51,28 @@ static int getpar(char *string, double *Val, char *Name){
|
|||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
glob_pars *G = NULL; // default parameters see in cmdlnopts.c
|
glob_pars *G = NULL; // default parameters see in cmdlnopts.c
|
||||||
initial_setup();
|
sl_init();
|
||||||
G = parse_args(argc, argv);
|
G = parse_args(argc, argv);
|
||||||
TTY_descr *dev = new_tty(G->ttyname, G->speed, 64);
|
sl_tty_t *dev = sl_tty_new(G->ttyname, G->speed, 64);
|
||||||
if(!dev) return 1;
|
if(!dev) return 1;
|
||||||
size_t got, L = 0;
|
size_t got, L = 0;
|
||||||
char buff[BUFLEN], *ptr = buff;
|
char buff[BUFLEN], *ptr = buff;
|
||||||
int errctr = 0;
|
int errctr = 0;
|
||||||
for(; errctr < ERRCTR_MAX; ++errctr){
|
for(; errctr < ERRCTR_MAX; ++errctr){
|
||||||
if(!tty_open(dev, 1)){
|
if(!sl_tty_open(dev, 1)){
|
||||||
sleep(1);
|
sleep(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
while(read_tty(dev)); // clear buffer
|
while(sl_tty_read(dev)); // clear buffer
|
||||||
if(write_tty(dev->comfd, "?U\r\n", 3)){
|
if(sl_tty_write(dev->comfd, "?U\r\n", 3)){
|
||||||
WARNX("write_tty()");
|
WARNX("write_tty()");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
double t0 = dtime();
|
double t0 = sl_dtime();
|
||||||
while(dtime() - t0 < 10.){ // timeout - 10s
|
while(sl_dtime() - t0 < 10.){ // timeout - 10s
|
||||||
got = read_tty(dev);
|
got = sl_tty_read(dev);
|
||||||
if(got == 0) continue;
|
if(got == 0) continue;
|
||||||
t0 = dtime();
|
t0 = sl_dtime();
|
||||||
if(L + got > BUFLEN - 1) break;
|
if(L + got > BUFLEN - 1) break;
|
||||||
L += got;
|
L += got;
|
||||||
buff[L] = 0;
|
buff[L] = 0;
|
||||||
@@ -92,8 +92,8 @@ int main(int argc, char **argv){
|
|||||||
continue;
|
continue;
|
||||||
}else break;
|
}else break;
|
||||||
}
|
}
|
||||||
while(read_tty(dev));
|
while(sl_tty_read(dev));
|
||||||
close_tty(&dev);
|
sl_tty_close(&dev);
|
||||||
if(errctr == ERRCTR_MAX){
|
if(errctr == ERRCTR_MAX){
|
||||||
ERRX("No connection to meteostation");
|
ERRX("No connection to meteostation");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ A="90:00:00"
|
|||||||
H="45:00:00"
|
H="45:00:00"
|
||||||
|
|
||||||
function sendcmd(){
|
function sendcmd(){
|
||||||
echo $1 | nc 192.168.70.33 10001 -q10
|
echo $1 | nc localhost 10001 -q10
|
||||||
}
|
}
|
||||||
|
|
||||||
sendcmd ":Sz${A}#"
|
sendcmd ":Sz${A}#"
|
||||||
|
|||||||
20
Auxiliary_utils/bash_scripts/allsky_sixel.sh
Executable file
20
Auxiliary_utils/bash_scripts/allsky_sixel.sh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# $1 - scaling factor (in percents) of the image
|
||||||
|
#
|
||||||
|
|
||||||
|
scale=50
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
scale=$1
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset http_proxy
|
||||||
|
|
||||||
|
clear
|
||||||
|
while [[ 1 ]]; do
|
||||||
|
tput cup 0 0
|
||||||
|
curl -s http://zarch.sao.ru/webcam/mirat_allsky.cgi | magick - -colors 256 +dither -normalize -resize $scale% sixel:-
|
||||||
|
# sleep 30s
|
||||||
|
sleep 5s
|
||||||
|
done
|
||||||
114
Auxiliary_utils/bash_scripts/allsky_sixel_weather.sh
Executable file
114
Auxiliary_utils/bash_scripts/allsky_sixel_weather.sh
Executable file
@@ -0,0 +1,114 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# $1 - scaling factor (in percents) of the image
|
||||||
|
#
|
||||||
|
|
||||||
|
function get_val {
|
||||||
|
echo $(echo $1 | cut -d "=" -f 2 | cut -d "." -f 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
white_col="\e[97m"
|
||||||
|
red_col="\e[1m\e[31m"
|
||||||
|
end_col="\e[0m"
|
||||||
|
last_row=0
|
||||||
|
|
||||||
|
scale=50
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
scale=$1
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset http_proxy
|
||||||
|
|
||||||
|
im_sleep=20 # in secs
|
||||||
|
info_sleep=180 # in secs
|
||||||
|
|
||||||
|
n_info=$((info_sleep/im_sleep))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
clear
|
||||||
|
while [[ 1 ]]; do
|
||||||
|
# weather info
|
||||||
|
m_old=($(curl 192.168.70.33:12345 2>/dev/null)// / )
|
||||||
|
rain=${m_old[0]}
|
||||||
|
clouds=${m_old[1]}
|
||||||
|
temp=${m_old[2]}
|
||||||
|
|
||||||
|
m_new=($(curl localhost:3333/stat3600 2>/dev/null | sed 's/[\x01-\x1F\x7F]//g')// / )
|
||||||
|
windmax=${m_new[0]}
|
||||||
|
m_new=($(curl localhost:3333 2>/dev/null | sed 's/[\x01-\x1F\x7F]//g')// / )
|
||||||
|
wind=${m_new[0]}
|
||||||
|
humi=${m_new[4]}
|
||||||
|
|
||||||
|
rain_col=$white_col
|
||||||
|
clouds_col=$white_col
|
||||||
|
humi_col=$white_col
|
||||||
|
wind_col=$white_col
|
||||||
|
wind_max_col=$white_col
|
||||||
|
|
||||||
|
let rain_flag=$(get_val $rain)
|
||||||
|
if [[ $rain_flag -eq 1 ]]; then
|
||||||
|
rain_col=$red_col
|
||||||
|
fi
|
||||||
|
|
||||||
|
let clouds_val=$(get_val $clouds)
|
||||||
|
if [[ $clouds_val -le 1500 ]]; then
|
||||||
|
clouds_col=$red_col
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
let humi_val=$(get_val $humi)
|
||||||
|
if [[ $humi_val -ge 90 ]]; then
|
||||||
|
humi_col=$red_col
|
||||||
|
fi
|
||||||
|
|
||||||
|
let wind_val=$(get_val $wind)
|
||||||
|
if [[ $wind_val -ge 10 ]]; then
|
||||||
|
wind_col=$red_col
|
||||||
|
fi
|
||||||
|
|
||||||
|
let wind_max_val=$(get_val $windmax)
|
||||||
|
if [[ $wind_max_val -ge 10 ]]; then
|
||||||
|
wind_max_col=$red_col
|
||||||
|
fi
|
||||||
|
|
||||||
|
ncols=`tput cols`
|
||||||
|
start_col=$((ncols-25))
|
||||||
|
|
||||||
|
tput cup $last_row $start_col
|
||||||
|
# echo -e "\e[4m`date`:\e[0m"
|
||||||
|
echo -e "\e[4m`date +'%F %T'`:\e[0m"
|
||||||
|
|
||||||
|
((++last_row))
|
||||||
|
|
||||||
|
tput cup $last_row $start_col
|
||||||
|
echo -e "$clouds_col$clouds $rain_col($rain)$end_col"
|
||||||
|
|
||||||
|
((++last_row))
|
||||||
|
|
||||||
|
tput cup $last_row $start_col
|
||||||
|
# echo -e "$temp $humi_col($humi)$end_col"
|
||||||
|
temp_val=`printf "%.1f" ${temp#*=}`
|
||||||
|
echo -e "Temp=$temp_val, ${humi_col}Hum=$humi_val%$end_col"
|
||||||
|
|
||||||
|
((++last_row))
|
||||||
|
|
||||||
|
tput cup $last_row $start_col
|
||||||
|
# echo -e "$wind_col$wind $wind_max_col($windmax @hour)$end_col\n"
|
||||||
|
wind_max_val=`printf "%.1f" ${windmax#*=}`
|
||||||
|
echo -e "$wind_col$wind $wind_max_col(Max=$wind_max_val/hour)$end_col\n"
|
||||||
|
|
||||||
|
IFS='[;' read -p $'\e[6n' -d R -rs _ last_row col _
|
||||||
|
|
||||||
|
# allsky image
|
||||||
|
|
||||||
|
for i in `seq $n_info`; do
|
||||||
|
tput cup 0 0
|
||||||
|
curl -s http://zarch.sao.ru/webcam/omea_allsky.cgi | magick - -colors 256 -normalize +dither -resize $scale% sixel:-
|
||||||
|
# curl -s http://zarch.sao.ru/webcam/mirat_allsky.cgi | magick - -colors 256 -resize $scale% sixel:-
|
||||||
|
sleep ${im_sleep}s
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
done
|
||||||
18
Auxiliary_utils/bash_scripts/copy_and_xz.sh
Executable file
18
Auxiliary_utils/bash_scripts/copy_and_xz.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# $1 - directory to be copied
|
||||||
|
#
|
||||||
|
|
||||||
|
RDIR="/home/obs/robotel1_2025"
|
||||||
|
|
||||||
|
# copy non-FITS files
|
||||||
|
tar c --exclude='*.fit' --exclude='*shit*' $1 | ssh obs@roboserv "tar x -C $RDIR"
|
||||||
|
#tar c --exclude='*.fit' --exclude='*shit*' $1 | ssh obs@roboserv "tar x -C /home/obs/robotel1_2023"
|
||||||
|
|
||||||
|
# copy FITS files and XZ-ing it on remote server
|
||||||
|
CMD='sh -c "xz -6e -T0 - > $TAR_FILENAME.xz"'
|
||||||
|
tar c $1/*.fit | ssh obs@roboserv "cd $RDIR; tar x --to-command='$CMD'"
|
||||||
|
#tar c $1/*.fit | ssh obs@roboserv "cd /home/obs/robotel1_2023; tar x --to-command='$CMD'"
|
||||||
|
|
||||||
|
ssh obs@roboserv "cd $RDIR; tar c $1 | ssh data@robostorage 'tar x -C /mnt/ARCHIVE/ROBOTEL1/'"
|
||||||
33
Auxiliary_utils/bash_scripts/park_telescope_1
Executable file
33
Auxiliary_utils/bash_scripts/park_telescope_1
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
A="01:48:38"
|
||||||
|
H="01:13:29"
|
||||||
|
|
||||||
|
function sendcmd(){
|
||||||
|
echo $1 | nc localhost 10001 -q10 || (echo "Can't connect" >&2; exit 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#lower limit is 0
|
||||||
|
sendcmd ":So0#"
|
||||||
|
sendcmd ":Sz${A}#"
|
||||||
|
sendcmd ":Sa${H}#"
|
||||||
|
sendcmd ":MA#" # || exit 1
|
||||||
|
|
||||||
|
errctr=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
sleep 2
|
||||||
|
ANS=$(sendcmd ":Gstat#")
|
||||||
|
if [ "x$ANS" == "x" ]; then
|
||||||
|
[[ $((++errctr)) < 5 ]] && continue
|
||||||
|
echo "No connection to server" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo $ANS
|
||||||
|
[ $ANS == "0#" -o $ANS == "7#" ] && break
|
||||||
|
done
|
||||||
|
|
||||||
|
# stop tracking
|
||||||
|
sendcmd ":AL#"
|
||||||
|
|
||||||
|
echo -e "\n\nTelescope parked\n"
|
||||||
33
Auxiliary_utils/bash_scripts/park_telescope_2
Executable file
33
Auxiliary_utils/bash_scripts/park_telescope_2
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
A="00:00:00"
|
||||||
|
H="2:00:00"
|
||||||
|
|
||||||
|
function sendcmd(){
|
||||||
|
echo $1 | nc localhost 10001 -q10 || (echo "Can't connect" >&2; exit 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#lower limit is 0
|
||||||
|
sendcmd ":So0#"
|
||||||
|
sendcmd ":Sz${A}#"
|
||||||
|
sendcmd ":Sa${H}#"
|
||||||
|
sendcmd ":MA#" # || exit 1
|
||||||
|
|
||||||
|
errctr=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
sleep 2
|
||||||
|
ANS=$(sendcmd ":Gstat#")
|
||||||
|
if [ "x$ANS" == "x" ]; then
|
||||||
|
[[ $((++errctr)) < 5 ]] && continue
|
||||||
|
echo "No connection to server" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo $ANS
|
||||||
|
[ $ANS == "0#" -o $ANS == "7#" ] && break
|
||||||
|
done
|
||||||
|
|
||||||
|
# stop tracking
|
||||||
|
sendcmd ":AL#"
|
||||||
|
|
||||||
|
echo -e "\n\nTelescope parked\n"
|
||||||
165
Auxiliary_utils/bash_scripts/run_full.new_foc
Executable file
165
Auxiliary_utils/bash_scripts/run_full.new_foc
Executable file
@@ -0,0 +1,165 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
RA="17:17:08.86"
|
||||||
|
DEC="+67:57:11.4"
|
||||||
|
OBJ="GALEX171708.5"
|
||||||
|
EXPTIME=31000
|
||||||
|
FLATTIME=40000
|
||||||
|
OBS="Fatkhullin T.A."
|
||||||
|
BADWEATHER=1300
|
||||||
|
DATEEND=$(sunrise 14)
|
||||||
|
# focus each N seconds
|
||||||
|
FOCUST=7200
|
||||||
|
|
||||||
|
FNO=1
|
||||||
|
FDATE=0
|
||||||
|
|
||||||
|
#
|
||||||
|
# NOTE: THIS IS A NEW VERSION OF THE OBSERVATION SCRIPT!
|
||||||
|
# CHAGLELOG:
|
||||||
|
# Oct 2024: replace focussing algorithm (T. Fatkhullin)
|
||||||
|
# At the night start the first focussing run
|
||||||
|
# uses of two-step algorithm:
|
||||||
|
# 1) rough focus astimation along full season-to-season
|
||||||
|
# focus range (as it was implemented in the old obs. script)
|
||||||
|
# 2) precise focussing along narrow range computed from
|
||||||
|
# previous rough estimation
|
||||||
|
# Next focussing runs compute range from the current focus value.
|
||||||
|
# (new function 'focustel_new')
|
||||||
|
#
|
||||||
|
# May 2025: rewrite detection of running this script process (T. Fatkhullin)
|
||||||
|
# (use of '-c' commandline option for 'pgrep' command)
|
||||||
|
|
||||||
|
function focustel_new(){
|
||||||
|
rm focus*.fit 2>/dev/null
|
||||||
|
echo "Focussing..."
|
||||||
|
|
||||||
|
let fno=$1
|
||||||
|
if [[ $fno -gt 1 ]]; then
|
||||||
|
let curr_foc=`fli_control | tail -n 5 | head -n 1 | cut -d "=" -f 2`
|
||||||
|
let lowf=$curr_foc-1500
|
||||||
|
let highf=$curr_foc+1500
|
||||||
|
focus_seq_FLI.py -v -c focus$(printf "%02d" $1).jpg $lowf $highf 500
|
||||||
|
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
echo -e "\nFOCUSSING SCRIPT RETURNED: $?"
|
||||||
|
echo -e "SOMETHING WAS WRONG IN FOCUSSING SEQUENCE!!! SET FOCUS TO PREVIOUS VALUE!!\n"
|
||||||
|
fli_control -g $curr_foc
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
focus_seq_FLI.py --guess -v -N 7 -c focus$(printf "%02d" $1).jpg 47000 60000 500
|
||||||
|
fi
|
||||||
|
|
||||||
|
FDATE=$(date +%s)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendcmd(){
|
||||||
|
echo $1 | nc 192.168.70.33 10001 -q10
|
||||||
|
}
|
||||||
|
|
||||||
|
function point_tel(){
|
||||||
|
touch lastpointing
|
||||||
|
send_coords -r $1 -d $2
|
||||||
|
}
|
||||||
|
|
||||||
|
#c=$(pgrep run_full | wc -l)
|
||||||
|
# $ will run another run_full, so c=2 for single run
|
||||||
|
#if [[ $c -gt 1 ]]; then
|
||||||
|
# echo "Another process is running; exiting"
|
||||||
|
# exit 1
|
||||||
|
#fi
|
||||||
|
|
||||||
|
if [[ $(pgrep -c run_full) -gt 1 ]]; then
|
||||||
|
echo "Another process is running! Exit!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export http_proxy=""
|
||||||
|
|
||||||
|
# set lower limit to 5degr
|
||||||
|
send_command2mount ":So5#"
|
||||||
|
|
||||||
|
echo "Time diff: $(($DATEEND-$(date +%s)))"
|
||||||
|
if [ $(($DATEEND-$(date +%s))) -lt 3600 ]; then
|
||||||
|
echo "There's less an hour for observation!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $(($DATEEND-$(date +%s))) -gt 53200 ]; then
|
||||||
|
echo "There's more than 12 hours till closing, check script data!"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
#STARTobs
|
||||||
|
|
||||||
|
#send_coords
|
||||||
|
#echo "Wait a little"
|
||||||
|
#sleep 10
|
||||||
|
|
||||||
|
echo "GoTo object: ${RA} ${DEC}"
|
||||||
|
send_coords -r${RA} -d${DEC}
|
||||||
|
|
||||||
|
echo "Start taking object"
|
||||||
|
|
||||||
|
badweather=0
|
||||||
|
while true; do
|
||||||
|
now=$(date +%s)
|
||||||
|
ANS=$(curl localhost:55555/status 2>/dev/null)
|
||||||
|
echo "Dome status: $ANS"
|
||||||
|
if [ $ANS != "opened" ]; then
|
||||||
|
echo "Closed"
|
||||||
|
curl localhost:55555/weather 2>/dev/null > DomeClosed
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
chkweather ${BADWEATHER} > lastweather && badweather=0 || badweather=$((badweather+1))
|
||||||
|
[ $badweather -gt 5 ] && break
|
||||||
|
[ -f stopobs ] && break
|
||||||
|
[ -f exitjob ] && exit 0
|
||||||
|
if [ "$now" -lt "$DATEEND" ]; then
|
||||||
|
est=$(sendcmd ":Gmte#"|sed -e 's/^0*//' -e 's/#//')
|
||||||
|
echo -e "\n\n\n\n\nEstimated time to flip: $est minutes"
|
||||||
|
if [[ ("x$est" == "x") || ($est -lt 3) ]]; then
|
||||||
|
point_tel "${RA}" "${DEC}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
ST=$(send_coords | awk '{print $4}')
|
||||||
|
[ "x$ST" == "x" ] && break
|
||||||
|
if [ $ST -ne "0" ]; then
|
||||||
|
point_tel "${RA}" "${DEC}"
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
[ $(($(date +%s) - $FDATE)) -gt $FOCUST ] && focustel_new $((FNO++))
|
||||||
|
# [ $(($(date +%s) - $FDATE)) -gt $FOCUST ] && focustel $((FNO++))
|
||||||
|
preflash
|
||||||
|
fli_control -r /tmp/10micron.fitsheader -x $EXPTIME -N "${OBS}" -O "${OBJ}" "$OBJ"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Dome closed @ $(date)" >> closed
|
||||||
|
curl localhost:55555/close
|
||||||
|
|
||||||
|
relay_manage -s1
|
||||||
|
park_telescope
|
||||||
|
|
||||||
|
for x in $(seq 1 10); do
|
||||||
|
preflash
|
||||||
|
fli_control -r /tmp/10micron.fitsheader -x1 -N "${OBS}" -O "bias" -d bias
|
||||||
|
preflash
|
||||||
|
fli_control -r /tmp/10micron.fitsheader -x $EXPTIME -N "${OBS}" -O "dark" -d dark
|
||||||
|
preflash
|
||||||
|
fli_control -r /tmp/10micron.fitsheader -n3 -x $FLATTIME -N "${OBS}" -O "flat" flat
|
||||||
|
done
|
||||||
|
|
||||||
|
relay_manage -r1
|
||||||
|
echo "Closed @ $(date)" >> closed
|
||||||
|
|
||||||
|
STOPobs || true
|
||||||
|
|
||||||
|
DIR=$(basename $PWD)
|
||||||
|
echo "TAR: $DIR"
|
||||||
|
cd ..
|
||||||
|
./copy_and_xz.sh $DIR
|
||||||
|
echo "Archived, end"
|
||||||
|
echo "$DIR archived @ $(date)" >> archived
|
||||||
44
Auxiliary_utils/bash_scripts/setup_obs.sh
Executable file
44
Auxiliary_utils/bash_scripts/setup_obs.sh
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# The script creates working directory,
|
||||||
|
# copies 'run' script into it and
|
||||||
|
# edits observer name according to
|
||||||
|
# the its argument
|
||||||
|
#
|
||||||
|
# Working directory name is formed from
|
||||||
|
# date of the script running and is computed
|
||||||
|
# as follows:
|
||||||
|
# now - 12hours
|
||||||
|
# i.e. the day starts from 12h not from 0h!
|
||||||
|
#
|
||||||
|
# $1 - observer name
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
obs_name="Fatkhullin T.A."
|
||||||
|
else
|
||||||
|
obs_name=$1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# now - 12h
|
||||||
|
let now12=`date +%s`-12*3600
|
||||||
|
|
||||||
|
# working directory
|
||||||
|
wdir=/DATA/FITS/`date -d @$now12 +%y.%m.%d`
|
||||||
|
|
||||||
|
echo -n "Creating working directory: $wdir ..."
|
||||||
|
if [[ -d $wdir ]]; then
|
||||||
|
echo -e "\tFAILED! The directory already exists! Exit!"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo -e "\tOK!"
|
||||||
|
mkdir $wdir
|
||||||
|
cd $wdir
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp ../run_full.new_foc .
|
||||||
|
|
||||||
|
# replace observer name
|
||||||
|
sed -i "0,/^OBS=\".*\"/ s//OBS=\"${obs_name}\"/" run_full.new_foc
|
||||||
|
#sed -i "0,/^OBS=\"[a-zA-Z \.]*\"/ s//OBS=\"${obs_name}\"/" run_full.new_foc
|
||||||
3
Auxiliary_utils/bash_scripts/video2dome1-2.sh
Executable file
3
Auxiliary_utils/bash_scripts/video2dome1-2.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
mpv -vo sixel --really-quiet=yes "rtsp://viewer:view25@192.168.70.25:554/axis-media/media.amp?videocodec=h264&resolution=640x480"
|
||||||
@@ -41,7 +41,7 @@ static glob_pars G = {
|
|||||||
* Define command line options by filling structure:
|
* Define command line options by filling structure:
|
||||||
* name has_arg flag val type argptr help
|
* name has_arg flag val type argptr help
|
||||||
*/
|
*/
|
||||||
static myoption cmdlnopts[] = {
|
static sl_option_t cmdlnopts[] = {
|
||||||
// common options
|
// common options
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
||||||
{"delimeter",NEED_ARG, NULL, 'd', arg_string, APTR(&G.delimeter), "coordinates delimeter string (default: ':')"},
|
{"delimeter",NEED_ARG, NULL, 'd', arg_string, APTR(&G.delimeter), "coordinates delimeter string (default: ':')"},
|
||||||
@@ -71,10 +71,10 @@ glob_pars *parse_args(int argc, char **argv){
|
|||||||
char helpstring[1024], *hptr = helpstring;
|
char helpstring[1024], *hptr = helpstring;
|
||||||
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||||
// format of help: "Usage: progname [args]\n"
|
// format of help: "Usage: progname [args]\n"
|
||||||
change_helpstring(helpstring);
|
sl_helpstring(helpstring);
|
||||||
// parse arguments
|
// parse arguments
|
||||||
parseargs(&argc, &argv, cmdlnopts);
|
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||||
if(help) showhelp(-1, cmdlnopts);
|
if(help) sl_showhelp(-1, cmdlnopts);
|
||||||
if(argc > 0){
|
if(argc > 0){
|
||||||
G.rest_pars_num = argc;
|
G.rest_pars_num = argc;
|
||||||
G.rest_pars = MALLOC(char *, argc);
|
G.rest_pars = MALLOC(char *, argc);
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ static void savepoints(FILE *f, point *pts, int N, char *delim, int mask){
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
initial_setup();
|
sl_init();
|
||||||
glob_pars *G = parse_args(argc, argv);
|
glob_pars *G = parse_args(argc, argv);
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
if(G->outfile){
|
if(G->outfile){
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ typedef struct{
|
|||||||
|
|
||||||
static glob_pars G = {.tolerance = 10.};
|
static glob_pars G = {.tolerance = 10.};
|
||||||
|
|
||||||
static myoption cmdlnopts[] = {
|
static sl_option_t cmdlnopts[] = {
|
||||||
// common options
|
// common options
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_none, APTR(&G.help), _("show this help")},
|
{"help", NO_ARGS, NULL, 'h', arg_none, APTR(&G.help), _("show this help")},
|
||||||
{"infile", NEED_ARG, NULL, 'i', arg_string, APTR(&G.input), _("input file name")},
|
{"infile", NEED_ARG, NULL, 'i', arg_string, APTR(&G.input), _("input file name")},
|
||||||
@@ -158,11 +158,11 @@ static double calcfocus(double coeffs[3]){
|
|||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
char helpstring[256];
|
char helpstring[256];
|
||||||
initial_setup();
|
sl_init();
|
||||||
snprintf(helpstring, 255, "Usage: `cat file | %%s` or with args; file format \"x y\\n..\"\n\tArgs:\n");
|
snprintf(helpstring, 255, "Usage: `cat file | %%s` or with args; file format \"x y\\n..\"\n\tArgs:\n");
|
||||||
change_helpstring(helpstring);
|
sl_helpstring(helpstring);
|
||||||
parseargs(&argc, &argv, cmdlnopts);
|
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||||
if(G.help) showhelp(-1, cmdlnopts);
|
if(G.help) sl_showhelp(-1, cmdlnopts);
|
||||||
if(G.tolerance <= 0.) ERRX("Tolerance should be > 0");
|
if(G.tolerance <= 0.) ERRX("Tolerance should be > 0");
|
||||||
FILE *f = stdin;
|
FILE *f = stdin;
|
||||||
if(G.input){
|
if(G.input){
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ static const char *radtodeg(double r){
|
|||||||
|
|
||||||
|
|
||||||
int main(){
|
int main(){
|
||||||
initial_setup();
|
sl_init();
|
||||||
at_MJD_t mjd;
|
at_MJD_t mjd;
|
||||||
if(!at_get_MJDu(time(NULL), &mjd)) ERRX("at_get_MJDu");
|
if(!at_get_MJDu(time(NULL), &mjd)) ERRX("at_get_MJDu");
|
||||||
printf("MJD=%g; TAI=%g/%g, TT=%g/%g, UTC=%g/%g\n", mjd.MJD, mjd.tai1, mjd.tai2, mjd.tt1, mjd.tt2, mjd.utc1, mjd.utc2);
|
printf("MJD=%g; TAI=%g/%g, TT=%g/%g, UTC=%g/%g\n", mjd.MJD, mjd.tai1, mjd.tai2, mjd.tt1, mjd.tt2, mjd.utc1, mjd.utc2);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ static parameters G = {
|
|||||||
.py = -10000.
|
.py = -10000.
|
||||||
};
|
};
|
||||||
|
|
||||||
static myoption cmdlnopts[] = {
|
static sl_option_t cmdlnopts[] = {
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||||
{"obsplace", NO_ARGS, NULL, 'O', arg_int, APTR(&G.obsplace), "input RA/Dec is observed place"},
|
{"obsplace", NO_ARGS, NULL, 'O', arg_int, APTR(&G.obsplace), "input RA/Dec is observed place"},
|
||||||
{"JD", NEED_ARG, NULL, 'J', arg_double, APTR(&G.JD), "Julian date"},
|
{"JD", NEED_ARG, NULL, 'J', arg_double, APTR(&G.JD), "Julian date"},
|
||||||
@@ -77,9 +77,9 @@ static myoption cmdlnopts[] = {
|
|||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
initial_setup();
|
sl_init();
|
||||||
parseargs(&argc, &argv, cmdlnopts);
|
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||||
if(G.help) showhelp(-1, cmdlnopts);
|
if(G.help) sl_showhelp(-1, cmdlnopts);
|
||||||
at_MJD_t MJD;
|
at_MJD_t MJD;
|
||||||
G.ra *= ERFA_DD2R;
|
G.ra *= ERFA_DD2R;
|
||||||
G.dec *= ERFA_DD2R;
|
G.dec *= ERFA_DD2R;
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ Different daemons & tools
|
|||||||
- *netsocket* - scripts for management of network 220V-socket
|
- *netsocket* - scripts for management of network 220V-socket
|
||||||
- *send_coordinates* - get/send coordinates to 10-micron mount through stellarium daemon
|
- *send_coordinates* - get/send coordinates to 10-micron mount through stellarium daemon
|
||||||
- *teldaemon* - open/close Astrosib-500 scope covers by network query
|
- *teldaemon* - open/close Astrosib-500 scope covers by network query
|
||||||
|
- *weatherdaemon* - weather daemon for old meteostation
|
||||||
|
- *weatherdaemon_newmeteo* - daemon for new (chinese) meteostation
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user