Compare commits

...

18 Commits

Author SHA1 Message Date
9f2e893f1a seems like PID works on real telescope 2026-03-23 17:22:58 +03:00
Edward Emelianov
6ea6bad008 add astrosib telescope daemon 2026-03-23 00:05:51 +03:00
Edward Emelianov
ddce8437ce add permissive and prohibited signals 2026-03-22 22:30:55 +03:00
Edward Emelianov
6c36b0acc9 new baader daemon 2026-03-22 21:15:14 +03:00
fa305a7cc0 fix encoders 2026-03-11 10:05:02 +03:00
205a190820 some fixes, sill have troubles with dummy socket/fd 2026-03-06 12:20:10 +03:00
7c2aaf1cb0 add serial/socket plugin example 2026-02-27 18:31:22 +03:00
54778dcf9a add pre-pre-alpha 2026-02-27 12:05:40 +03:00
Edward Emelianov
31752b58cc polltest 2026-01-26 22:27:51 +03:00
09642743a6 add some bash scripts 2026-01-26 17:16:49 +03:00
a80347643f fixed for new vesrion of usefull_macros 2026-01-26 16:54:50 +03:00
Edward Emelianov
50cbaea550 some work done 2026-01-24 00:57:11 +03:00
c8449c916a fixed for no-connect error 2026-01-20 09:27:06 +03:00
74d9ebb75f .. 2025-12-08 21:04:06 +03:00
Edward Emelianov
2e9241f079 time 2025-11-01 19:54:18 +03:00
2aa8502796 . 2025-11-01 15:39:55 +03:00
f6edc12b01 fixed time 2025-11-01 15:02:11 +03:00
27a7388164 OK 2025-09-26 14:57:24 +03:00
225 changed files with 5692 additions and 752 deletions

3
.gitignore vendored
View File

@@ -23,3 +23,6 @@
*.so *.so
*.so.* *.so.*
# build dirs
mk/
build/

View File

@@ -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++)

View File

@@ -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;

View File

@@ -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);
} }

View File

@@ -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

View File

@@ -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);
} }

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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
} }

View File

@@ -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];
} }

View File

@@ -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);

View File

@@ -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;
} }
} }

View File

@@ -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);

View File

@@ -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){

View File

@@ -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

View File

@@ -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)){

View File

@@ -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;

View File

@@ -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));
DBG("%g: dX=%.4f'', dY=%.4f''", 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(errlog)
fprintf(errlog, "%10.4g %10.4g %10.4g\n", t, 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));
if(MCC_E_OK != Mount.correctTo(&target, &endpoint)) WARNX("Error of correction!"); }
while((t = Mount.currentT()) - tlast < MCC_PID_REFRESH_DT) usleep(50); if(MCC_E_OK != Mount.correctTo(&target)) WARNX("Error of correction!");
while((t = Mount.timeFromStart()) - tlast < Config->PIDRefreshDt) usleep(500);
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");
; ;

View File

@@ -1,4 +1,4 @@
Current configuration: # Current configuration
MountDevPath=/dev/ttyUSB0 MountDevPath=/dev/ttyUSB0
MountDevSpeed=19200 MountDevSpeed=19200
EncoderDevPath=(null) EncoderDevPath=(null)

View File

@@ -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}
}; };

View 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;
}

View 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);

View File

@@ -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>

View File

@@ -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

View File

@@ -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, &param, curt)) return MCC_E_FAILED; if(!model_move2(Xmodel, &param, 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, &param, curt)) return MCC_E_FAILED; if(!model_move2(Xmodel, &param, 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,
}; };

View File

@@ -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))

View File

@@ -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);
} }

View File

@@ -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{

View 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;
}

View File

@@ -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;
} }

View File

@@ -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,15 +256,16 @@ 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)){
parse_encbuf(databuf, &tcur);
wridx = 0; wridx = 0;
} }
} }
}
if(encfd[0] > -1){ if(encfd[0] > -1){
close(encfd[0]); close(encfd[0]);
encfd[0] = -1; 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;
break;
}
}
double curt = timefromstart();
if(getdata(&strbuf[i], &msrlast[i])) mtlast[i] = curt;
if(curt - t0[i] >= Conf.EncoderReqInterval){ // get last records
//DBG("last rec %d, curt=%g, t0=%g, mtlast=%g", i, curt, t0[i], mtlast[i]);
if(curt - mtlast[i] < 1.5*Conf.EncoderReqInterval){
//DBG("time OK");
pthread_mutex_lock(&datamutex);
double pos = (double)msrlast[i];
if(i == 0){
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(); getXspeed();
if(getencval(encfd[1], &v, &t)){ //mountdata.encXspeed.val = kf[i].x[1];
mountdata.encYposition.val = Y_ENC2RAD(v); //mountdata.encXspeed.t = mountdata.encXposition.t;
//DBG("encY(%g) = %g", t, mountdata.encYposition.val); }else{
mountdata.encYposition.t = t; pos = Yenc2rad(pos);
//if(t - lastYenc.t > Conf.EncoderSpeedInterval) getYspeed(); 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(); getYspeed();
errctr = 0; //mountdata.encYspeed.val = kf[i].x[1];
need2ask = 0; //mountdata.encYspeed.t = mountdata.encYposition.t;
} else {
if(need2ask) ++errctr;
else need2ask = 1;
continue;
} }
} else { pthread_mutex_unlock(&datamutex);
if(need2ask) ++errctr;
else need2ask = 1;
continue;
} }
while(nanotime() - t0 < Conf.EncoderReqInterval){ usleep(50); } if(!asknext(encfd[i])){
//DBG("DT=%g (RI=%g)", nanotime()-t0, Conf.EncoderReqInterval); ++errctr;
t0 = nanotime(); break;
} }
DBG("ERRCTR=%d", errctr); t0[i] = (curt - t0[i] < 2.*Conf.EncoderReqInterval) ? t0[i] + Conf.EncoderReqInterval : curt;
++got;
}
}
if(got == 2) errctr = 0;
}while(encfd[0] > -1 && encfd[1] > -1 && errctr < MAX_ERR_CTR && !GlobExit);
DBG("\n\nEXIT: 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
static uint32_t oldmillis = 0;
if(Conf.RunModel){
double Xprev = NAN, Yprev = NAN; // previous coordinates
int xcnt = 0, ycnt = 0;
while(!GlobExit){
coordpair_t c;
movestate_t xst, yst;
// now change data // now change data
getModData(&mountdata); 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); pthread_mutex_unlock(&datamutex);
double t0 = nanotime(); while(timefromstart() - t0 < Conf.EncoderReqInterval) usleep(50);
while(nanotime() - t0 < Conf.EncoderReqInterval) usleep(50); t0 = timefromstart();
t0 = nanotime(); }
} }
// 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;
for(size_t i = 0; i < in->maxlen; ++i){ return FALSE;
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 = 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); 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:

View 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

View File

@@ -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;

View File

@@ -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);
if(++(*nstopped) > MOTOR_STOPPED_CNT){
stat = AXIS_STOPPED;
DBG("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(MCC_E_OK == getMD(&md)){ if(!curtime(&curt)){
if(md.encXposition.t == 0 || md.encYposition.t == 0){ usleep(10000);
DBG("Just started, t-t0 = %g!", t - t0);
sleep(1);
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); //DBG("XENC2RAD: %g (xez=%d, xesr=%.10g)", Xenc2rad(32424842), X_ENC_ZERO, X_ENC_STEPSPERREV);
if(MCC_E_OK == getMD(&md)){
if(md.encXposition.t.tv_sec == 0 || md.encYposition.t.tv_sec == 0){
DBG("Just started? t-t0 = %g!", t - t0);
usleep(10000);
continue;
}
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");

View File

@@ -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);

View File

@@ -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")

View File

@@ -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");

View File

@@ -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++){

View File

@@ -1,5 +1,5 @@
# run `make DEF=...` to add extra defines # run `make DEF=...` to add extra defines
PROGRAM := getparams PROGRAM := invertercmd
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
LDFLAGS += -lusefull_macros LDFLAGS += -lusefull_macros
SRCS := $(wildcard *.c) SRCS := $(wildcard *.c)

Binary file not shown.

View File

@@ -30,6 +30,7 @@ typedef struct{
char *getstatus; char *getstatus;
int baudrate; int baudrate;
int status; int status;
int settershelp;
} globopts; } globopts;
typedef struct{ typedef struct{
@@ -39,6 +40,9 @@ typedef struct{
int flag; int flag;
int status; int status;
int mode; int mode;
int warning;
int deflt;
int bateq;
} statusinfo; } statusinfo;
typedef void (*parsefn)(const char *answer); typedef void (*parsefn)(const char *answer);
@@ -55,7 +59,8 @@ static sl_option_t opts[] = {
{"devpath", NEED_ARG, NULL, 'd', arg_string, APTR(&G.path), "path to device (default: /dev/ttyS0)"}, {"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)"}, {"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"}, {"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 (type 'help' for options)"}, {"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 end_option
}; };
@@ -66,6 +71,9 @@ static sl_suboption_t sopts[] = {
{"flag", NO_ARGS, arg_int, APTR(&S.flag)}, {"flag", NO_ARGS, arg_int, APTR(&S.flag)},
{"status", NO_ARGS, arg_int, APTR(&S.status)}, {"status", NO_ARGS, arg_int, APTR(&S.status)},
{"mode", NO_ARGS, arg_int, APTR(&S.mode)}, {"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 end_suboption
}; };
@@ -76,16 +84,20 @@ static void gettershelp(){
fprintf(stderr, "flag - device flag status (QFLAG)\n"); fprintf(stderr, "flag - device flag status (QFLAG)\n");
fprintf(stderr, "status - device general status parameters (QPIGS)\n"); fprintf(stderr, "status - device general status parameters (QPIGS)\n");
fprintf(stderr, "mode - device mode (QMOD)\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"); // fprintf(stderr, "\n");
} }
static uint8_t *cal_crc(const char *cmd, int len){ static uint8_t *cal_crc(const char *cmd, int len){
static uint8_t CRC[3]; // 0 - hi, 1 - low static uint8_t CRC[4]; // 0 - hi, 1 - low, four bytes for quick zeroing
if(!cmd || len < 1) return 0; if(!cmd || len < 1) return 0;
const uint16_t crc_table[16] = { const uint16_t crc_table[16] = {
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef
}; };
*((uint32_t*)CRC) = 0;
uint16_t crc = 0; uint16_t crc = 0;
uint8_t *ptr = (uint8_t*)cmd; uint8_t *ptr = (uint8_t*)cmd;
while(len--){ while(len--){
@@ -131,6 +143,7 @@ static char *rd(){
if(!dev) return NULL; if(!dev) return NULL;
int got = sl_tty_read(dev); int got = sl_tty_read(dev);
if(got < 0) ERR("Can't read"); if(got < 0) ERR("Can't read");
DBG("got %d bytes, buflen: %zd", got, dev->buflen);
if(dev->buflen < 3) return NULL; if(dev->buflen < 3) return NULL;
uint8_t *CRC = cal_crc(dev->buf, dev->buflen - 3); 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]); DBG("GOT CRC: 0x%02X 0x%02X", (uint8_t)dev->buf[dev->buflen-3], (uint8_t)dev->buf[dev->buflen-2]);
@@ -152,7 +165,7 @@ static void ratingparsing(const char *str){
&b, &c, &d, &e, &f, &h, &i, &j1, &k1, &j2, &k2, &l, &o1, &p1, &q, &o2, &p2, &q2, &r, &s, &t, &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); &u, &v, &w, &x);
if(N != 25){ WARNX("Got not full answer (%d instead of 25): '%s'", N, str); return; } if(N != 25){ WARNX("Got not full answer (%d instead of 25): '%s'", N, str); return; }
printf("\nGrid rating voltage: %g\nGrid rating current: %g\nAC optuput rating voltage: %g\n", b, c, d); 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 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("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); printf("Battery undervoltage: %g\nBattery bulk voltage: %g\nBattery float voltage: %g\n", j2, k2, l);
@@ -175,27 +188,26 @@ static void ratingparsing(const char *str){
printf("\nTopology: %s\n", (t) ? "transformer" : "transformerless"); printf("\nTopology: %s\n", (t) ? "transformer" : "transformerless");
printf("Output mode: %d\nBattery redischarge voltage: %g\n", u, v); 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 OK condition for parallel: %s\n", (w) ? "Only all connected" : "At least one connected");
printf("PV power balance: %s\n\n", (x) ? "Sum of powers" : "Max charged current"); 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){ static void flagparsing(const char *str){
char c; char c;
int first = 0; int first = 0;
while((c = *str++)){ while((c = *str++)){
if(c == 'D'){ red("\nDISABLED: "); first = 1; } if(c == 'D'){ green("\nDISABLED: "); first = 1; }
else if(c == 'E'){ green("\nENABLED: "); first = 1; } else if(c == 'E'){ red("ENABLED: "); first = 1; }
else{ else{
const char *field = "unknown"; const char *field = "unknown";
switch(c){ for(int i = 0; DEflags[i]; ++i){
case 'a': field = "buzzer"; break; if(DEflags[i] == c){ field = DEmeanings[i]; break; }
case 'b': field = "bypass"; break;
case 'j': field = "power saving"; break;
case 'k': field = "LCD display escape 1min"; break;
case 'u': field = "overload restart"; break;
case 'v': field = "over temperature restart"; break;
case 'x': field = "backlight on"; break;
case 'y': field = "alarm on interrupt"; break;
case 'z': field = "fault code record"; break;
} }
printf("%s%s", (first) ? "" : ", ", field); printf("%s%s", (first) ? "" : ", ", field);
first = 0; first = 0;
@@ -204,9 +216,19 @@ static void flagparsing(const char *str){
printf("\n"); 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){ static void showflags(const char *flags, const char **meaning, int nfields){
for(int i = 0; i < nfields; ++i) for(int i = 0; i < nfields; ++i){
printf("\t%s: %s\n", meaning[i], flags[i]=='1' ? "on/yes" : "off/no"); 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 // 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
@@ -214,8 +236,6 @@ static void showflags(const char *flags, const char **meaning, int nfields){
static void statusparsing(const char *str){ static void statusparsing(const char *str){
float b, c, d, e, j, u, w; float b, c, d, e, j, u, w;
int f, g, h, i, k, o, t, e1, p, S, H, I; int f, g, h, i, k, o, t, e1, p, S, H, I;
//int l = strlen(str);
//for(int i = 0; i < l; ++i){char c = str[i]; if(isalnum(c)||c==' '||c=='.') printf("%c", c); else printf("\\x%02X", c); }
char x[9], T[4]; 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", 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); &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &o, &t, &e1, &u, &w, &p, x, &S, &H, &I, T);
@@ -227,8 +247,8 @@ static void statusparsing(const char *str){
printf("Bus voltage: %d\nBattery voltage: %g\nBattery charging current: %d\nBattery capacity: %d\n", i, j, k, o); 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("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); printf("Battery voltage from SCC: %g\nBattery discharge current: %d\nDevice status:\n", w, p);
static const char *sf[] = {"AC charging", "SCC charging", "Charging", "Steady batt voltage while charging", static const char *sf[] = {"SBU priority version", "configuration changed", "SCC firmware updated",
"Load status", "SCC firmware updated", "configuration changed", "SBU priority version"}; "Load status", "Steady batt voltage while charging", "Charging", "SCC charging", "AC charging"};
showflags(x, sf, 8); showflags(x, sf, 8);
if(N > 17){ if(N > 17){
printf("Battery offset for fans on: %d\nEEPROM version: %d\nPV charging power: %d\n", S, H, I); printf("Battery offset for fans on: %d\nEEPROM version: %d\nPV charging power: %d\n", S, H, I);
@@ -242,29 +262,125 @@ static void statusparsing(const char *str){
static void modeparsing(const char *str){ static void modeparsing(const char *str){
printf("Device mode: "); printf("Device mode: ");
switch(*str){ switch(*str){
case 'B': printf("Battery\n"); break; case 'B': printf("Battery"); break;
case 'F': printf("Fault\n"); break; case 'F': printf("Fault"); break;
case 'H': printf("Power saving\n"); break; case 'H': printf("Power saving"); break;
case 'L': printf("Line\n"); break; case 'L': printf("Line"); break;
case 'P': printf("Power on\n"); break; case 'P': printf("Power on"); break;
case 'S': printf("Standby\n"); break; case 'S': printf("Standby"); break;
default: printf("Unknown"); default: printf("Unknown");
} }
printf("\n"); 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){ static void runparsing(const char *cmd, parsefn f){
if(sendcmd(cmd)){ if(sendcmd(cmd)){
char *got = rd(); char *got = rd();
if(got) f(got); if(got && *got){
f(got);
printf("\n");
} else red("Can't get data\n\n");
} }
} }
static void showstatus(){ static void showstatus(){
green("\nModel name: "); runparsing("QMN", printpar);
if(S.all || S.rating) runparsing("QPIRI", ratingparsing); if(S.all || S.rating) runparsing("QPIRI", ratingparsing);
if(S.all || S.flag) runparsing("QFLAG", flagparsing); if(S.all || S.flag) runparsing("QFLAG", flagparsing);
if(S.all || S.status) runparsing("QPIGS", statusparsing); if(S.all || S.status) runparsing("QPIGS", statusparsing);
if(S.all || S.mode) runparsing("QMOD", modeparsing); 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){ int main(int argc, char **argv){
@@ -293,5 +409,6 @@ int main(int argc, char **argv){
} }
} }
sl_tty_close(&dev); sl_tty_close(&dev);
if(G.settershelp) showsettershelp();
return 0; return 0;
} }

View File

@@ -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")

View File

@@ -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++)

View File

@@ -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");
} }

View File

@@ -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}#"

View 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

View 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

View 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/'"

View 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"

View 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"

View 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

View 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

View 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"

View File

@@ -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);

View File

@@ -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){

View File

@@ -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){

View File

@@ -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);

View File

@@ -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;

View File

@@ -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