Revision: 8019
          http://playerstage.svn.sourceforge.net/playerstage/?rev=8019&view=rev
Author:   gbiggs
Date:     2009-07-14 22:58:11 +0000 (Tue, 14 Jul 2009)

Log Message:
-----------
Applied patch #2821365

Modified Paths:
--------------
    code/player/trunk/server/drivers/position/roboteq/roboteq.cc

Modified: code/player/trunk/server/drivers/position/roboteq/roboteq.cc
===================================================================
--- code/player/trunk/server/drivers/position/roboteq/roboteq.cc        
2009-07-14 22:55:20 UTC (rev 8018)
+++ code/player/trunk/server/drivers/position/roboteq/roboteq.cc        
2009-07-14 22:58:11 UTC (rev 8019)
@@ -18,23 +18,17 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  */
+ 
+ // TODO: add config parameter to specify analog feedback.
 
 /** @ingroup drivers */
 /** @{ */
 /** @defgroup driver_roboteq roboteq
- * @brief Motor control driver for Roboteq AX2550
+ * @brief Motor control driver for Roboteq AX2850
+ 
+Provides position2d interface to the Roboteq AX2850 motor controller
+http://www.roboteq.com/ax2850-folder.html
 
-Provides position2d interface to the Roboteq AX2550 motor controller
-http://www.roboteq.com/ax2550-folder.html
-This driver ignores all configuration requests and produces no data
-although the hardware device supports a number of configurations
-and data over the serial link. It simply accepts 2 types of commmands
-translation and rotation, then converts these to the syntax used by
-the Roboteq. This driver uses the configuration file options
-max_rot_spd and max_trans_spd to scale commands sent to the controller.
-These values can be determined by testing with RC -- or closed loop
-could be implemented by integrating dead-reckoning devices.
-
 @par Compile-time dependencies
 
 - none
@@ -42,6 +36,7 @@
 @par Provides
 
 - @ref interface_position2d
+  @ref interface_power
 
 @par Requires
 
@@ -61,30 +56,59 @@
   - Default: 9600
   - The baud rate to be used.
 
-- max_rot_spd (float)
-  - Default: none
-  - maximum rotational speed (in rad/sec) that would be achieved
-       by vehicle if full power where applied to that channel.
+- encoder_ppr
+  - Default: 500
+  - The number of pulses per revolution for the encoders. Optional if no 
encoders are present.
+  
+- wheel_circumference
+  - Default: 1 meter
+  - The wheel circumference in meters. Optional if no encoders present. 
+  
+- axle_length
+  - Default: 1 meter
+  - The distance between the centers of the two wheels. Optional if no 
encoders present.
+  
+- gear_ratio
+  - Default: 1
+  - The gear ratio from the motor to the wheel (the number of motor 
revolutions per one revolution of the wheel). Optional if no encoders present.
+  
+- controller_current_limit
+  - Default: 105 amperes
+  - The maximum current to the motors. See the Roboteq manual.
+  
+- acceleration
+  - Default: 0x20
+  - The acceleration time constant. See the Roboteq manual.
+  
+- encoder_time_base
+  - Default: 0x16
+  - The encoder time base. Optional if no encoders are present. See the 
Roboteq manual.
+  
+- encoder_distance_divider
+  - Default: 0x08
+  - The encoder distance divider. Optional if no encoders are present. See the 
Roboteq manual.
+  
+- invert_directions
+  - Default: false (off)
+  - Invert the motor directions, useful if you find the motors turning in the 
opposite direction from what you intended...
+  
+- rc_mode_on_shutdown
+  - Default: true (on)
+  - Set the motor controller to RC mode when the driver shuts down.
 
-- max_trans_spd (float)
-  - Default: none
-  - maximum translational speed (in meters/sec) that would be achieved
-       by vehicle if full power where applied to that channel.
-
 @par Example
 
 @verbatim
 driver
 (
   name "roboteq"
-  provides ["position2d:0"]
+  provides ["position2d:0" "power:0"]
   devicepath "/dev/ttyS0"
-  max_trans_spd 6.0
-  max_rot_spd 4.0
 )
 @endverbatim
 
 @author Pablo Rivera [email protected]
+...@author Mike Roddewig [email protected]
 
 */
 /** @} */
@@ -95,25 +119,77 @@
 #include <termios.h>
 #include <sys/ioctl.h> // ioctl
 #include <unistd.h> // close(2),fcntl(2),getpid(2),usleep(3),execvp(3),fork(2)
-#include <netdb.h> // for gethostbyname(3)
-#include <netinet/in.h>  // for struct sockaddr_in, htons(3)
-#include <sys/types.h>  // for socket(2)
-#include <sys/socket.h>  // for socket(2)
-#include <signal.h>  // for kill(2)
-#include <fcntl.h>  // for fcntl(2)
-#include <string.h>  // for strncpy(3),memcpy(3)
-#include <stdlib.h>  // for atexit(3),atoi(3)
-#include <pthread.h>  // for pthread stuff
+#include <netdb.h> // for gethostbyname(3) 
+#include <netinet/in.h>  // for struct sockaddr_in, htons(3) 
+#include <sys/types.h>  // for socket(2) 
+#include <sys/socket.h>  // for socket(2) 
+#include <signal.h>  // for kill(2) 
+#include <fcntl.h>  // for fcntl(2) 
+#include <string.h>  // for strncpy(3),memcpy(3) 
+#include <stdlib.h>  // for atexit(3),atoi(3) 
+#include <pthread.h>  // for pthread stuff 
 #include <math.h>
 
 #include <libplayercore/playercore.h>
 
 // settings
-#define SERIAL_BUFF_SIZE               128
+#define SERIAL_BUFF_SIZE                       128
 #define MAX_MOTOR_SPEED                        127
-#define ROBOTEQ_CON_TIMEOUT            10      // seconds to time-out on 
setting RS-232 mode
-#define ROBOTEQ_DEFAULT_BAUD   9600
+#define ROBOTEQ_CON_TIMEOUT            10              // seconds to time-out 
on setting RS-232 mode
+#define ROBOTEQ_DEFAULT_BAUD           9600 
+#define INPUT_SWITCHES_FUNCTIONS       0x01    // sets the input switches to 
function as an e-stop.
 
+// Default parameter settings
+
+#define DEFAULT_CONTROLLER_CURRENT_LIMIT       105             // Amperes
+#define DEFAULT_ACCELERATION                           0x20    // About 1 
second from stop to full speed.
+#define DEFAULT_ENCODER_TIME_BASE                      0x16
+#define DEFAULT_ENCODER_DISTANCE_DIVIDER       0x08
+#define DEFAULT_GEAR_RATIO                                     1
+#define DEFAULT_WHEEL_CIRCUMFERENCE            1
+#define DEFAULT_AXLE_LENGTH                            1
+#define DEFAULT_ENCODER_PPR                            500
+#define DEFAULT_INVERT_DIRECTIONS                      false
+#define DEFAULT_RC_MODE_ON_SHUTDOWN            true
+
+// Parameter addresses
+
+#define CHANNEL1_OPERATING_MODE_ADDRESS        0x80
+#define CHANNEL2_OPERATING_MODE_ADDRESS        0x81
+#define CONTROLLER_IDENTIFICATION_ADDRESS      0x8A
+#define CONTROLLER_STATUS_ADDRESS                      0x89
+#define INPUT_CONTROL_MODE_ADDRESS             0x00
+#define MOTOR_CONTROL_MODE_ADDRESS             0x01
+#define CURRENT_LIMIT_ADDRESS                          0x02
+#define ACCELERATION_ADDRESS                           0x03
+#define INPUT_SWITCHES_FUNCTION_ADDRESS        0x04
+#define ENCODER1_TIME_BASE_ADDRESS             0xA2
+#define ENCODER2_TIME_BASE_ADDRESS             0xA3
+#define ENCODER_DISTANCE_DIVIDER_ADDRESS       0xA5
+#define EXPONENTIATION_CHANNEL1_ADDRESS        0x07
+#define EXPONENTIATION_CHANNEL2_ADDRESS        0x08
+#define PID_PROPORTIONAL_GAIN1_ADDRESS         0x82
+#define PID_PROPORTIONAL_GAIN2_ADDRESS         0x83
+#define PID_INTEGRAL_GAIN1_ADDRESS                     0x84
+#define PID_INTEGRAL_GAIN2_ADDRESS                     0x85
+#define PID_DIFFERENTIAL_GAIN1_ADDRESS         0x86
+#define PID_DIFFERENTIAL_GAIN2_ADDRESS         0x87
+
+// Constants
+
+#define MOTOR_CONTROL_MODE_CLOSED_LOOP 0xC5
+#define MOTOR_CONTROL_MODE_OPEN_LOOP           0x01
+#define MAX_PID_GAIN                                           63.0/8.0
+#define EXPONENTIATION_LINEAR                          0x00
+#define EXPONENTIATION_STRONG_EXP                      0x02
+#define INPUT_CONTROL_MODE                             0x01
+
+// Message levels
+
+#define MESSAGE_ERROR                                  0
+#define MESSAGE_INFO                                           1
+#define MESSAGE_DEBUG                                  2
+
 #ifndef CRTSCTS
 #ifdef IHFLOW
 #ifdef OHFLOW
@@ -140,321 +216,864 @@
 
 // *************************************
 
-#define FORWARD "!A"
-#define REVERSE "!a"
-#define LEFT "!B"
-#define RIGHT "!b"
-
 ///////////////////////////////////////////////////////////////////////////
 
-class roboteq:public ThreadedDriver
+class roboteq : public ThreadedDriver
 {
-  private:
-      int roboteq_fd;
-      char serialin_buff[SERIAL_BUFF_SIZE];
-      char serialout_buff[SERIAL_BUFF_SIZE];
-      const char* devicepath;
-      int roboteq_baud;
-      double speed_scaling_factor, rot_scaling_factor;
+       private:
+               int roboteq_fd;
+               char serialin_buff[SERIAL_BUFF_SIZE];
+               char serialout_buff[SERIAL_BUFF_SIZE];
+               const char* devicepath;
+               int roboteq_baud;
+               double speed_to_rpm;
+               double turning_circumference;
+               char encoder_present;
+               unsigned char motor_control_mode;
+               double max_forward_velocity;
+               double max_rotational_velocity;
+               bool motors_enabled;
+       
+               // Config parameters.
 
-      int FormMotorCmd(char* cmd_str, short trans_command, short rot_command);
+               int controller_current_limit;
+               unsigned char controller_current_limit_value;
+               int acceleration;
+               int encoder_time_base;
+               int encoder_distance_divider;
+               int encoder_ppr;
+               double wheel_circumference;
+               double axle_length;
+               double speed_per_tick;
+               double rad_per_tick;
+               double gear_ratio;
+               bool rc_mode_on_shutdown;
+               bool invert_directions;
+       
+               int checkConfigParameter (char, int, int);
+               void UpdatePowerData (player_power_data_t *);
+               void UpdatePositionData (player_position2d_data_t *);
+               int WriteMotorVelocity (double, double);
 
-      // current data
-      player_position2d_data_t data;
-      player_devaddr_t position2d_id;
+               player_position2d_data_t position_data;
+               player_power_data_t power_data;
+               player_devaddr_t position_addr;
+               player_devaddr_t power_addr;
+               player_pose2d current_position;
 
-  public:
-    roboteq( ConfigFile* cf, int section);
-
-    virtual int ProcessMessage(QueuePointer &resp_queue,
-                                               player_msghdr * hdr, void * 
data);
-    virtual int MainSetup();
-    virtual void MainQuit();
-    virtual void Main();
+       public:
+               roboteq(ConfigFile *, int);
+    
+               virtual int ProcessMessage(QueuePointer &, 
+                       player_msghdr *, void *);
+               virtual int MainSetup();
+               virtual void MainQuit();
+               virtual void Main();
 };
 
 
 ///////////////////////////////////////////////////////////////////////////
 // initialization function
-Driver* roboteq_Init( ConfigFile* cf, int section)
-{
-  return((Driver*)(new roboteq( cf, section)));
+Driver* roboteq_Init( ConfigFile* cf, int section) {
+       return((ThreadedDriver*)(new roboteq(cf, section)));
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
 // a driver registration function
-void roboteq_Register(DriverTable* table)
-{
-  table->AddDriver("roboteq",  roboteq_Init);
+void roboteq_Register(DriverTable* table) {
+       table->AddDriver("roboteq",  roboteq_Init);
 }
 
 ///////////////////////////////////////////////////////////////////////////
-roboteq::roboteq( ConfigFile* cf, int section) : ThreadedDriver(cf, section)
-{
-       memset (&this->position2d_id, 0, sizeof (player_devaddr_t));
-
-    // Outgoing position 2d interface
-       if(cf->ReadDeviceAddr(&(this->position2d_id), section, "provides",
-                      PLAYER_POSITION2D_CODE, -1, NULL) == 0){
-               if(this->AddInterface(this->position2d_id) != 0){
-                       this->SetError(-1);
+roboteq::roboteq( ConfigFile* cf, int section) : ThreadedDriver(cf, section) { 
 
+       
+       memset (&this->position_addr, 0, sizeof (player_devaddr_t));
+       memset (&this->power_addr, 0, sizeof (player_devaddr_t));
+       
+       // Check the config file to see if we are providing a position 
interface.
+       if (cf->ReadDeviceAddr(&(this->position_addr), section, "provides",
+               PLAYER_POSITION2D_CODE, -1, NULL) == 0) {
+               if (this->AddInterface(this->position_addr) != 0) {
+                       PLAYER_ERROR("Error adding position interface.");
+                       SetError(-1);
                        return;
-       }   }
+               }
+       }
+       
+       // Check the config file to see if we are providing a power interface.
+       if (cf->ReadDeviceAddr(&(this->power_addr), section, "provides",
+               PLAYER_POWER_CODE, -1, NULL) == 0) {
+               if (this->AddInterface(this->power_addr) != 0) {
+                       PLAYER_ERROR("Error adding power interface.");
+                       SetError(-1);
+                       return;
+               }
+       }
 
-    double max_trans_spd, max_rot_spd;
+       // Required parameter.
+       
+       if(!(this->devicepath = (char*)cf->ReadString(section, "devicepath", 
NULL))){
+               PLAYER_ERROR("ROBOTEQ: you must specify the serial port 
device.");
+               this->SetError(-1);
+               return;
+       }
+       
+       // Optional parameters.
+       
+       encoder_ppr = cf->ReadInt(section, "encoder_ppr", DEFAULT_ENCODER_PPR);
+       if (encoder_ppr < 0) {
+               PLAYER_ERROR("ROBOTEQ: 'encoder_ppr' must be positive.");
+               this->SetError(-1);
+               return;
+       }
 
-  // required parameter(s)
-  if(!(this->devicepath = (char*)cf->ReadString(section, "devicepath", NULL))){
-    PLAYER_ERROR("must specify devicepath");
-    this->SetError(-1);
-    return;
-  }
-  if(!(max_trans_spd = (double)cf->ReadFloat(section, "max_trans_spd", 0))){
-    PLAYER_ERROR("must specify maximum translational speed");
-    this->SetError(-1);
-    return;
-  }
-  if(!(max_rot_spd = (double)cf->ReadFloat(section, "max_rot_spd", 0))){
-    PLAYER_ERROR("must specify maximum rotational speed");
-    this->SetError(-1);
-    return;
-  }
-  fprintf(stderr, "Roboteq: using a max translational speed of %f m/s\n\tand 
max rotational speed of %f rad/s to scale motor commands\n", max_trans_spd, 
max_rot_spd);
+       wheel_circumference = cf->ReadFloat(section, "wheel_circumference", 
DEFAULT_WHEEL_CIRCUMFERENCE);
+       if (encoder_ppr < 0.0) {
+               PLAYER_ERROR("ROBOTEQ: 'wheel_circumference' must be 
positive.");
+               this->SetError(-1);
+               return;
+       }
+       
+       axle_length = cf->ReadFloat(section, "axle_length", 
DEFAULT_AXLE_LENGTH);
+       if (axle_length < 0.0) {
+               PLAYER_ERROR("ROBOTEQ: 'axle_length' must be positive.");
+               this->SetError(-1);
+               return;
+       }
+       
+       gear_ratio = cf->ReadFloat(section, "gear_ratio", DEFAULT_GEAR_RATIO);
+       if (gear_ratio < 0.0) {
+               PLAYER_ERROR("ROBOTEQ: 'gear_ratio' must be positive.");
+               this->SetError(-1);
+               return;
+       }
+       
+       controller_current_limit = cf->ReadInt(section, 
"controller_current_limit", DEFAULT_CONTROLLER_CURRENT_LIMIT);
+       
+       // Compute the value to send to the microcontroller.
+       if (controller_current_limit < 1) {
+               PLAYER_ERROR("ROBOTEQ: The current limit must be greater than 
0.");
+               this->SetError(-1);
+               return;
+       }
+       else if (controller_current_limit <= 30) {
+               controller_current_limit_value = 0;
+               controller_current_limit_value += (30 - 
controller_current_limit) << 4;
+       }
+       else if (controller_current_limit <= 45) {
+               controller_current_limit_value = 1;
+               controller_current_limit_value += (45 - 
controller_current_limit) << 4;
+       }
+       else if (controller_current_limit <= 60) {
+               controller_current_limit_value = 2;
+               controller_current_limit_value += (60 - 
controller_current_limit) << 4;
+       }
+       else if (controller_current_limit <= 75) {
+               controller_current_limit_value = 3;
+               controller_current_limit_value += (75 - 
controller_current_limit) << 4;
+       }
+       else if (controller_current_limit <= 90) {
+               controller_current_limit_value = 4;
+               controller_current_limit_value += (90 - 
controller_current_limit) << 4;
+       }
+       else if (controller_current_limit <= 105) {
+               controller_current_limit_value = 5;
+               controller_current_limit_value += (105 - 
controller_current_limit) << 4;
+       }
+       else if (controller_current_limit <= 120) {
+               controller_current_limit_value = 6;
+               controller_current_limit_value += (120 - 
controller_current_limit) << 4;
+       }
+       else {
+               PLAYER_ERROR("ROBOTEQ: The current limit value must be less 
than 120 A.");
+               this->SetError(-1);
+               return;
+       }
 
-  speed_scaling_factor = ((double)(MAX_MOTOR_SPEED / max_trans_spd));
-  rot_scaling_factor = ((double)(MAX_MOTOR_SPEED / max_rot_spd));
+       acceleration = cf->ReadInt(section, "acceleration", 
DEFAULT_ACCELERATION);
+       if (acceleration < 0 || acceleration > 53) {
+               PLAYER_ERROR("ROBOTEQ: 'acceleration' must be a value between 0 
and 53.");
+               this->SetError(-1);
+               return;
+       }
+       
+       encoder_time_base = cf->ReadInt(section, "encoder_time_base", 
DEFAULT_ENCODER_TIME_BASE);
+       if (encoder_time_base < 0 || encoder_time_base > 63) {
+               PLAYER_ERROR("ROBOTEQ: 'encoder_time_base' must be a value 
between 0 and 63.");
+               this->SetError(-1);
+               return;
+       }
+       
+       encoder_distance_divider = cf->ReadInt(section, 
"encoder_distance_divider", DEFAULT_ENCODER_DISTANCE_DIVIDER);
+       if (encoder_distance_divider < 0 || encoder_distance_divider > 63) {
+               PLAYER_ERROR("ROBOTEQ: 'encoder_distance_divider' must be a 
value between 0 and 63.");
+               this->SetError(-1);
+               return;
+       }
+       
+       invert_directions = cf->ReadBool(section, "invert_directions", 
DEFAULT_INVERT_DIRECTIONS);
 
-  // optional parameter(s)
-  roboteq_baud = cf->ReadInt(section, "baud", ROBOTEQ_DEFAULT_BAUD);
+       roboteq_baud = cf->ReadInt(section, "baud", ROBOTEQ_DEFAULT_BAUD);
+       
+       rc_mode_on_shutdown = cf->ReadBool(section, "rc_mode_on_shutdown", 
DEFAULT_RC_MODE_ON_SHUTDOWN);
 
-  memset(&data,0,sizeof(data));
-  roboteq_fd = -1;
+       memset(&position_data, 0, sizeof(position_data));
+       memset(&power_data, 0, sizeof(power_data));
+       roboteq_fd = -1;
 
-  return;
-}
+       PLAYER_MSG1(MESSAGE_INFO, "Configuring Roboteq serial port at %s", 
devicepath);
+       
+       roboteq_fd = open(devicepath, O_RDWR|O_NDELAY);
+       if (roboteq_fd == -1){
+               PLAYER_ERROR("ROBOTEQ: Unable to configure serial port!");
+               this->SetError(-1);
+               return; 
+       }
+       else{
+               struct termios options;
+      
+               tcgetattr(roboteq_fd, &options);
 
-///////////////////////////////////////////////////////////////////////////
-int
-roboteq::MainSetup()
-{
-  int ret, i;
+               // default is 9600 unless otherwise specified
 
-  fprintf(stderr, "Configuring Roboteq serial port at %s..\n", devicepath);
-  roboteq_fd = open(devicepath, O_RDWR|O_NDELAY);
-  if (roboteq_fd == -1){
-    fputs("Unable to configure serial port for RoboteQ!", stderr);
-    return 0;
-  }else{
-      struct termios options;
+               if (roboteq_baud == 4800) {
+                       cfsetispeed(&options, B4800);
+                       cfsetospeed(&options, B4800);
+               }
+               else if (roboteq_baud == 19200) {
+                       cfsetispeed(&options, B19200);
+                       cfsetospeed(&options, B19200);
+               }
+               else if (roboteq_baud == 38400) {
+                       cfsetispeed(&options, B38400);
+                       cfsetospeed(&options, B38400);
+               }
+               else {
+                       cfsetispeed(&options, B9600);
+                       cfsetospeed(&options, B9600);
+               }
 
-      tcgetattr(roboteq_fd, &options);
+               // set to 7bit even parity, no flow control
+               options.c_cflag |= (CLOCAL | CREAD);
+               options.c_cflag |= PARENB;
+               options.c_cflag &= ~PARODD;
+               options.c_cflag &= ~CSTOPB;
+               options.c_cflag &= ~CSIZE;
+               options.c_cflag |= CS7;
+               options.c_cflag &= ~CRTSCTS;
 
-      // default is 9600 unless otherwise specified
+               options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    // 
non-canonical
 
-      if (roboteq_baud == 4800){
-        cfsetispeed(&options, B4800);
-        cfsetospeed(&options, B4800);
-      }
-      else if (roboteq_baud == 19200){
-        cfsetispeed(&options, B19200);
-        cfsetospeed(&options, B19200);
-      }
-      else if (roboteq_baud == 38400){
-        cfsetispeed(&options, B38400);
-        cfsetospeed(&options, B38400);
-      }
-      else{
-        cfsetispeed(&options, B9600);
-        cfsetospeed(&options, B9600);
-      }
+               tcsetattr(roboteq_fd, TCSANOW, &options);
+               ioctl(roboteq_fd, TCIOFLUSH, 2);
+               tcflush(roboteq_fd, TCIFLUSH);
+       }
+       
+       // Compute the encoder speed to RPM conversion factor.
+       speed_to_rpm = (60.0 * 1000000.0) / (((double) encoder_ppr) * 4.0 * 
256.0 * (((double) encoder_time_base) + 1.0));
+       
+       // Compute the speed value to m/s conversion factor.
+       
+       speed_per_tick = (speed_to_rpm * wheel_circumference) / (gear_ratio * 
60);
+       
+       // Compute the turning circumference.
+       turning_circumference = 2.0 * M_PI * axle_length;
+       
+       // Compute the speed value to rad/s conversion factor.
+       
+       rad_per_tick = (2.0 * M_PI * speed_per_tick) / turning_circumference;
+       
+       max_forward_velocity = speed_per_tick * 127;
+       max_rotational_velocity = rad_per_tick * 127;
+       
+       PLAYER_MSG1(MESSAGE_INFO, "Computed maximum forward velocity of %f 
m/s.", max_forward_velocity);
+       PLAYER_MSG1(MESSAGE_INFO, "Computed maximum rotational velocity of %f 
rad/s.", max_rotational_velocity);
+       
+       // Motors are disabled on startup.
+       motors_enabled = false;
+       
+       // Enable new motor commands to overwrite old ones if they have not yet 
been processed.
+       this->InQueue->AddReplaceRule(-1, -1, -1, -1, PLAYER_MSGTYPE_CMD, 
PLAYER_POSITION2D_CMD_VEL, true);
+       
+       // Set the driver to always on so only one existence exists (is this 
the best way to do that?).
+       this->alwayson = true;
+       
+       PLAYER_MSG0(MESSAGE_DEBUG, "Done.");
+       
+       return;
+}
 
-      // set to 7bit even parity, no flow control
-      options.c_cflag |= (CLOCAL | CREAD);
-      options.c_cflag |= PARENB;
-      options.c_cflag &= ~PARODD;
-      options.c_cflag &= ~CSTOPB;
-      options.c_cflag &= ~CSIZE;
-      options.c_cflag |= CS7;
-      options.c_cflag &= ~CRTSCTS;
+///////////////////////////////////////////////////////////////////////////
+int roboteq::MainSetup() {
+       int returned_value;
+       int ret;
+       int i;
+       
+       // initialize RoboteQ to RS-232 mode
+       strcpy(serialout_buff, "\r");
+       for (i=0; i<10; i++) { 
+               write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+               tcdrain(roboteq_fd);
+               usleep(25000);
+       }
+       
+       // Disable the watchdog timer.
+       strcpy(serialout_buff, "^00 01\r");
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+       
+       strcpy(serialout_buff, "%rrrrrr\r");
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       sleep(2); // Sleep for two seconds to give the controller sufficient 
time to reboot.
+       
+       tcflush(roboteq_fd, TCIFLUSH); // Clear all the reboot crap out of the 
input buffer.
+       
+       // Read controller model to make sure the serial link is ok.
+       strcpy(serialout_buff, "^8A\r");
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+       
+       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00; // Null terminate our buffer 
to make sure sscanf doesn't run amok.
+       
+       ret = sscanf(serialin_buff, "%*3c %2X", &returned_value);
+       if (ret != 1) {
+               PLAYER_ERROR("ROBOTEQ: Unable to communicate with the 
controller! Check the serial device.");
+               this->SetError(-1);
+               return -1;
+       }
+       else {
+               if (returned_value & 0x01) {
+                       PLAYER_MSG0(MESSAGE_INFO, "AX500 found.");
+               }
+               else if (returned_value & 0x02) {
+                       PLAYER_MSG0(MESSAGE_INFO, "AX1500 found.");
+               }
+               else if (returned_value & 0x04) {
+                       PLAYER_MSG0(MESSAGE_INFO, "AX2500 found.");
+               }
+               else if (returned_value & 0x08) {
+                       PLAYER_MSG0(MESSAGE_INFO, "AX3500 found.");
+               }
+               else {
+                       PLAYER_WARN("Unknown controller found."); // 
Weird...this shouldn't happen.
+               }
+               
+               if (returned_value & 0x20) {
+                       PLAYER_MSG0(MESSAGE_INFO, "Encoder present.");
+                       encoder_present = 1;
+               }
+               else {
+                       encoder_present = 0;
+               }
+               
+               if (returned_value & 0x40) {
+                       PLAYER_MSG0(MESSAGE_INFO, "Short circuit detection 
capable.");
+               }
+       }
+       
+       if (encoder_present == 1) {
+               motor_control_mode = MOTOR_CONTROL_MODE_CLOSED_LOOP;
+       }
+       else {
+               motor_control_mode = MOTOR_CONTROL_MODE_OPEN_LOOP;
+       }
+       
+       // Set configuration parameters
 
-      options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    // non-canonical
+       if (this->checkConfigParameter('^', MOTOR_CONTROL_MODE_ADDRESS, 
motor_control_mode) != 0) {
+               PLAYER_ERROR("ROBOTEQ: Error setting motor control mode.");
+               this->SetError(-1);
+               return -1;
+       }
+       if (this->checkConfigParameter('^', CURRENT_LIMIT_ADDRESS, 
controller_current_limit_value) != 0) {
+               PLAYER_ERROR("ROBOTEQ: Error setting controller current 
limit.");
+               this->SetError(-1);
+               return -1;
+       }
+       if (this->checkConfigParameter('^', ACCELERATION_ADDRESS, acceleration) 
!= 0) {
+               PLAYER_ERROR("ROBOTEQ: Error setting acceleration profile.");
+               this->SetError(-1);
+               return -1;
+       }
+       if (this->checkConfigParameter('^', EXPONENTIATION_CHANNEL1_ADDRESS, 
EXPONENTIATION_LINEAR) != 0) {
+               PLAYER_ERROR("ROBOTEQ: Error setting channel 1 exponentiation 
to linear.");
+               this->SetError(-1);
+               return -1;
+       }
+       if (this->checkConfigParameter('^', EXPONENTIATION_CHANNEL2_ADDRESS, 
EXPONENTIATION_LINEAR) != 0) {
+               PLAYER_ERROR("ROBOTEQ: Error setting channel 2 exponentiation 
to linear.");
+               this->SetError(-1);
+               return -1;
+       }
+       if (encoder_present != 0) {
+               // Set encoder parameters.
+               
+               if (this->checkConfigParameter('*', ENCODER1_TIME_BASE_ADDRESS, 
encoder_time_base) != 0) {
+                       PLAYER_ERROR("ROBOTEQ: Error setting encoder one time 
base.");
+                       this->SetError(-1);
+                       return -1;
+               }
+               if (this->checkConfigParameter('*', ENCODER2_TIME_BASE_ADDRESS, 
encoder_time_base) != 0) {
+                       PLAYER_ERROR("ROBOTEQ: Error setting encoder two time 
base.");
+                       this->SetError(-1);
+                       return -1;
+               }
+               if (this->checkConfigParameter('*', 
ENCODER_DISTANCE_DIVIDER_ADDRESS, encoder_distance_divider) != 0) {
+                       PLAYER_ERROR("ROBOTEQ: Error setting encoder distance 
divider.");
+                       this->SetError(-1);
+                       return -1;
+               }
+       }
+       // Reboot the controller.
+       strcpy(serialout_buff, "%rrrrrr\r");
+        write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+        tcdrain(roboteq_fd);
+        sleep(2); // Sleep for two seconds to give the controller sufficient 
time to reboot.
 
-      tcsetattr(roboteq_fd, TCSANOW, &options);
-      ioctl(roboteq_fd, TCIOFLUSH, 2);
-  }
+        tcflush(roboteq_fd, TCIFLUSH); // Clear all the reboot crap out of the 
input buffer.
 
+       return 0;
+}
 
-  // initialize RoboteQ to RS-232 mode
-  strcpy(serialout_buff, "\r");
-  for (i=0; i<10; i++){
-    write(roboteq_fd, serialout_buff, 1);
-    tcdrain(roboteq_fd);
+// Check the value of the configuration parameter located at the given 
address, 
+// set it to the value if it does not equal the provided value.
+int roboteq::checkConfigParameter(char prefix, int address, int value) {
+       unsigned int returned_value;
+       char command_status;
+       int ret;
+
+
+       sprintf(serialout_buff, "%c%.2X\r", prefix, address);
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
        usleep(25000);
-  }
-
-    // check response from RoboteQ
+       
        memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
-       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE);
-       int beg_time = time(NULL);
-       bool mode_changed = true;
-       while (! strchr(serialin_buff, 'W')){
-               if ((time(NULL) - beg_time)>ROBOTEQ_CON_TIMEOUT){
-                        mode_changed = false;
-                        break;
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00; // Null terminate our buffer 
to make sure sscanf doesn't run amok.
+       
+       ret = sscanf(serialin_buff, "%*3c %2X", &returned_value);
+       if (ret != 1) {
+               returned_value = value + 1;
+               // Yes, it's hackish. BUT, for some reason if you try and read 
a value that has not been set instead of returning
+               // say -1 the Roboteq controller will return an error which 
will screw this function up. But, once you write to
+               // the address everything is just peachy. So, we basically 
ensure that we write to the address. And we do look 
+               // for an error further down the line.
+       }
+       
+       if ((char) returned_value != value) {
+               sprintf(serialout_buff, "%c%.2X %.2X\r", prefix, address, 
value);
+               write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+               tcdrain(roboteq_fd);
+               usleep(25000);
+               
+               // Check that the command was received ok.
+               
+               memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+               ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+               serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00; // Null terminate our 
buffer to make sure sscanf doesn't run amok.
+               ret = sscanf(serialin_buff, "%*6c %1c", &command_status);
+               if (ret != 1 || command_status != '+') {
+                       return -2; // An error occured writing the command.
                }
-               memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
-               ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE);
        }
-       if (!mode_changed)
-               fputs("Failed to set Roboteq to RS-232 mode!\n", stderr);
-       else
-               fputs("Successfully initialized Roboteq connection.\n", stderr);
-
-       fputs("Done.\n", stderr);
-
-  return(0);
+       return 0;
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
-void roboteq::MainQuit()
-{
-  int ret;
-  // return RoboteQ to RC mode
-  strcpy(serialout_buff, "^00 00\r");
-  write(roboteq_fd, serialout_buff, 7);
-  tcdrain(roboteq_fd);
-  usleep(25000);
-  strcpy(serialout_buff, "%rrrrrr\r");
-  write(roboteq_fd, serialout_buff, 8);
-  tcdrain(roboteq_fd);
-  usleep(25000);
-
-    // check response from RoboteQ
-       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
-       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE);
-       int beg_time = time(NULL);
-       while (! strchr(serialin_buff, 'W')){
-               if ((time(NULL) - beg_time)>ROBOTEQ_CON_TIMEOUT){
-                       // no 'W's for ROBOTEQ_CON_TIMEOUT seconds
-                       //              means we're probably in RC mode again
-
-                       // 07-09-07
-                       // this test may need to change since the reset
-                       // appears to fail quite often. is it really
-                       // failing or is the test bad?
-                       return;
+void roboteq::MainQuit() {
+       // Shut off the motors.
+       
+       WriteMotorVelocity(0.0, 0.0);
+       
+       if (rc_mode_on_shutdown == true) {
+               // Reset the input exponentiation mode to strong exponential.
+       
+               if (this->checkConfigParameter('^', 
EXPONENTIATION_CHANNEL1_ADDRESS, EXPONENTIATION_STRONG_EXP) != 0) {
+                       PLAYER_WARN("ROBOTEQ: Error setting channel 1 
exponentiation to strong exponential");
                }
-               memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
-               ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE);
+               if (this->checkConfigParameter('^', 
EXPONENTIATION_CHANNEL2_ADDRESS, EXPONENTIATION_STRONG_EXP) != 0) {
+                       PLAYER_WARN("ROBOTEQ: Error setting channel 2 
exponentiation to strong exponential.");
+               }
+               strcpy(serialout_buff, "^00 00\r");
+               write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+               tcdrain(roboteq_fd);
+               usleep(25000);
+               strcpy(serialout_buff, "%rrrrrr\r");
+               write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+               tcdrain(roboteq_fd);
+               usleep(25000);
        }
-       fputs("Unable to reset Roboteq to RC mode!", stderr);
 
-    close(roboteq_fd);
+       close(roboteq_fd);
+  
+       return;
 }
 
 
////////////////////////////////////////////////////////////////////////////////
 // Process an incoming message
 
////////////////////////////////////////////////////////////////////////////////
-int roboteq::ProcessMessage (QueuePointer &resp_queue, player_msghdr * hdr, 
void * data)
-{
-    assert(hdr);
-/*
-    fprintf(stderr, "ProcessMessage: type=%d subtype=%d\n",
-            hdr->type, hdr->subtype);
-  */
-       if (Message::MatchMessage(hdr, PLAYER_POSITION2D_REQ_MOTOR_POWER,
-                                PLAYER_POSITION2D_CMD_VEL, position2d_id)){
-        assert(hdr->size == sizeof(player_position2d_cmd_vel_t));
+int roboteq::ProcessMessage (QueuePointer &resp_queue, player_msghdr * hdr, 
void * data) {
+       
+       if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_CMD, 
PLAYER_POSITION2D_CMD_VEL, position_addr)) {
 
-        player_position2d_cmd_vel_t & command
-            = *reinterpret_cast<player_position2d_cmd_vel_t *> (data);
+               assert(hdr->size == sizeof(player_position2d_cmd_vel_t));
 
-        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-        // convert from the generic position interface
-        // to the Roboteq-specific command
-        // assumes "Mixed Mode" --
-        // channel 1 : FW/BW speed
-        // channel 2 : rotation
-        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-        float vel_xtrans = command.vel.px;
-        //float vel_ytrans = command.vel.py;
-        float vel_yawspd = command.vel.pa;
+               player_position2d_cmd_vel_t & command 
+                       = *reinterpret_cast<player_position2d_cmd_vel_t *> 
(data);
+               
+               WriteMotorVelocity(command.vel.px, command.vel.pa);
+       }
+       else if (Message::MatchMessage(hdr , PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION2D_REQ_SET_ODOM, position_addr)) {
+               assert(hdr->size == sizeof(player_position2d_set_odom_req_t));
+               
+               player_position2d_set_odom_req_t & odom_req = * 
reinterpret_cast<player_position2d_set_odom_req_t *> (data);
+               
+               current_position.px = odom_req.pose.px;
+               current_position.py = odom_req.pose.py;
+               current_position.pa = odom_req.pose.pa;
+               
+               this->Publish(position_addr, resp_queue, 
PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_SET_ODOM);
+       }
+       else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION2D_REQ_RESET_ODOM, position_addr)) {             
+               current_position.px = 0.0;
+               current_position.py = 0.0;
+               current_position.pa = 0.0;
+               
+               this->Publish(position_addr, resp_queue, 
PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_RESET_ODOM);
+       }
+       else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION2D_REQ_MOTOR_POWER)) {
+               assert(hdr->size == sizeof(player_position2d_power_config_t));
+               
+               player_position2d_power_config_t * power_config = 
reinterpret_cast<player_position2d_power_config_t *> (data);
+               motors_enabled = (bool) power_config->state;
+               
+               this->Publish(position_addr, resp_queue, 
PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_MOTOR_POWER);
+       }
+       else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION2D_REQ_SPEED_PID)) {
+               assert(hdr->size == sizeof(player_position2d_speed_pid_req_t));
+               
+               player_position2d_speed_pid_req_t * pid_req = 
reinterpret_cast<player_position2d_speed_pid_req_t *> (data);
+               
+               // Check the gain values we received.
+               
+               if ((pid_req->kp < 0 || pid_req->kp > MAX_PID_GAIN) || 
(pid_req->ki < 0 || pid_req->ki > MAX_PID_GAIN) || 
+                       (pid_req->kd < 0 || pid_req->kd > MAX_PID_GAIN)) {
+                       PLAYER_WARN("ROBOTEQ: Invalid PID gain parameter(s).");
 
-        //fprintf(stderr, "ProcessMessage: trans=%f, steer=%f\n", vel_trans, 
vel_turret);
+                       this->Publish(position_addr, resp_queue, 
PLAYER_MSGTYPE_RESP_NACK, PLAYER_POSITION2D_REQ_SPEED_PID);
+               }
+               else {
+               
+                       if (this->checkConfigParameter('^', 
PID_PROPORTIONAL_GAIN1_ADDRESS, (unsigned char) (pid_req->kp * 8)) != 0) {
+                               PLAYER_WARN("ROBOTEQ: Error setting channel one 
proportional gain.");
+                       }
+                       if (this->checkConfigParameter('^', 
PID_PROPORTIONAL_GAIN2_ADDRESS, (unsigned char) (pid_req->kp * 8)) != 0) {
+                               PLAYER_WARN("ROBOTEQ: Error setting channel two 
proportional gain.");
+                       }
+                       if (this->checkConfigParameter('^', 
PID_INTEGRAL_GAIN1_ADDRESS,(unsigned char) (pid_req->ki * 8)) != 0) {
+                               PLAYER_WARN("ROBOTEQ: Error setting channel one 
integral gain.");
+                       }
+                       if (this->checkConfigParameter('^', 
PID_INTEGRAL_GAIN2_ADDRESS, (unsigned char) (pid_req->ki * 8)) != 0) {
+                               PLAYER_WARN("ROBOTEQ: Error setting channel two 
integral gain.");
+                       }
+                       if (this->checkConfigParameter('^', 
PID_DIFFERENTIAL_GAIN1_ADDRESS, (unsigned char) (pid_req->kd * 8)) != 0) {
+                               PLAYER_WARN("ROBOTEQ: Error setting channel one 
differential gain.");
+                       }
+                       if (this->checkConfigParameter('^', 
PID_DIFFERENTIAL_GAIN2_ADDRESS, (unsigned char) (pid_req->kd * 8)) != 0) {
+                               PLAYER_WARN("ROBOTEQ: Error setting channel two 
differential gain.");
+                       }
+                       
+                       this->Publish(position_addr, resp_queue, 
PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_SPEED_PID);
+               }
+       }
+       else {
+               this->Publish(position_addr, resp_queue, 
PLAYER_MSGTYPE_RESP_NACK, hdr->subtype);
+       }
+       
+       return 0;
+}
 
-        // scale and translate to Roboteq command
-        vel_xtrans = (double)vel_xtrans * speed_scaling_factor;
-        vel_yawspd = (double)vel_yawspd * rot_scaling_factor;
-        FormMotorCmd(serialout_buff, (short)vel_xtrans, (short)vel_yawspd);
+void roboteq::UpdatePowerData(player_power_data_t * power_data) {
+       int ret;
+       int current_1, current_2, voltage;
+       
+       power_data->valid &= (PLAYER_POWER_MASK_VOLTS | 
PLAYER_POWER_MASK_WATTS); // We only return the voltage and power (in watts).
+       
+       // Request the battery voltage.
+       strcpy(serialout_buff, "?E\r");
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+       
+       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00; // Null terminate our buffer 
to make sure sscanf doesn't run amok.
+       
+       ret = sscanf(serialin_buff, "%*2c %2X", &voltage);
+       if (ret == 1) {
+               power_data->volts = ((double) voltage) * (55.0/256.0);
+       }
+       
+       // Request the current flowing to each channel. We sum the result to 
compute the power.
+       strcpy(serialout_buff, "?A\r");
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+       
+       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00; // Null terminate our buffer 
to make sure sscanf doesn't run amok.
 
-        // write motor cmd
-        write(roboteq_fd, serialout_buff, strlen(serialout_buff)+1);
-        tcdrain(roboteq_fd);
-               /*
-        char* temp;
-        while (temp = strchr(serialout_buff, '\r')) *temp = 32;
-        puts(serialout_buff);
-        fflush(stdout);
-        */
-        return 0;
-    }
-
-  return -1;
+       ret = sscanf(serialin_buff, "%*2c %2X %2X", &current_1, &current_2);
+       if (ret == 2) {
+               power_data->watts = power_data->volts * (current_1 + current_2);
+       }
 }
 
-///////////////////////////////////////////////////////////////////////////
-// Main driver thread runs here.
-///////////////////////////////////////////////////////////////////////////
-void roboteq::Main()
-{
-  double position_time=0;
+int roboteq::WriteMotorVelocity(double forward_velocity, double 
rotational_velocity) {
+       unsigned char forward_value, rotational_value;
+       char returned_value;
+       int ret;
+       
+       // We hard limit the input velocities.
+       if (fabs(forward_velocity) > max_forward_velocity) {
+               forward_value = 0x7F;
+       }
+       else {
+               forward_value = (unsigned char) (fabs(forward_velocity) / 
speed_per_tick);
+       }
+       
+       if (fabs(rotational_velocity) > max_rotational_velocity) {
+               rotational_value = 0x7F;
+       }
+       else {
+               rotational_value = (unsigned char) (fabs(rotational_velocity) / 
rad_per_tick);
+       }
+       
+       // Software enable/disable.
+       
+       if (motors_enabled == false) {
+               forward_value = 0;
+               rotational_value = 0;
+               
+               // Give the user an informational message to hopefully save 
them hours of torment. >:D
+               PLAYER_MSG0(MESSAGE_INFO, "Warning, the motors are disabled! 
Enable them before use.");
+       }
+       
+       // Check if we need to invert the velocities.
+       
+       if (invert_directions == true) {
+               forward_velocity = -forward_velocity;
+               rotational_velocity = -rotational_velocity;
+       }
 
-  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
+       // Write forward velocity and check result.
+       
+       if (forward_velocity < 0) {
+               sprintf(serialout_buff, "!b%.2X\r", forward_value);
+       } 
+       else {
+               sprintf(serialout_buff, "!B%.2X\r", forward_value);
+       }
+       
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+               
+       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00; // Null terminate our buffer 
to make sure sscanf doesn't run amok.
 
-  for(;;){
-    ProcessMessages();
-       pthread_testcancel();
-    // publish dummy data
-    Publish(device_addr, PLAYER_MSGTYPE_DATA, PLAYER_POSITION2D_DATA_STATE,
-                         (unsigned char*) &data, sizeof(data), &position_time);
-    usleep(10);
-  }
+       ret = sscanf(serialin_buff, "%*4c %1c", &returned_value);
+               
+       if (returned_value != '+') {
+               // Some kind of error happened.
+               
+               PLAYER_WARN("ROBOTEQ: Error writing forward velocity command.");
+               return -1;
+       }
+       
+       // Write rotational velocity and check result.
+       
+       if (rotational_velocity < 0) {
+               sprintf(serialout_buff, "!a%.2X\r", rotational_value);
+       } 
+       else {
+               sprintf(serialout_buff, "!A%.2X\r", rotational_value);
+       }
+       
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+               
+       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00; // Null terminate our buffer 
to make sure sscanf doesn't run amok.
 
-  pthread_exit(NULL);
+       ret = sscanf(serialin_buff, "%*4c %1c", &returned_value);
+               
+       if (returned_value != '+') {
+               // Some kind of error happened.
+               
+               PLAYER_WARN("ROBOTEQ: Error writing rotational velocity 
command.");
+               return -1;
+       }
+       
+       return 0;
+}
 
-  return;
+void roboteq::UpdatePositionData(player_position2d_data_t * position_data) {
+       int ret;
+       int speed1_value, speed2_value;
+       int encoder1_count, encoder2_count;
+       double rpm1, rpm2;
+       double speed1, speed2, speed_diff;
+       double distance1, distance2, distance_diff;
+       
+       // Ok, the math for this section is a bit tricky. Wheel 1 is assumed to 
be on the right
+       // and a positive rotational velocity indicates the robot is turning 
left. To determine 
+       // the rotational velocity we compute the difference in wheel 
velocities and then 
+       // compute the rotational velocity by converting m/s to rad/s on a 
circle of radius 
+       // axle length.
+       
+       // Read in the current speed value.
+       strcpy(serialout_buff, "?Z\r");
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+       
+       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00;
+       ret = sscanf(serialin_buff, "%*2c %2X %2X", &speed1_value, 
&speed2_value);
+       if (ret != 2) {
+               return; // Well, best not to update the data without any data...
+       }
+       
+       // Compute the speed (in m/s).
+       speed1 = ((double) speed1_value) * speed_per_tick;
+       speed2 = ((double) speed2_value) * speed_per_tick;
+       
+       if (invert_directions == true) {
+               speed1 = -speed1;
+               speed2 = -speed2;
+       }
+       
+       speed_diff = speed1 - speed2;
+       
+       position_data->vel.pa = (speed_diff / turning_circumference) * (2.0 * 
M_PI);
+       position_data->vel.px = 0.0;
+       
+       if (fabs(speed1) > fabs(speed2)) {
+               position_data->vel.py = speed2;
+       }
+       else {
+               position_data->vel.py = speed1;
+       }
+       
+       // Compute a new position. We don't use the speed data.
+
+       // Get the encoder counters. We use the relative mode.
+       strcpy(serialout_buff, "?Q4\r");
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+       
+       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00;
+       ret = sscanf(serialin_buff, "%*3c %X", &encoder1_count);
+       if (ret != 1) {
+               return;
+       }
+       
+       strcpy(serialout_buff, "?Q5\r");
+       write(roboteq_fd, serialout_buff, strlen(serialout_buff));
+       tcdrain(roboteq_fd);
+       usleep(25000);
+       
+       memset(serialin_buff, 0, SERIAL_BUFF_SIZE);
+       ret = read(roboteq_fd, serialin_buff, SERIAL_BUFF_SIZE-1);
+       serialin_buff[SERIAL_BUFF_SIZE-1] = 0x00;
+       ret = sscanf(serialin_buff, "%*3c %X", &encoder2_count);
+       if (ret != 1) {
+               return;
+       }
+       
+       // NOTE!!! There could be a bug here. If the driver is pre-empted or 
the delay
+       // between reading encoder one and two is too long then there will be 
an error
+       // in the computed position. It seems odd that Roboteq wouldn't provide 
a
+       // command that would read out the values in both encoders 
simultaneously
+       // but they don't. Solution, anyone?
+       
+       rpm1 = (((double) encoder1_count) / ((double) encoder_ppr)) / 
gear_ratio;
+       rpm2 = (((double) encoder2_count) / ((double) encoder_ppr)) / 
gear_ratio;
+       
+       distance1 = rpm1 * wheel_circumference;
+       distance2 = rpm2 * wheel_circumference;
+       
+       if (invert_directions == true) {
+               distance1 = -distance1;
+               distance2 = -distance2;
+       }
+       
+       distance_diff = distance1 - distance2;
+       
+       // We make an approximation here. Technically, if the speed in the 
wheels was different
+       // then the robot was turning in a circle. Which means I would have to 
try and calculate
+       // the arc but I really don't want to so I assume the motion is linear. 
If the position is 
+       // updated often enough this really shouldn't matter, but then again, 
why are you 
+       // relying on this data to be accurate? I would also need an accurate 
time since the last
+       // measurement which is rather difficult to obtain accurately 
(preemption, anyone?).
+       
+       if (fabs(distance1) > fabs(distance2)) {
+               current_position.px += cos(current_position.pa) * distance2;
+               current_position.py += sin(current_position.pa) * distance2;
+       }
+       else {
+               current_position.px += cos(current_position.pa) * distance1;
+               current_position.py += sin(current_position.pa) * distance1;
+       }
+       
+       current_position.pa += (distance_diff / turning_circumference) * (2.0 * 
M_PI);
+       current_position.pa = fmod(current_position.pa, (2.0 * M_PI)); // 
Constrain the angle to be within 2*pi.
+       
+       position_data->pos = current_position;
 }
 
-
 ///////////////////////////////////////////////////////////////////////////
-int roboteq::FormMotorCmd(char* cmd_str, short trans_command, short 
rot_command)
-{
-  char speed[8];
-  char heading[8];
-
-  if (trans_command > MAX_MOTOR_SPEED) trans_command = MAX_MOTOR_SPEED;
-  else if (trans_command < -MAX_MOTOR_SPEED) trans_command = -MAX_MOTOR_SPEED;
-  if (rot_command > MAX_MOTOR_SPEED) rot_command = MAX_MOTOR_SPEED;
-  else if (rot_command < -MAX_MOTOR_SPEED) rot_command = -MAX_MOTOR_SPEED;
-
-  if (trans_command > 0)
-    strcpy(speed, FORWARD);
-  else strcpy(speed, REVERSE);
-  if (rot_command > 0)
-    strcpy(heading, LEFT);
-  else strcpy(heading, RIGHT);
-
-  // form motor cmd string
-  strcpy(cmd_str, speed);
-  snprintf(cmd_str+2, 4, "%.2x", abs(trans_command)); // start at char 3
-  strcat(cmd_str, "\r");
-  strcat(cmd_str, heading);
-  snprintf(cmd_str + strlen(cmd_str), 4, "%.2x", abs(rot_command));
-  strcat(cmd_str, "\r");
-
-  return 0;
+// Main driver thread runs here.
+///////////////////////////////////////////////////////////////////////////
+void roboteq::Main() {
+       for (;;) {
+               pthread_testcancel();
+               ProcessMessages();
+               
+               // Update data
+               UpdatePowerData(&(this->power_data));
+               
+               if (encoder_present != 0) {
+                       UpdatePositionData(&(this->position_data));
+               }
+               
+               // Publish data
+               this->Publish(position_addr, PLAYER_MSGTYPE_DATA, 
PLAYER_POSITION2D_DATA_STATE, 
+                       (unsigned char *) &position_data, 
sizeof(position_data), NULL);
+               this->Publish(power_addr, PLAYER_MSGTYPE_DATA, 
PLAYER_POWER_DATA_STATE,
+                       (unsigned char *) &power_data, sizeof(power_data), 
NULL);
+               
+               usleep(25000);
+       }
+         
+       return;
 }
 
 


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge  
This is your chance to win up to $100,000 in prizes! For a limited time, 
vendors submitting new applications to BlackBerry App World(TM) will have
the opportunity to enter the BlackBerry Developer Challenge. See full prize  
details at: http://p.sf.net/sfu/Challenge
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to