00001 #include "motion.h"
00002 #include "bppio.h"
00003 #include "bppimpl.h"
00004
00005
00006 #ifdef DEBUG
00007 const static int debuglevel =
00008 #ifdef DEBUGLEVEL
00009 DEBUGLEVEL;
00010 #else
00011 1;
00012 #endif
00013 #define DBUGMESS(N, PRINTARGS) if ((N) >= debuglevel) fprintf PRINTARGS
00014 #else
00015 #define DBUGMESS(N, PRINTARGS)
00016 #define DBUGPERR(ERR, MESS) if (ERR) perror(MESS)
00017 #endif
00018
00019 static const char* xlabel = "X";
00020 static const char* ylabel = "Y";
00021
00022 static const int deltamag = 1;
00023 static const velocity_t basespeed = 50.0;
00024 static const velocity_t vslow = 1.0 / 128;
00025 static const velocity_t minturn = 5.0;
00026 static const velocity_t mincoast = 0.5;
00027
00028 static void* track (void* arg);
00029 static int xstep(int fd, int incr);
00030 static int ystep(int fd, int incr);
00031 static int nextwake (const velocity_t vs, const velocity_t minspeed, struct timespec* thewake);
00032 static double findattract (const double qn, const velocity_t pn,
00033 const double qg, const velocity_t pg);
00034 static velocity_t nextvel (const double qn, const velocity_t pn,
00035 const double qg, const velocity_t pg,
00036 const double qc, coord_t* delta);
00037
00038
00039
00040
00041 void showreg (int fd)
00042 {
00043 struct bpp_reg reg_dump;
00044 int err;
00045
00046 err = ioctl(fd, BPPIOC_GETREGS, ®_dump);
00047 fprintf(stderr, "Status code (register dump) %d.\n", err);
00048 fprintf(stderr, "dma_csr = %x\n", reg_dump.dma_csr);
00049 fprintf(stderr, "dma_addr = %x\n", reg_dump.dma_addr);
00050 fprintf(stderr, "dma_bcnt = %d\n", reg_dump.dma_bcnt);
00051 fprintf(stderr, "dma_tst_csr = %x\n", reg_dump.dma_tst_csr);
00052 fprintf(stderr, "hw_config = %x\n", reg_dump.hw_config);
00053 fprintf(stderr, "op_config = %x\n", reg_dump.op_config);
00054 fprintf(stderr, "data = %x\n", reg_dump.data);
00055 fprintf(stderr, "trans_cntl = %x\n", reg_dump.trans_cntl);
00056 fprintf(stderr, "out_pins = %x\n", reg_dump.out_pins);
00057 fprintf(stderr, "in_pins = %x\n", reg_dump.in_pins);
00058 fprintf(stderr, "int_cntl = %x\n", reg_dump.int_cntl);
00059 }
00060
00061 int openstage (ptraject stage, const char* path)
00062 {
00063 int err = 0;
00064
00065 stage->filedescr = open(path, O_WRONLY);
00066 if (stage->filedescr < 0) {
00067 err = stage->filedescr;
00068 perror("Couldn't open");
00069 } else {
00070 int ax = stage->naxes;
00071 ptranslation trans = stage->axis;
00072 while (!err && ax-- && (trans != NULL)) {
00073 err = start(stage->filedescr, trans, &stage->tattr);
00074 trans++;
00075 }
00076 if (!err && !ax) {
00077 err = -1;
00078 DBUGMESS(1, (stderr, "openstage: undefined axis\n"));
00079 }
00080 }
00081 return err;
00082 }
00083
00084 int closestage (ptraject stage)
00085 {
00086 int ax = stage->naxes;
00087 ptranslation trans = stage->axis;
00088 int err = 0;
00089
00090 while (!err && ax-- && (trans != NULL)) {
00091 err = stop(trans);
00092 trans++;
00093 }
00094 if (!err && !ax) {
00095 err = -1;
00096 DBUGMESS(1, (stderr, "closestage: undefined axis\n"));
00097 }
00098 if (!err) {
00099 err = close(stage->filedescr);
00100 if (err)
00101 perror("Closestage couldn't close");
00102 }
00103 return err;
00104 }
00105
00106 void disposestage (ptraject stage)
00107 {
00108 free(stage->axis);
00109 pthread_attr_destroy(&stage->tattr);
00110 free(stage);
00111 }
00112
00113 int movestage (ptraject stage, pmotion motn)
00114 {
00115 int err = 0;
00116 int ax = stage->naxes;
00117
00118 ptranslation trans = stage->axis;
00119 while (!err && ax-- && (trans != NULL) && (motn != NULL)) {
00120 DBUGMESS(1, (stderr, "Movestage: starting %s axis\n", trans->label));
00121 err = moveto(trans, motn);
00122 trans++;
00123 motn++;
00124 }
00125 if (err) DBUGMESS(1, (stderr, "Movestage: error on axis %d, code %d\n", ax, err));
00126 if (!err && !ax) {
00127 err = -1;
00128 if (trans == NULL)
00129 DBUGMESS(1, (stderr, "movestage: undefined axis\n"));
00130 if (motn == NULL)
00131 DBUGMESS(1, (stderr, "movestage: undefined motion\n"));
00132 }
00133 return err;
00134 }
00135
00136 int findstage (ptraject stage, pmotion motn)
00137 {
00138 int err = 0;
00139 int ax = stage->naxes;
00140
00141 ptranslation trans = stage->axis;
00142 while (!err && ax-- && (trans != NULL) && (motn != NULL)) {
00143 DBUGMESS(1, (stderr, "Findstage: looking at %s axis\n", trans->label));
00144 err = where(trans, motn);
00145 trans++;
00146 motn++;
00147 }
00148 if (err) DBUGMESS(1, (stderr, "Findstage: error on axis %d, code %d\n", ax, err));
00149 if (!err && !ax) {
00150 err = -1;
00151 if (trans == NULL)
00152 DBUGMESS(1, (stderr, "Findstage: undefined axis\n"));
00153 if (motn == NULL)
00154 DBUGMESS(1, (stderr, "Findstage: undefined motion\n"));
00155 }
00156 return err;
00157 }
00158
00159 ptraject newxystage (void)
00160 {
00161 ptraject stage;
00162
00163 stage = (ptraject)malloc(sizeof(struct Traject));
00164 if (stage != NULL) {
00165 int err = pthread_attr_init(&stage->tattr);
00166 if (!err) {
00167 if (err = pthread_attr_setdetachstate(&stage->tattr, PTHREAD_CREATE_DETACHED))
00168 pthread_attr_destroy(&stage->tattr);
00169 }
00170 if (err) {
00171 free(stage);
00172 stage == NULL;
00173 }
00174 }
00175 if (stage != NULL) {
00176 stage->naxes = 2;
00177 stage->axis = (ptranslation)calloc(2, sizeof(struct Translation));
00178 if (stage->axis == NULL) {
00179 free(stage);
00180 stage = NULL;
00181 } else {
00182 stage->axis[0].label = xlabel;
00183 stage->axis[0].step = xstep;
00184 stage->axis[0].pos = 0;
00185 stage->axis[0].goalpos = 0;
00186 stage->axis[0].backlash= 0;
00187 stage->axis[0].maxlash = 13;
00188 stage->axis[0].vel = 0;
00189 stage->axis[0].goalvel = 0;
00190 stage->axis[1].label = ylabel;
00191 stage->axis[1].step = ystep;
00192 stage->axis[1].pos = 0;
00193 stage->axis[1].goalpos = 0;
00194 stage->axis[1].backlash= 0;
00195 stage->axis[1].maxlash = 2;
00196 stage->axis[1].vel = 0;
00197 stage->axis[1].goalvel = 0;
00198 }
00199 }
00200 return (stage);
00201 }
00202
00203 static double findattract (const double qn, const velocity_t pn,
00204 const double qg, const velocity_t pg)
00205 {
00206 double qc;
00207
00208 if (fabs(qn - qg) < deltamag/2.0) {
00209 qc = (fabs(pn - pg) <= fabs(pg)) ? qn : qn + pg;
00210 } else if ((fabs((pg + pn)/2) < deltamag/100.0) || (fabs(qn - qg) < deltamag/2.0)) {
00211 qc = (qn + qg)/2;
00212 } else {
00213 qc = (pow(pn,2) - pow(pg,2) + pow(qn,2) - pow(qg,2)) / (2*(qn - qg));
00214 }
00215 return (qc);
00216 }
00217
00218 static velocity_t nextvel (const double qn, const velocity_t pn,
00219 const double qg, const velocity_t pg,
00220 const double qc, coord_t* delta)
00221 {
00222 double r, r2;
00223 velocity_t v;
00224
00225 if ((fabs(qn - qg) < deltamag/2.0) &&
00226 ((fabs(pn - pg) < vslow) || (fabs(pn - pg) < (fabs(pn) + fabs(pg))))){
00227 DBUGMESS(1, (stderr, "*"));
00228 *delta = 0;
00229 v = pg;
00230 DBUGMESS(1, (stderr, "--->\n"));
00231 return (v);
00232 }
00233 r2 = pow(qc - qg, 2) + pow(pg, 2);
00234 r = sqrt(r2);
00235 if ((pn > 0) && (qn > (qc + r - (1 - 1E-4L)*deltamag))) {
00236 *delta = 0;
00237 v = -fabs(pn);
00238 } else if ((pn < 0) && (qn < (qc - r + (1 - 1E-4L)*deltamag))) {
00239 *delta = 0;
00240 v = fabs(pn);
00241 } else {
00242 *delta = ((pn < 0) || (pn == 0 && (qn > qc))) ? -deltamag : deltamag;
00243 v = sqrt(fabs(r2 - pow((qc - qn - *delta), 2)));
00244 if (*delta < 0)
00245 v = -v;
00246 }
00247 DBUGMESS(1, (stderr, "r=%10.4f qc=%10.4f qn=%10.4f qg=%10.4f pn=%10.4f pg=%10.4f v=%10.4f\n",
00248 r, qc, qn, qg, pn, pg, v));
00249 return v;
00250 }
00251
00252 static int nextwake (const velocity_t vs, const velocity_t minspeed, struct timespec* thewake)
00253 {
00254 long tick;
00255 time_t sec;
00256 int err = 0;
00257 velocity_t v = fabs(vs);
00258
00259 if (v < fabs(minspeed))
00260 v = fabs(minspeed);
00261 sec = (time_t)floor(1.0 / v);
00262 tick = (long)((double)NANOSEC * (1.0/v - sec));
00263 err = clock_gettime(CLOCK_REALTIME, thewake);
00264 if (err) {
00265 perror("Couldn't get time");
00266 } else {
00267 thewake->tv_sec += sec;
00268 if ((thewake->tv_nsec += tick) >= NANOSEC) {
00269 thewake->tv_nsec -= NANOSEC;
00270 thewake->tv_sec++;
00271 }
00272 }
00273 return (err);
00274 }
00275
00276 static int xstep(int fd, int incr)
00277 {
00278 struct bpp_delta move;
00279
00280 move.x = 0;
00281 move.y = incr;
00282 return ioctl(fd, BPPIOC_STEP, &move);
00283 }
00284
00285 static int ystep(int fd, int incr)
00286 {
00287 struct bpp_delta move;
00288
00289 move.x = incr;
00290 move.y = 0;
00291 return ioctl(fd, BPPIOC_STEP, &move);
00292 }
00293
00294 int moveto (ptranslation state, pmotion goal)
00295 {
00296 int err = 0;
00297
00298 err = pthread_mutex_lock(&state->modlock);
00299 if (err) {
00300 DBUGMESS(1, (stderr, "moveto (%s axis): couldn't acquire lock, error %d\n",
00301 state->label, err));
00302 return (err);
00303 }
00304 DBUGMESS(1, (stderr, "moveto (%s axis): going to %d, target speed %10.4f waiting....\n",
00305 state->label, goal->pos, goal->vel));
00306 while (!err && ((state->pos != state->goalpos) ||
00307 (fabs(state->vel - state->goalvel) >= vslow))) {
00308 err = pthread_cond_wait(&state->ontrack, &state->modlock);
00309 }
00310 if (err) {
00311 DBUGMESS(1, (stderr, "moveto (waiting on cv): invalid value\n"));
00312 } else {
00313 if (goal->relative) {
00314 state->goalpos = state->pos + goal->pos;
00315 state->goalvel = state->vel + goal->vel;
00316 } else {
00317 state->goalpos = goal->pos;
00318 state->goalvel = goal->vel;
00319 }
00320 state->scale = sqrt(120*(1 - (1/(1 + pow(state->vel - state->goalvel, 2) +
00321 pow(state->pos - state->goalpos, 2)))));
00322 if (state->scale < 0.25) state->scale = 0.25;
00323 state->attract = findattract(state->pos, state->vel / state->scale,
00324 state->goalpos, state->goalvel / state->scale);
00325 pthread_cond_signal(&state->offtrack);
00326 if (err)
00327 DBUGMESS(1, (stderr, "moveto (cv signal): invalid value\n"));
00328 }
00329 DBUGMESS(1, (stderr, "moveto (%s axis): going to %d, initial speed %10.4f\n",
00330 state->label, state->goalpos, state->vel));
00331 pthread_mutex_unlock(&state->modlock);
00332 return (err);
00333 }
00334
00335 int where (ptranslation state, pmotion here)
00336 {
00337 int err = 0;
00338
00339 err = pthread_mutex_lock(&state->modlock);
00340 if (err) {
00341 DBUGMESS(1, (stderr, "where (%s axis): couldn't acquire lock, error %d\n",
00342 state->label, err));
00343 return (err);
00344 }
00345 while (!err && (state->pos != state->goalpos)
00346 && (fabs(state->vel - state->goalvel) < vslow)) {
00347 err = pthread_cond_wait(&state->ontrack, &state->modlock);
00348 }
00349 if (err) {
00350 DBUGMESS(1, (stderr, "where: invalid value\n"));
00351 } else {
00352 here->pos = state->pos;
00353 here->vel = state->vel;
00354 }
00355 pthread_mutex_unlock(&state->modlock);
00356 return (err);
00357 }
00358
00359 static void* track (void* arg)
00360 {
00361 ptranslation state = (ptranslation)arg;
00362 struct timespec wakeup;
00363 int delta;
00364 int err = 0;
00365
00366 err = pthread_mutex_lock(&state->modlock);
00367 if (err)
00368 DBUGMESS(1, (stderr, "track (%s axis): couldn't acquire lock, error %d\n", state->label, err));
00369 while (!err) {
00370
00371 while (!err && (state->pos == state->goalpos)
00372 && (fabs(state->vel) < vslow) && (fabs(state->goalvel) < vslow)) {
00373 DBUGMESS(1, (stderr, "%s resting\n", state->label));
00374 err = pthread_cond_wait(&state->offtrack, &state->modlock);
00375 }
00376
00377 delta = ((state->vel > 0) ||
00378 ((state->vel == 0) && (state->pos < state->goalpos))) ? deltamag : -deltamag;
00379 err = nextwake(state->vel, mincoast, &wakeup);
00380
00381 while (!err && (state->pos == state->goalpos)
00382 && (fabs(state->vel - state->goalvel) < vslow)) {
00383 DBUGMESS(1, (stderr, "%s coasting pos = %d vel = %10.4f\n",
00384 state->label, state->pos, state->vel));
00385 err = pthread_cond_timedwait(&state->offtrack, &state->modlock, &wakeup);
00386 if (err == ETIMEDOUT) {
00387 err = (*(state->step))(state->device, delta);
00388 if (!err) {
00389 err = nextwake(state->vel, mincoast, &wakeup);
00390 if (abs(state->backlash + delta) < state->maxlash) {
00391 state->backlash += delta;
00392 } else {
00393 state->pos += delta;
00394 state->goalpos += delta;
00395 }
00396 }
00397 }
00398 }
00399
00400 delta = ((state->vel > 0) ||
00401 ((state->vel == 0) && (state->pos < state->goalpos))) ? deltamag : -deltamag;
00402 err = nextwake(state->vel, minturn, &wakeup);
00403 while (!err && ((state->pos != state->goalpos) ||
00404 (fabs(state->vel - state->goalvel) >= vslow))) {
00405 DBUGMESS(1, (stderr, ">>> %s moving pos = %d vel = %10.4f\n",
00406 state->label, state->pos, state->vel));
00407 err = pthread_cond_timedwait(&state->ontrack, &state->modlock, &wakeup);
00408 if (err == ETIMEDOUT) {
00409 if (delta) {
00410 err = (*(state->step))(state->device, delta);
00411 if (err)
00412 perror("driving motor");
00413 if (abs(state->backlash + delta) < state->maxlash) {
00414 state->backlash += delta;
00415 } else {
00416 state->pos += delta;
00417 }
00418 } else {
00419 err = 0;
00420 }
00421 if (!err) {
00422 state->vel = nextvel(state->pos, state->vel / state->scale,
00423 state->goalpos, state->goalvel / state->scale,
00424 state->attract, &delta) * state->scale;
00425 err = nextwake(state->vel, minturn, &wakeup);
00426 }
00427 }
00428 }
00429 if (!err && (state->pos == state->goalpos)) {
00430 DBUGMESS(1, (stderr, "*** %s reached goal pos = %d vel = %10.4f\n",
00431 state->label, state->pos, state->vel));
00432
00433 pthread_cond_signal(&state->ontrack);
00434 }
00435 }
00436 DBUGMESS(1, (stderr, "Exiting on code %d\n", err));
00437 pthread_mutex_unlock(&state->modlock);
00438 return (NULL);
00439 }
00440
00441 int start (int fd, ptranslation state, pthread_attr_t* ptattr)
00442 {
00443 int err = 0;
00444
00445 state->device = fd;
00446 if (err = pthread_mutex_init(&state->modlock, NULL)) {
00447 DBUGMESS(1, (stderr, "start (%s mutex init): ", state->label));
00448 } else if (err = pthread_cond_init(&state->offtrack, NULL)) {
00449 DBUGMESS(1, (stderr, "start (%s offtrack init): ", state->label));
00450 } else if (err = pthread_cond_init(&state->ontrack, NULL)) {
00451 DBUGMESS(1, (stderr, "start (%s ontrack init): ", state->label));
00452 } else if (err = pthread_create(&state->tid, ptattr, track, (void*)state)) {
00453 DBUGMESS(1, (stderr, "start (%s thread creation): ", state->label));
00454 }
00455 switch (err) {
00456 case ENOMEM:
00457 DBUGMESS(1, (stderr, "not enough memory.\n"));
00458 break;
00459 case EAGAIN:
00460 DBUGMESS(1, (stderr, "system limit reached.\n"));
00461 break;
00462 case EINVAL:
00463 DBUGMESS(1, (stderr, "invalid attribute.\n"));
00464 break;
00465 case ESRCH:
00466 DBUGMESS(1, (stderr, "invalid thread ID.\n"));
00467 break;
00468 default:
00469 if (err)
00470 DBUGMESS(1, (stderr, "error %d.\n", err));
00471 }
00472 return (err);
00473 }
00474
00475 int stop (ptranslation state)
00476 {
00477 int err = 0;
00478
00479 DBUGMESS(1, (stderr, "stop (%s axis): waiting . . . .\n", state->label));
00480 pthread_mutex_lock(&state->modlock);
00481 if (err) {
00482 DBUGMESS(1, (stderr, "stop (%s axis): couldn't acquire lock, error %d\n",
00483 state->label, err));
00484 return (err);
00485 }
00486 while (!err && ((state->pos != state->goalpos) ||
00487 (fabs(state->vel - state->goalvel) >= vslow))) {
00488 err = pthread_cond_wait(&state->ontrack, &state->modlock);
00489 }
00490 DBUGMESS(1, (stderr, "stop (%s axis): starting cleanup\n", state->label));
00491 if (err) {
00492 DBUGMESS(1, (stderr, "stop (%s waiting for move to complete): ", state->label));
00493 } else if (err = pthread_cancel(state->tid)) {
00494 DBUGMESS(1, (stderr, "stop (%s cancelling thread): ", state->label));
00495 }
00496 pthread_mutex_unlock(&state->modlock);
00497 if (!err) {
00498 if (err = pthread_cond_destroy(&state->offtrack)) {
00499 DBUGMESS(1, (stderr, "stop (%s offtrack destroy): ", state->label));
00500 } else if (err = pthread_cond_destroy(&state->ontrack)) {
00501 DBUGMESS(1, (stderr, "stop (%s ontrack destroy): ", state->label));
00502 } else if (err = pthread_mutex_destroy(&state->modlock)) {
00503 DBUGMESS(1, (stderr, "stop (%s mutex destroy): ", state->label));
00504 }
00505 }
00506 switch (err) {
00507 case ESRCH:
00508 DBUGMESS(1, (stderr, "invalid thread ID.\n"));
00509 break;
00510 case EINVAL:
00511 DBUGMESS(1, (stderr, "invalid attribute.\n"));
00512 break;
00513 default:
00514 if (err)
00515 DBUGMESS(1, (stderr, "error %d.\n", err));
00516 }
00517 return (err);
00518 }