1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Interface for the Servolens lens attached to the camera fixed on the
33  * Afma4 robot.
34  *
35  * Authors:
36  * Fabien Spindler
37  *
38  *****************************************************************************/
39 
40 /*!
41 
42   \file vpServolens.cpp
43 
44   Interface for the Servolens lens attached to the camera fixed on the
45   Afma4 robot.
46 
47 */
48 
49 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
50 
51 #include <fcntl.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <sys/stat.h>
56 #include <sys/types.h>
57 #include <termios.h>
58 #include <unistd.h>
59 
60 #include <visp3/core/vpDebug.h>
61 #include <visp3/core/vpTime.h>
62 #include <visp3/robot/vpRobotException.h>
63 #include <visp3/robot/vpServolens.h>
64 
65 /*!
66   Default constructor. Does nothing.
67 
68   \sa open()
69 
70 */
vpServolens()71 vpServolens::vpServolens() : remfd(0), isinit(false) {}
72 
73 /*!
74   Open and initialize the Servolens serial link at 9600 bauds, 7
75   data bits, even parity, 1 stop bit. The cariage return mode is not
76   active, that means that each character is directly read without
77   waitong for a cariage return.
78 
79   \sa open()
80 */
vpServolens(const char * port)81 vpServolens::vpServolens(const char *port) : remfd(0), isinit(false) { this->open(port); }
82 
83 /*!
84   Destructor.
85 
86   Close the Servolens serial link.
87 
88   \sa close()
89 */
~vpServolens()90 vpServolens::~vpServolens() { this->close(); }
91 
92 /*!
93 
94   Open and initialize the Servolens serial link at 9600 bauds, 7
95   data bits, even parity, 1 stop bit. The cariage return mode is not
96   active, that means that each character is directly read without
97   waitong for a cariage return.
98 
99   \param port : Serial device like /dev/ttyS0 or /dev/ttya.
100 
101   \exception vpRobotException::communicationError : If cannot open
102   Servolens serial port or intialize the serial link.
103 
104   \sa close()
105 */
open(const char * port)106 void vpServolens::open(const char *port)
107 {
108   if (!isinit) {
109     struct termios info;
110 
111     printf("\nOpen the Servolens serial port \"%s\"\n", port);
112 
113     if ((this->remfd = ::open(port, O_RDWR | O_NONBLOCK)) < 0) {
114       vpERROR_TRACE("Cannot open Servolens serial port.");
115       throw vpRobotException(vpRobotException::communicationError, "Cannot open Servolens serial port.");
116     }
117 
118     // Lecture des parametres courants de la liaison serie.
119     if (tcgetattr(this->remfd, &info) < 0) {
120       ::close(this->remfd);
121       vpERROR_TRACE("Error using TCGETS in ioctl.");
122       throw vpRobotException(vpRobotException::communicationError, "Error using TCGETS in ioctl");
123     }
124 
125     //
126     // Configuration de la liaison serie:
127     // 9600 bauds, 1 bit de stop, parite paire, 7 bits de donnee
128     //
129 
130     // Traitement sur les caracteres recus
131     info.c_iflag = 0;
132     info.c_iflag |= INLCR;
133 
134     // Traitement sur les caracteres envoyes sur la RS232.
135     info.c_oflag = 0; // idem
136 
137     // Traitement des lignes
138     info.c_lflag = 0;
139 
140     // Controle materiel de la liaison
141     info.c_cflag = 0;
142     info.c_cflag |= CREAD;                // Validation reception
143     info.c_cflag |= B9600 | CS7 | PARENB; // 9600 baus, 7 data, parite paire
144 
145     // Caracteres immediatement disponibles.
146     //  info.c_cc[VMIN] = 1;
147     //  info.c_cc[VTIME] = 0;
148 
149     if (tcsetattr(this->remfd, TCSANOW, &info) < 0) {
150       ::close(this->remfd);
151       vpERROR_TRACE("Error using TCGETS in ioctl.");
152       throw vpRobotException(vpRobotException::communicationError, "Error using TCGETS in ioctl");
153     }
154 
155     // Supprime tous les caracteres recus mais non encore lus par read()
156     tcflush(this->remfd, TCIFLUSH);
157 
158     isinit = true;
159 
160     this->init();
161 
162     // Try to get the position of the zoom to check if the lens is really
163     // connected
164     unsigned int izoom;
165     if (this->getPosition(vpServolens::ZOOM, izoom) == false) {
166       vpERROR_TRACE("Cannot dial with the servolens. Check if the serial "
167                     "link is connected.");
168       throw vpRobotException(vpRobotException::communicationError, "Cannot dial with the servolens. Check if the "
169                                                                    "serial link is connected.");
170     }
171   }
172 }
173 
174 /*!
175   Close the Servolens serial link.
176   \sa open()
177 */
close()178 void vpServolens::close()
179 {
180   if (isinit) {
181     printf("\nClose the serial connection with Servolens\n");
182     ::close(this->remfd);
183     isinit = false;
184   }
185 }
186 
187 /*!
188   Reset the Servolens.
189 
190   \exception vpRobotException::communicationError : If cannot dial
191   with Servolens.
192 */
reset() const193 void vpServolens::reset() const
194 {
195   if (!isinit) {
196     vpERROR_TRACE("Cannot dial with Servolens.");
197     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
198   }
199   char commande[10];
200 
201   /* suppression de l'echo */
202   sprintf(commande, "SE1");
203   this->write(commande);
204 
205   /* initialisation de l'objectif, idem qu'a la mise sous tension */
206   sprintf(commande, "SR0");
207   this->write(commande);
208 
209   vpTime::wait(25000);
210 
211   this->wait();
212 
213   /* suppression de l'echo */
214   sprintf(commande, "SE0");
215   this->write(commande);
216 
217   /* devalide l'incrustation de la fenetre sur l'ecran du moniteur */
218   sprintf(commande, "VW0");
219   this->write(commande);
220 }
221 /*!
222   Initialize the Servolens lens.
223 
224   \exception vpRobotException::communicationError : If cannot dial
225   with Servolens.
226 
227   \sa open()
228 */
init() const229 void vpServolens::init() const
230 {
231   if (!isinit) {
232     vpERROR_TRACE("Cannot dial with Servolens.");
233     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
234   }
235 
236   char commande[10];
237 
238   /* suppression de l'echo */
239   sprintf(commande, "SE0");
240   this->write(commande);
241 
242   /* devalide l'incrustation de la fenetre sur l'ecran du moniteur */
243   sprintf(commande, "VW0");
244   this->write(commande);
245 
246   /* L'experience montre qu'une petite tempo est utile.		*/
247   vpTime::wait(500);
248 }
249 
250 /*!
251   Enable or disable the emission of the Servolens prompt "SERVOLENS>".
252 
253   \param active : true to activate the emission of the prompy. false
254   to disable this functionality.
255 
256   \exception vpRobotException::communicationError : If cannot dial
257   with Servolens.
258 
259 */
enablePrompt(bool active) const260 void vpServolens::enablePrompt(bool active) const
261 {
262   if (!isinit) {
263     vpERROR_TRACE("Cannot dial with Servolens.");
264     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
265   }
266   char commande[10];
267 
268   /* suppression de l'echo */
269   if (active == true)
270     sprintf(commande, "SE1");
271   else
272     sprintf(commande, "SE0");
273 
274   this->write(commande);
275 }
276 
277 /*!
278   Set the controller type.
279 
280   \param controller : Controller type.
281 
282   \exception vpRobotException::communicationError : If cannot dial
283   with Servolens.
284 */
setController(vpControllerType controller) const285 void vpServolens::setController(vpControllerType controller) const
286 {
287   if (!isinit) {
288     vpERROR_TRACE("Cannot dial with Servolens.");
289     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
290   }
291   char commande[10];
292 
293   switch (controller) {
294   case AUTO:
295     /* Valide l'incrustation de la fenetre sur l'ecran du moniteur */
296     sprintf(commande, "VW1");
297     this->write(commande);
298     break;
299   case CONTROLLED:
300     /* nettoyage : mot d'etat vide 0000 */
301     sprintf(commande, "SX0842");
302     this->write(commande);
303     /* devalide l'incrustation de la fenetre sur l'ecran du moniteur */
304     sprintf(commande, "VW0");
305     this->write(commande);
306     break;
307   case RELEASED:
308     sprintf(commande, "SX1084");
309     this->write(commande);
310     /* devalide l'incrustation de la fenetre sur l'ecran du moniteur */
311     sprintf(commande, "VW0");
312     this->write(commande);
313     break;
314   }
315 }
316 
317 /*!
318   Activates the auto iris mode of the Servolens.
319 
320   \param enable : true to activate the auto iris.
321 
322 */
setAutoIris(bool enable) const323 void vpServolens::setAutoIris(bool enable) const
324 {
325   if (!isinit) {
326     vpERROR_TRACE("Cannot dial with Servolens.");
327     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
328   }
329   char commande[10];
330 
331   if (enable)
332     sprintf(commande, "DA1");
333   else
334     sprintf(commande, "DA0");
335 
336   this->write(commande);
337 }
338 
339 /*!
340   Set the Servolens servo to the desired position.
341 
342   \param servo : Servolens servo motor to actuate.
343   \param position : Desired position of the servo.
344 
345   \exception vpRobotException::communicationError : If cannot dial
346   with Servolens.
347 
348 */
setPosition(vpServoType servo,unsigned int position) const349 void vpServolens::setPosition(vpServoType servo, unsigned int position) const
350 {
351   if (!isinit) {
352     vpERROR_TRACE("Cannot dial with Servolens.");
353     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
354   }
355   std::stringstream command;
356 
357 /* attente du prompt pour envoyer une commande */
358 /*
359 printf("attente prompt\n");
360 this->wait();
361 */
362 
363 #ifdef FINSERVO
364   /* envoie des commandes pour qu'en fin de mouvement servolens renvoie */
365   /* une commande de fin de mouvement (ex: ZF, FF, DF). */
366   this->enableCommandComplete();
367 #endif /* FINSERVO */
368 
369   // 08/08/00 Fabien S. - Correction de la consigne demandee
370   // pour prendre en compte l'erreur entre la consigne demandee
371   // et la consigne mesuree.
372   // A la consigne du zoom on retranche 1.
373   // A la consigne du focus on ajoute 1.
374   // A la consigne du iris on ajoute 1.
375   switch (servo) {
376   case ZOOM:
377     // printf("zoom demande: %d ", position);
378     position--;
379     if (position < ZOOM_MIN)
380       position = ZOOM_MIN;
381     // printf("zoom corrige: %d \n", position);
382     break;
383   case FOCUS:
384     // printf("focus demande: %d ", position);
385     position++;
386     if (position > FOCUS_MAX)
387       position = FOCUS_MAX;
388     // printf("focus corrige: %d \n", position);
389     break;
390   case IRIS:
391     // printf("iris demande: %d ", position);
392     position++;
393     if (position > IRIS_MAX)
394       position = IRIS_MAX;
395     // printf("iris corrige: %s \n", position);
396     break;
397   }
398 
399   /* commande a envoyer aux servomoteurs */
400   switch (servo) {
401   case ZOOM:
402     command << "ZD" << position;
403     break;
404   case FOCUS:
405     command << "FD" << position;
406     break;
407   case IRIS:
408     command << "DD" << position;
409     break;
410   }
411 /* envoie de la commande */
412 #ifdef PRINT
413   printf("\ncommand: %s", command.str());
414 #endif
415 
416   this->write(command.str().c_str());
417 
418 #ifdef FINSERVO
419   /* on attend la fin du mouvement des objectifs */
420   this->wait(servo); /* on attend les codes ZF, FF, DF */
421 #endif
422 }
423 
424 /*!
425   Get the Servolens current servo position.
426 
427   \param servo : Servolens servo motor to actuate.
428   \param position : Measured position of the servo.
429 
430   \exception vpRobotException::communicationError : If cannot dial
431   with Servolens.
432 
433 */
getPosition(vpServoType servo,unsigned int & position) const434 bool vpServolens::getPosition(vpServoType servo, unsigned int &position) const
435 {
436   if (!isinit) {
437     vpERROR_TRACE("Cannot dial with Servolens.");
438     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
439   }
440   char commande[10];
441   char posit[10], *pt_posit;
442   char c;
443   short fin_lect_posit;         /* indique si on a lu la position du servo-moteur */
444   short posit_car;              /* donne la position du caractere lu */
445   short lecture_posit_en_cours; /* indique si on lit la position courante */
446 
447   /* attente du prompt pour envoyer une commande */
448   /*
449   this->wait();
450   */
451   pt_posit = posit;
452 
453   /* envoie des commandes pour obtenir la position des servo-moteurs. */
454   switch (servo) {
455   case ZOOM:
456     sprintf(commande, "ZD?");
457     break;
458   case FOCUS:
459     sprintf(commande, "FD?");
460     break;
461   case IRIS:
462     sprintf(commande, "DD?");
463     break;
464   default:
465     break;
466   }
467   /* envoie de la commande */
468   //    printf("\ncommande: %s", commande);
469 
470   this->write(commande);
471 
472   /* on cherche a lire la position du servo-moteur */
473   /* Servolens renvoie une chaine de caractere du type ZD00400 ou FD00234 */
474   fin_lect_posit = 0;
475   posit_car = 0;
476   lecture_posit_en_cours = 0;
477   do {
478     if (this->read(&c, 1) == true) {
479 
480       //    printf("caractere lu: %c\n", c);
481       switch (posit_car) {
482       /* on lit le 1er caractere; (soit Z, soit F, soit D) */
483       case 0:
484         /* sauvegarde du pointeur */
485         pt_posit = posit;
486 
487         switch (servo) {
488         case ZOOM:
489           if (c == 'Z')
490             posit_car = 1;
491           break;
492         case FOCUS:
493           if (c == 'F')
494             posit_car = 1;
495           break;
496         case IRIS:
497           if (c == 'D')
498             posit_car = 1;
499           break;
500         }
501         break;
502 
503       /* si le 1er caractere est correct, on lit le 2eme caractere */
504       /* (toujours D) */
505       case 1:
506         if (c == 'D')
507           posit_car = 2;
508         else
509           posit_car = 0; /* le 2eme caractere n'est pas correct */
510         break;
511 
512       /* si on a lu les 2 premiers caracteres, on peut lire la */
513       /* position du servo-moteur */
514       case 2:
515         if (c >= '0' && c <= '9') {
516           *pt_posit++ = c; /* sauvegarde de la position */
517           lecture_posit_en_cours = 1;
518         } else if (lecture_posit_en_cours) {
519           fin_lect_posit = 1;
520           *pt_posit = '\0';
521         } else
522           posit_car = 0;
523         break;
524       }
525 
526     } else {
527       // Timout sur la lecture, on retoure FALSE
528       return false;
529     }
530   } while (!fin_lect_posit);
531 
532   //    printf("\nChaine lue: posit: %s", posit);
533 
534   /* toilettage de la position courantes lue */
535   this->clean(posit, posit);
536 
537   //    printf("\nChaine toilettee: posit: %s", posit);
538   position = (unsigned int)atoi(posit);
539 
540   return (true);
541 }
542 
543 /*!
544 
545   These parameters are computed from the Dragonfly2 DR2-COL camera sensor
546 pixel size (7.4 um) and from the servolens zoom position.
547 
548   \param I : An image coming from the Dragonfly2 camera attached to the
549   servolens.
550 
551 \code
552 #include <visp3/vs/vpServolens.h>
553 
554 int main()
555 {
556   // UNIX vpServolens servolens("/dev/ttyS0");
557 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
558 
559   vpImage<unsigned char> I(240, 320);
560   vpCameraParameters cam = servolens.getCameraParameters(I);
561   std::cout << "Camera parameters: " << cam << std::endl;
562 #endif
563   }
564 \endcode
565 
566   \exception vpRobotException::communicationError : If cannot dial with Servolens.
567 
568  */
getCameraParameters(vpImage<unsigned char> & I) const569 vpCameraParameters vpServolens::getCameraParameters(vpImage<unsigned char> &I) const
570 {
571   if (!isinit) {
572     vpERROR_TRACE("Cannot dial with Servolens.");
573     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
574   }
575   vpCameraParameters cam;
576   double pix_size = 7.4e-6; // Specific to the Dragonfly2 camera
577   double px = 1000, py = 1000, u0 = 320, v0 = 240;
578   // Determine if the image is subsampled.
579   // Dragonfly2 native images are 640 by 480
580   double subsample_factor = 1.;
581   double width = I.getWidth();
582   double height = I.getHeight();
583 
584   if (width > 300 && width < 340 && height > 220 && height < 260)
585     subsample_factor = 2;
586   else if (width > 140 && width < 1800 && height > 100 && height < 140)
587     subsample_factor = 4;
588 
589   unsigned zoom;
590   getPosition(vpServolens::ZOOM, zoom);
591   // std::cout << "Actual zoom value: " << zoom << std::endl;
592 
593   // XSIZE_PIX_CAM_AFMA4 / focale et YSIZE_PIX_CAM_AFMA4 / focale
594   // correspondent aux parametres de calibration de la camera (donnees
595   // constructeur) pour des tailles d'images CCIR (768x576), donc avec scale
596   // = 1.
597   double focale = zoom * 1.0e-5;                       // Transformation en metres
598   px = focale / (double)(subsample_factor * pix_size); // Taille des pixels en metres.
599   py = focale / (double)(subsample_factor * pix_size); // Taille des pixels en metres.
600   u0 = I.getWidth() / 2.;
601   v0 = I.getHeight() / 2.;
602   cam.initPersProjWithoutDistortion(px, py, u0, v0);
603 
604   return cam;
605 }
606 
607 /*!
608   Waits for the Servolens promt '>'.
609 
610   \return The prompt character.
611 
612   \exception vpRobotException::communicationError : If cannot dial
613   with Servolens.
614 */
wait() const615 char vpServolens::wait() const
616 {
617   if (!isinit) {
618     vpERROR_TRACE("Cannot dial with Servolens.");
619     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
620   }
621 
622   ssize_t r;
623   r = ::write(this->remfd, "\r\n", strlen("\r\n"));
624   if (r != (ssize_t)(strlen("\r\n"))) {
625     throw vpRobotException(vpRobotException::communicationError, "Cannot write on Servolens.");
626   }
627   char c;
628   do {
629     r = ::read(this->remfd, &c, 1);
630     c &= 0x7f;
631     if (r != 1) {
632       throw vpRobotException(vpRobotException::communicationError, "Cannot read on Servolens.");
633     }
634   } while (c != '>');
635   return c;
636 }
637 
638 /*!
639 
640   Waits the end of motion of the corresponding servo motor.
641 
642   \param servo : Servolens servo motor.
643 
644   \exception vpRobotException::communicationError : If cannot dial
645   with Servolens.
646 
647 */
wait(vpServoType servo) const648 void vpServolens::wait(vpServoType servo) const
649 {
650   if (!isinit) {
651     vpERROR_TRACE("Cannot dial with Servolens.");
652     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
653   }
654 
655   char c;
656   char fin_mvt[3];
657   bool sortie = false;
658 
659   switch (servo) {
660   case ZOOM:
661     sprintf(fin_mvt, "ZF");
662     break;
663   case FOCUS:
664     sprintf(fin_mvt, "FF");
665     break;
666   case IRIS:
667   default:
668     sprintf(fin_mvt, "DF");
669     break;
670   }
671 
672   /* lecture des caracteres recus */
673   do {
674     /* lecture des caracteres */
675     if (::read(this->remfd, &c, 1) != 1) {
676       throw vpRobotException(vpRobotException::communicationError, "Cannot read on Servolens.");
677     }
678     c &= 0x7f;
679 
680     /* tests si fin de mouvement */
681     if (c == fin_mvt[0]) {
682       /* lecture du caractere suivant */
683       if (::read(this->remfd, &c, 1) != 1) {
684         throw vpRobotException(vpRobotException::communicationError, "Cannot read on Servolens.");
685       }
686 
687       c &= 0x7f;
688       if (c == fin_mvt[1]) {
689         sortie = true;
690       }
691     }
692   } while (!sortie);
693 
694   /*  printf("\nmouvement fini: chaine lue = %s", chaine); */
695 }
696 
697 /*!
698   Read one character form the serial link.
699 
700   \param c : The character that was read.
701   \param timeout_s : Timeout in seconds.
702 
703   \return true if the reading was successfully achieved, false otherwise.
704 
705   \exception vpRobotException::communicationError : If cannot dial
706   with Servolens.
707 */
read(char * c,long timeout_s) const708 bool vpServolens::read(char *c, long timeout_s) const
709 {
710   if (!isinit) {
711     vpERROR_TRACE("Cannot dial with Servolens.");
712     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
713   }
714 
715   fd_set readfds;                          /* list of fds for select to listen to */
716   struct timeval timeout = {timeout_s, 0}; // seconde, micro-sec
717 
718   FD_ZERO(&readfds);
719   FD_SET(static_cast<unsigned int>(this->remfd), &readfds);
720 
721   if (select(FD_SETSIZE, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timeout) > 0) {
722     ssize_t n = ::read(this->remfd, c, 1); /* read one character at a time */
723     if (n != 1)
724       return false;
725     *c &= 0x7f;
726     // printf("lecture 1 car: %c\n", *c);
727     return (true);
728   }
729 
730   return (false);
731 }
732 
733 /*!
734   Write a string to the serial link.
735 
736   \param s : String to send to the Servolens.
737 
738   \exception vpRobotException::communicationError : If cannot dial
739   with Servolens.
740 */
write(const char * s) const741 void vpServolens::write(const char *s) const
742 {
743   if (!isinit) {
744     vpERROR_TRACE("Cannot dial with Servolens.");
745     throw vpRobotException(vpRobotException::communicationError, "Cannot dial with Servolens.");
746   }
747   ssize_t r = 0;
748   r = ::write(this->remfd, "\r", strlen("\r"));
749   r += ::write(this->remfd, s, strlen(s));
750   r += ::write(this->remfd, "\r", strlen("\r"));
751   if (r != (ssize_t)(2 * strlen("\r") + strlen(s))) {
752     throw vpRobotException(vpRobotException::communicationError, "Cannot write on Servolens.");
753   }
754 
755   /*
756    * Une petite tempo pour laisser le temps a la liaison serie de
757    * digerer la commande envoyee. En fait, la liaison serie fonctionne
758    * a 9600 bauds soit une transmission d'environ 9600 bits pas seconde.
759    * Les plus longues commandes envoyees sur la liaison serie sont du type:
760    * SX0842 soit 6 caracteres codes sur 8 bits chacuns = 48 bits pour
761    * envoyer la commande SX0842.
762    * Ainsi, le temps necessaire pour envoyer SX0842 est d'environ
763    * 48 / 9600 = 0,0050 secondes = 5 milli secondes.
764    * Ici on rajoute une marge pour amener la tempo a 20 ms.
765    */
766   vpTime::wait(20);
767 }
768 
769 /*!
770   Suppress all the zero characters on the left hand of \e in string.
771 
772   \param in : Input string.
773   \param out : Output string without zero characters on the left.
774 
775 */
clean(const char * in,char * out) const776 bool vpServolens::clean(const char *in, char *out) const
777 {
778   short nb_car, i = 0;
779   bool error = false;
780 
781   nb_car = strlen(in);
782 
783   /* on se positionne sur le 1er caractere different de zero */
784   while (*(in) == '0' && i++ < nb_car) {
785     in++;
786     if (i == nb_car) {
787       error = true;   /* la chaine ne contient pas une position */
788       *(out++) = '0'; /* mise a zero de la position */
789     }
790   }
791 
792   /* copie de la position epuree des zeros de gauche */
793   while (i++ <= nb_car) { /* on a mis le = pour copier le caractere de fin */
794     /* de chaine \0 */
795     *(out++) = *(in++);
796   }
797   return (error);
798 }
799 
800 #endif
801