Revision: 9005
          http://playerstage.svn.sourceforge.net/playerstage/?rev=9005&view=rev
Author:   jpgr87
Date:     2010-12-11 21:51:30 +0000 (Sat, 11 Dec 2010)

Log Message:
-----------
Applied patch #3134435: Lego NXT driver

Modified Paths:
--------------
    code/player/trunk/server/drivers/mixed/CMakeLists.txt

Added Paths:
-----------
    code/player/trunk/server/drivers/mixed/nxt/
    code/player/trunk/server/drivers/mixed/nxt/CMakeLists.txt
    code/player/trunk/server/drivers/mixed/nxt/examples/
    code/player/trunk/server/drivers/mixed/nxt/examples/standalone.cc
    code/player/trunk/server/drivers/mixed/nxt/src/
    code/player/trunk/server/drivers/mixed/nxt/src/chronos.cc
    code/player/trunk/server/drivers/mixed/nxt/src/chronos.hh
    code/player/trunk/server/drivers/mixed/nxt/src/nxt_driver.cc
    code/player/trunk/server/drivers/mixed/nxt/src/nxtdc.cc
    code/player/trunk/server/drivers/mixed/nxt/src/nxtdc.hh

Modified: code/player/trunk/server/drivers/mixed/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/mixed/CMakeLists.txt       2010-12-11 
21:25:28 UTC (rev 9004)
+++ code/player/trunk/server/drivers/mixed/CMakeLists.txt       2010-12-11 
21:51:30 UTC (rev 9005)
@@ -12,6 +12,7 @@
 ADD_SUBDIRECTORY (mbase)
 ADD_SUBDIRECTORY (mricp)
 ADD_SUBDIRECTORY (nomad)
+ADD_SUBDIRECTORY (nxt)
 ADD_SUBDIRECTORY (p2os)
 ADD_SUBDIRECTORY (phidgetIFK)
 ADD_SUBDIRECTORY (reb)

Added: code/player/trunk/server/drivers/mixed/nxt/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/mixed/nxt/CMakeLists.txt                   
        (rev 0)
+++ code/player/trunk/server/drivers/mixed/nxt/CMakeLists.txt   2010-12-11 
21:51:30 UTC (rev 9005)
@@ -0,0 +1,19 @@
+PLAYERDRIVER_OPTION (nxt build_nxt ON)
+PLAYERDRIVER_REQUIRE_PKG (nxt build_nxt libusb-1.0
+    nxt_includeDirs nxt_libDirs nxt_linkLibs
+    nxt_linkFlags nxt_cFlags)
+
+if (NOT HAVE_STL)
+    PLAYERDRIVER_OPTION (nxt build_nxt OFF "STL not found")
+endif(NOT HAVE_STL)
+
+PLAYERDRIVER_ADD_DRIVER (nxt build_nxt
+    INCLUDEDIRS ${nxt_includeDirs} LIBDIRS ${nxt_libDirs}
+    LINKLIBS ${nxt_linkLibs} LINKFLAGS ${nxt_linkFlags}
+    CFLAGS ${nxt_cFlags} 
+    SOURCES     
+    src/chronos.cc
+    src/nxt_driver.cc
+    src/nxtdc.cc
+)
+

Added: code/player/trunk/server/drivers/mixed/nxt/examples/standalone.cc
===================================================================
--- code/player/trunk/server/drivers/mixed/nxt/examples/standalone.cc           
                (rev 0)
+++ code/player/trunk/server/drivers/mixed/nxt/examples/standalone.cc   
2010-12-11 21:51:30 UTC (rev 9005)
@@ -0,0 +1,72 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2010
+ *     Alejandro R. Mosteo
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <cstdio>
+#include "nxtdc.hh"
+#include <unistd.h>
+
+using namespace NXT;
+using namespace std;
+
+void set_power ( brick &b, int power )
+{
+  const int i = power;
+
+  b.set_motor ( B,  i );
+  b.set_motor ( C, -i );
+ 
+  usleep ( 100000 );
+  const output_state st_b = b.get_motor_state ( B );
+  const output_state st_c = b.get_motor_state ( C );
+  printf ( "Power: %4d B:[%6d/%6d/%6d/%6d] C:[%6d/%6d/%6d/%6d]\n",
+           i,
+           st_b.tacho_limit, st_b.tacho_count, st_b.block_tacho_count, 
st_b.rotation_count,
+           st_c.tacho_limit, st_c.tacho_count, st_c.block_tacho_count, 
st_c.rotation_count );
+}
+
+int main ( void )
+{
+  NXT::brick b;
+
+  const NXT::versions v = b.get_version();
+
+  printf ( "Connected to NXT brick named %s, protocol %d.%d firmware %d.%d\n",
+           b.get_device_info().brick_name,
+           v.protocol_major, v.protocol_minor,
+           v.firmware_major, v.firmware_minor );
+
+  printf ( "Battery: %d\n", b.get_battery_level() );
+
+  b.play_tone ( 700, 1000 );
+
+  for ( int i = 0; i <= 100; i++ )
+    set_power ( b, i );
+  for ( int i = 100; i >= -100; i-- )
+    set_power ( b, i );
+  for ( int i = -100; i <= 0; i++ )
+    set_power ( b, i );
+
+  printf ( "Battery: %d\n", b.get_battery_level() );
+
+  // b.msg_rate_check();
+
+  return 0;
+}

Added: code/player/trunk/server/drivers/mixed/nxt/src/chronos.cc
===================================================================
--- code/player/trunk/server/drivers/mixed/nxt/src/chronos.cc                   
        (rev 0)
+++ code/player/trunk/server/drivers/mixed/nxt/src/chronos.cc   2010-12-11 
21:51:30 UTC (rev 9005)
@@ -0,0 +1,55 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2010
+ *     Alejandro R. Mosteo
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <cerrno>
+#include "chronos.hh"
+#include <cstring>
+#include <stdexcept>
+#include <sys/time.h>
+
+using namespace nxt_driver;
+
+Chronos::Chronos ( double seconds_since_epoch )
+{
+  clock_ = seconds_since_epoch;
+}
+
+double Chronos::elapsed ( void ) const
+  {
+    return now() - clock_;
+  }
+
+void Chronos::reset ( void )
+{
+  clock_ = now();
+}
+
+double Chronos::now ( void )
+  {
+    struct timeval clock;
+
+    if ( gettimeofday ( &clock, NULL ) != 0 )
+      throw std::runtime_error ( strerror ( errno ) );
+
+    return
+      static_cast<double> ( clock.tv_sec ) +
+      static_cast<double> ( clock.tv_usec ) * 1e-6;
+  }
\ No newline at end of file

Added: code/player/trunk/server/drivers/mixed/nxt/src/chronos.hh
===================================================================
--- code/player/trunk/server/drivers/mixed/nxt/src/chronos.hh                   
        (rev 0)
+++ code/player/trunk/server/drivers/mixed/nxt/src/chronos.hh   2010-12-11 
21:51:30 UTC (rev 9005)
@@ -0,0 +1,43 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2010
+ *     Alejandro R. Mosteo
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _chronos_
+#define _chronos_
+
+namespace nxt_driver
+  {
+
+  class Chronos
+    {
+    public:
+      Chronos ( double seconds_since_epoch = now() );
+      double elapsed ( void ) const;
+      void reset ( void );
+
+    private:
+      double clock_;
+
+      static double now ( void ) ;
+    };
+
+}
+
+#endif

Added: code/player/trunk/server/drivers/mixed/nxt/src/nxt_driver.cc
===================================================================
--- code/player/trunk/server/drivers/mixed/nxt/src/nxt_driver.cc                
                (rev 0)
+++ code/player/trunk/server/drivers/mixed/nxt/src/nxt_driver.cc        
2010-12-11 21:51:30 UTC (rev 9005)
@@ -0,0 +1,412 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2010
+ *     Alejandro R. Mosteo
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @ingroup drivers */
+/** @{ */
+/** @defgroup driver_nxt nxt
+ * @brief Lego Mindstorms NXT
+
+This driver implements partial interaction with a USB-connected Lego 
Mindstorms NXT brick.\n
+Motors are implemented.\n
+Sensors are unimplemented.
+
+...@par Compile-time dependencies
+
+- libusb-1.0 or newer (www.libusb.org)
+
+...@par Provides
+
+- @ref interface_position1d
+    - One per each of the A, B, C motors
+    - These can be aggregated in a position2d using, e.g., @ref 
driver_differential
+    - Velocity commands are accepted. Position commands are not.
+- @ref interface_power
+    - Battery level of the brick.
+
+...@par Configuration file options
+
+- max_power (tuple of float [%] default: [100 100 100])
+  - Power applied when maximum vel is requested for each motor.
+
+- max_speed (tuple of float [length/s] default: [0.5 0.5 0.5])
+  - Speed that each motor provides at max_power (must be calibrated/measured 
somehow depending on the LEGO model built).
+
+- odom_rate (tuple of float default [0.0005 0.0005 0.0005])
+  - Multiplier for the tachometer in the lego motor. tacho_count x odom_rate = 
real_distance (must be calibrated also).
+  - Default is somewhat close to the standard small wheels with direct motor 
drive.
+
+- period (float [s] default 0.05)
+  - Seconds between reads of motor encoders. Since this requires polling and 
affects CPU use, each app can set an adequate timing.
+  - Note that a polling roundtrip via USB takes (empirically measured) around 
2ms per motor.
+
+...@par Example
+
+...@verbatim
+
+# Standard configured brick with B and C motors in use
+
+unit_length "m"
+unit_angle  "radians"
+
+# The NXT driver
+driver
+(
+  name "nxt"
+  provides [ "B:::position1d:0" "C:::position1d:1" "power:0" ]
+
+  max_power [100 100 100] # 100% power is to be used
+  max_speed [0.5 0.5 0.5] # in order to achieve 0.5 m/s linearly
+  odom_rate [0.1 0.1 0.1] # multiplier for odometry
+
+  period 0.05
+)
+
+# The differential driver that provides simplified position2d management
+driver
+(
+  name "differential"
+  requires [ "left:::position1d:0" "right:::position1d:1" ]
+  provides [ "position2d:0" ]
+
+  axis_length 0.25
+)
+
+...@endverbatim
+
+...@author Alejandro R. Mosteo
+*/
+/** @} */
+
+#include "chronos.hh"
+#include "libplayercore/playercore.h"
+#include "nxtdc.hh"
+
+using namespace nxt_driver;
+
+const int kNumMotors = 3;
+
+class Nxt : public ThreadedDriver
+  {
+  public:
+    Nxt ( ConfigFile* cf, int section );
+
+    virtual void Main ( void );
+    virtual int  MainSetup ( void );
+    virtual void MainQuit ( void );
+
+    virtual int ProcessMessage ( QueuePointer &resp_queue, player_msghdr * 
hdr, void * data );
+
+  private:
+    player_devaddr_t motor_addr_[kNumMotors];
+    player_devaddr_t power_addr_;
+
+    player_position1d_data_t data_state_     [kNumMotors]; // Just read status.
+    player_position1d_data_t data_state_prev_[kNumMotors]; // Previous status 
to integrate speed.
+
+    double           max_power_[kNumMotors];
+    double           max_speed_[kNumMotors];
+    double           odom_rate_[kNumMotors];
+
+    bool             publish_motor_[kNumMotors];
+    bool             publish_power_;
+
+    player_power_data_t juice_;
+
+    double           period_;
+    Chronos          timer_battery_;
+    Chronos          timer_period_;
+
+    NXT::brick       *brick_;
+
+    void             CheckBattery ( void );
+    void             CheckMotors ( void );
+    NXT::motors      GetMotor ( const player_devaddr_t &addr ) const;
+    int8_t           GetPower ( float vel, NXT::motors motor ) const;
+  };
+
+Driver* nxt_Init ( ConfigFile* cf, int section )
+{
+  return static_cast<Driver*> ( new Nxt ( cf, section ) );
+}
+
+void nxt_Register ( DriverTable* table )
+{
+  table->AddDriver ( "nxt", nxt_Init );
+}
+
+const char *motor_names[kNumMotors] = { "A", "B", "C" };
+
+Nxt::Nxt ( ConfigFile *cf, int section )
+    : ThreadedDriver ( cf, section ),
+    period_ ( cf->ReadFloat ( section, "period", 0.05 ) ),
+    timer_battery_ ( -666.0 )   // Ensure first update to be sent immediately
+{
+  for ( int i = 0; i < kNumMotors; i++ )
+    {
+      // Read them regardless of motor usage to placate player unused warnings
+      max_power_[i] = cf->ReadTupleFloat ( section, "max_power", i, 100.0 );
+      max_speed_[i] = cf->ReadTupleFloat ( section, "max_speed", i, 0.5 );
+      odom_rate_[i] = cf->ReadTupleFloat ( section, "odom_rate", i, 0.0005 );
+
+      if ( cf->ReadDeviceAddr ( &motor_addr_[i], section, "provides", 
PLAYER_POSITION1D_CODE, -1, motor_names[i] ) == 0 )
+        {
+          PLAYER_MSG1 ( 3, "nxt: Providing motor %s", motor_names[i] );
+
+          if ( AddInterface ( motor_addr_[i] ) != 0 )
+            throw std::runtime_error ( "Cannot add position1d interface" );
+          else
+            {
+              publish_motor_[i] = true;
+
+              data_state_[i].pos    = 0.0f;
+              data_state_[i].vel    = 0.0f;
+              data_state_[i].stall  = 0;
+              data_state_[i].status = ( 1 << PLAYER_POSITION1D_STATUS_ENABLED 
);
+            }
+        }
+    }
+
+  if ( cf->ReadDeviceAddr ( &power_addr_, section, "provides", 
PLAYER_POWER_CODE, -1, NULL ) == 0 )
+    {
+      if ( AddInterface ( power_addr_ ) != 0 )
+        throw std::runtime_error ( "Cannot add power interface" );
+      else
+        publish_power_ = true;
+    }
+  else
+    {
+      publish_power_ = false;
+    }
+
+}
+
+int Nxt::MainSetup ( void )
+{
+  brick_ = new NXT::brick();
+
+  // Reset odometries to origin
+  for ( int i = 0; i < kNumMotors; i++ )
+    if ( publish_motor_[i] )
+      brick_->execute ( brick_->prepare_reset_motor_position ( 
static_cast<NXT::motors> ( i ), false ) );
+
+  return 0;
+}
+
+void Nxt::MainQuit ( void )
+{
+  // Stop motors just in case they're running.
+  // The brick has no watchdog, so they will keep its last commanded speed 
forever
+  for ( int i = 0; i < kNumMotors; i++ )
+    if ( publish_motor_[i] )
+      brick_->set_motor ( static_cast<NXT::motors> ( i ), 0 );
+
+  delete brick_;
+}
+
+void Nxt::Main ( void )
+{
+  while ( true )
+    {
+      // Wait till we get new data or we need to measure something
+      Wait ( period_ );
+
+      pthread_testcancel();
+
+      ProcessMessages ( 0 );
+
+      CheckBattery();
+      CheckMotors();
+    }
+}
+
+void Nxt::CheckBattery ( void )
+{
+  if ( ! publish_power_ )
+    return;
+
+  // We don't want to poll battery level innecesarily often (how much is this, 
really?)
+  if ( timer_battery_.elapsed() > 10.0 )
+    {
+      timer_battery_.reset();
+
+      juice_.valid = PLAYER_POWER_MASK_VOLTS;
+      juice_.volts = static_cast<float> ( brick_->get_battery_level() ) / 
1000.0f;
+      // Omitted 4 unknown values here
+    };
+
+  // Publish value
+  if ( HasSubscriptions() )
+    Publish ( power_addr_,
+              PLAYER_MSGTYPE_DATA,
+              PLAYER_POWER_DATA_STATE,
+              static_cast<void*> ( &juice_ ) );
+
+  PLAYER_MSG1 ( 3, "Publishing power: %8.2f\n", juice_.volts );
+}
+
+void Nxt::CheckMotors ( void )
+{
+  if ( timer_period_.elapsed() < period_ )
+    return;
+
+  timer_period_.reset();
+
+  // First we get odometry updates from brick
+  for ( int i = 0; i < kNumMotors; i++ )
+    {
+      if ( ! publish_motor_[i] )
+        continue;
+
+      const NXT::output_state state = brick_->get_motor_state ( 
static_cast<NXT::motors> ( i ) );
+
+      data_state_[i].pos = state.tacho_count * odom_rate_[i];
+      data_state_[i].vel = ( data_state_[i].pos - data_state_prev_[i].pos ) / 
period_;
+
+      PLAYER_MSG3 ( 5, "nxt: odom read is [raw/adjusted/vel] = [ %8d / %8.2f / 
%8.2f ]",
+                    state.tacho_count, data_state_[i].pos, data_state_[i].vel 
);
+
+      data_state_prev_[i] = data_state_[i];
+    }
+
+  // Then we publish them together, to minimize unsyncing in a consuming 
driver (e.g. differential driver)
+  for ( int i = 0; i < kNumMotors; i++ )
+    if ( publish_motor_[i] && HasSubscriptions() )
+      Publish ( motor_addr_[i],
+                PLAYER_MSGTYPE_DATA,
+                PLAYER_POSITION1D_DATA_STATE,
+                static_cast<void*> ( &data_state_[i] ) );
+
+}
+
+int Nxt::ProcessMessage ( QueuePointer  & resp_queue,
+                          player_msghdr * hdr,
+                          void          * data )
+{
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POWER_REQ_SET_CHARGING_POLICY, power_addr_ ) )
+    {
+      PLAYER_WARN ( "nxt: there are no charging policies." );
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_CMD, 
PLAYER_POSITION1D_CMD_POS ) ||
+       Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION1D_REQ_POSITION_PID ) )
+    {
+      PLAYER_WARN ( "nxt: position commands not supported" );
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_CMD, 
PLAYER_POSITION1D_CMD_VEL ) )
+    {
+      player_position1d_cmd_vel_t &vel = 
*static_cast<player_position1d_cmd_vel_t*> ( data );
+
+      const NXT::motors motor = GetMotor ( hdr->addr );
+      brick_->set_motor ( motor, GetPower ( vel.vel, motor ) );
+
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION1D_REQ_GET_GEOM ) )
+    {
+      PLAYER_WARN ( "nxt: geometry not supported" );
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION1D_REQ_POSITION_MODE ) )
+    {
+      PLAYER_WARN ( "nxt: mode is always speed" );
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION1D_REQ_MOTOR_POWER ) )
+    {
+      PLAYER_WARN ( "nxt: motors are always on" );
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION1D_REQ_RESET_ODOM ) )
+    {
+      brick_->execute ( brick_->prepare_reset_motor_position ( GetMotor ( 
hdr->addr ) ) );
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION1D_REQ_SET_ODOM ) )
+    {
+      PLAYER_WARN ( "nxt: odometry setting to arbitrary values not supported" 
);
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION1D_REQ_SPEED_PID ) )
+    {
+      PLAYER_WARN ( "nxt: speed profiles not supported" );
+      return 0;
+    }
+
+  if ( Message::MatchMessage ( hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_POSITION1D_REQ_SPEED_PROF ) )
+    {
+      PLAYER_WARN ( "nxt: acceleration ignored, adjusting max speed only" );
+      const NXT::motors motor = GetMotor ( hdr->addr );
+      const player_position1d_speed_prof_req_t &prof = 
*static_cast<player_position1d_speed_prof_req_t*> ( data );
+
+      max_power_[motor] *= ( prof.speed / max_speed_[motor] ); // Adjust power 
proportionally
+      max_speed_[motor]  = prof.speed;
+
+      if ( abs ( max_power_[motor] ) > 100 )
+        PLAYER_WARN2 ( "nxt: requested speed would require excess power: 
[speed/power] = [ %8.2f / %8.2f ]",
+                       max_speed_[motor], max_power_[motor] );
+      return 0;
+    }
+
+  PLAYER_WARN4 ( "nxt: Message not processed idx:%d type:%d sub:%d seq:%d\n", 
hdr->addr.index, hdr->type, hdr->subtype, hdr->seq );
+  return -1;
+}
+
+
+NXT::motors Nxt::GetMotor ( const player_devaddr_t &addr ) const
+  {
+    for ( int i = 0; i < kNumMotors; i++ )
+      if ( ( addr.host   == motor_addr_[i].host ) &&
+           ( addr.robot  == motor_addr_[i].robot ) &&
+           ( addr.index  == motor_addr_[i].index ) &&
+           ( addr.interf == motor_addr_[i].interf ) )
+        return static_cast<NXT::motors> ( i );
+
+    throw std::runtime_error ( "nxt: received request for unknown motor" );
+  }
+
+template<class T>
+T sign ( T x )
+{
+  return x >= 0 ? 1 : -1;
+}
+
+int8_t Nxt::GetPower ( float vel, NXT::motors motor ) const
+{
+  const int8_t power = vel / max_speed_[motor] * max_power_[motor];
+  if ( abs ( power ) > abs ( max_power_[motor] ) )
+    {
+      PLAYER_WARN3 ( "nxt: exceeded max power [motor/reqvel/reqpwr] = [ %s / 
%8.2f / %d ]",
+                     motor_names[motor], vel, power );
+      return max_power_[motor] * sign<float> ( power );
+    }
+  else
+    return power;
+}
+

Added: code/player/trunk/server/drivers/mixed/nxt/src/nxtdc.cc
===================================================================
--- code/player/trunk/server/drivers/mixed/nxt/src/nxtdc.cc                     
        (rev 0)
+++ code/player/trunk/server/drivers/mixed/nxt/src/nxtdc.cc     2010-12-11 
21:51:30 UTC (rev 9005)
@@ -0,0 +1,494 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2010
+ *     Alejandro R. Mosteo
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <endian.h>
+#include "nxtdc.hh"
+#include <sstream>
+#include <sys/time.h>
+
+using namespace NXT;
+using namespace std;
+
+const uint16_t VENDOR_LEGO = 0x0694;
+const uint16_t PRODUCT_NXT = 0x0002;
+
+const int kNxtConfig    = 1;
+const int kNxtInterface = 0;
+
+const uint8_t kMaxTelegramSize = 64; // Per NXT spec.
+
+const unsigned char kOutEndpoint = 0x1;
+const unsigned char kInEndpoint  = 0x82;
+
+enum direct_commands
+{
+  command_play_tone            = 0x03,
+  command_set_output_state     = 0x04,
+  command_get_output_state     = 0x06,
+  command_reset_motor_position = 0x0A,
+  command_get_battery_level    = 0x0B,
+  command_stop_sound_playback  = 0x0C,
+  command_keep_alive           = 0x0D
+};
+
+enum system_commands
+{
+  system_get_firmware_version  = 0x88,
+  system_get_device_info       = 0x9B
+};
+
+const char *usberr_to_str ( int err )
+{
+  switch ( err )
+    {
+    case 0:
+      return "LIBUSB_SUCCESS";
+    case -1:
+      return "LIBUSB_ERROR_IO";
+    case -2:
+      return "LIBUSB_ERROR_INVALID_PARAM";
+    case -3:
+      return "LIBUSB_ERROR_ACCESS";
+    case -4:
+      return "LIBUSB_ERROR_NO_DEVICE";
+    case -5:
+      return "LIBUSB_ERROR_NOT_FOUND";
+    case -6:
+      return "LIBUSB_ERROR_BUSY";
+    case -7:
+      return "LIBUSB_ERROR_TIMEOUT";
+    case -8:
+      return "LIBUSB_ERROR_OVERFLOW";
+    case -9:
+      return "LIBUSB_ERROR_PIPE";
+    case -10:
+      return "LIBUSB_ERROR_INTERRUPTED";
+    case -11:
+      return "LIBUSB_ERROR_NO_MEM";
+    case -12:
+      return "LIBUSB_ERROR_NOT_SUPPORTED";
+    case -99:
+      return "LIBUSB_ERROR_OTHER";
+    default:
+      return "LIBUSB_ERROR_UNKNOWN";
+    }
+}
+
+string nxterr_to_str ( int err )
+{
+  switch ( err )
+    {
+    case 0x20:
+      return "Pending communication transaction in progress";
+    case 0x40:
+      return "Specified mailbox queue is empty";
+    case 0xBD:
+      return "Request failed (e.g. specified file not found)";
+    case 0xBE:
+      return "Unknown command opcode";
+    case 0xBF:
+      return "Insane packet";
+    case 0xC0:
+      return "Data contains out-of-range values";
+    case 0xDD:
+      return "Communication bus error";
+    case 0xDE:
+      return "No free memory in communication buffer";
+    case 0xDF:
+      return "Specified channel/connection is not valid";
+    case 0xE0:
+      return "Specified channel/connection not configured or busy";
+    case 0xEC:
+      return "No active program";
+    case 0xED:
+      return "Illegal size specified";
+    case 0xEE:
+      return "Illegal mailbox queue ID specified";
+    case 0xEF:
+      return "Attempted to access invalid field or structure";
+    case 0xF0:
+      return "Bad input or output specified";
+    case 0xFB:
+      return "Insufficient memory available";
+    case 0xFF:
+      return "Bad arguments";
+    default:
+      char s[100];
+      snprintf ( s, 100, "NXT_UNCATEGORIZED_ERROR: 0x%02x", err );
+      return string ( s );
+    }
+}
+
+buffer & buffer::append_byte ( uint8_t byte )
+{
+  push_back ( static_cast<unsigned char> ( byte ) );
+  return *this;
+}
+
+buffer & buffer::append_word ( uint16_t word )
+{
+  union
+    {
+      unsigned char bytes[2];
+      uint16_t      le_word;
+    } tmp;
+
+  tmp.le_word = htole16 ( word );
+
+  push_back ( tmp.bytes[0] );
+  push_back ( tmp.bytes[1] );
+
+  return *this;
+}
+
+buffer & buffer::append ( const buffer & buf )
+{
+  for ( size_t i = 0; i < buf.size(); i++ )
+    push_back ( buf[i] );
+
+  return *this;
+}
+
+void buffer::dump ( const string & header ) const
+  {
+    printf ( "%s\n", header.c_str() );
+
+    for ( size_type i = 0; i < this->size(); i++ )
+      printf ( "%2d = 0x%02x\n", i, this->at ( i ) );
+  }
+
+void USB_transport::usb_check ( int usb_error )
+{
+  if ( usb_error != LIBUSB_SUCCESS )
+    throw runtime_error ( string ( "USB error: " ) + usberr_to_str ( usb_error 
) );
+}
+
+USB_transport::USB_transport ( void )
+{
+  usb_check ( libusb_init ( &context_ ) );
+  libusb_set_debug ( context_, 3 );
+
+  handle_ = libusb_open_device_with_vid_pid ( context_, VENDOR_LEGO, 
PRODUCT_NXT );
+  if ( handle_ == NULL )
+    throw runtime_error ( "USB_transport: brick not found." );
+
+  usb_check ( libusb_set_configuration ( handle_, kNxtConfig ) );
+  usb_check ( libusb_claim_interface ( handle_, kNxtInterface ) );
+  usb_check ( libusb_reset_device ( handle_ ) );
+}
+
+USB_transport::~USB_transport ( void )
+{
+  //    usb_check(libusb_release_interface(handle_, kNxtInterface));
+  // Fails, why?
+
+  libusb_close ( handle_ );
+  libusb_exit ( context_ );
+}
+
+void USB_transport::write ( const buffer &buf )
+{
+  int transferred;
+
+  // buf.dump ( "write" );
+
+  usb_check ( libusb_bulk_transfer
+              ( handle_, kOutEndpoint,
+                ( unsigned char* ) &buf[0], buf.size(),
+                &transferred, 0 ) );
+  // printf ( "T:%d\n", transferred );
+}
+
+buffer USB_transport::read ( void )
+{
+  unsigned char buf[kMaxTelegramSize];
+  int           transferred;
+
+  usb_check ( libusb_bulk_transfer
+              ( handle_, kInEndpoint,
+                &buf[0], kMaxTelegramSize,
+                &transferred, 0 ) );
+  // printf ( "%2x %2x %2x (%d read)\n", buf[0], buf[1], buf[2], transferred );
+
+  buffer result;
+  result.reserve ( transferred );
+
+  for ( int i = 0; i < transferred; i++ )
+    result.push_back ( buf[i] );
+
+  return result ;
+}
+
+brick::brick ( void )
+{
+  ;
+}
+
+brick::~brick ( void )
+{
+  ;
+}
+
+buffer brick::execute ( const buffer &command, bool with_feedback )
+{
+  assert ( command.size() >= 2 );
+
+  if ( with_feedback && ( ! ( command[0] & 0x80 ) ) )
+    link_.write ( command );
+  else if ( ( !with_feedback ) && ( command[0] & 0x80 ) )
+    link_.write ( command );
+  else
+    {
+      buffer newcomm ( command );
+
+      // Set or reset 0x80 bit (confirmation request)
+      newcomm[0] = ( with_feedback ? command[0] & 0x7F : command[0] | 0x80 );
+
+      link_.write ( newcomm );
+    }
+
+  if ( with_feedback )
+    {
+      const buffer reply = link_.read ();
+      if ( reply.size() < 3 )
+        {
+          stringstream s;
+          s << "Reply too short: " << reply.size() << " bytes";
+          throw nxt_error ( s.str () );
+        }
+      else if ( reply[0] != brick::reply )
+        {
+          char s[100];
+          snprintf ( s, 100, "Unexpected telegram: 0x%02x != 0x%02x", 
reply[0], brick::reply );
+          throw nxt_error ( s );
+        }
+      else if ( reply[1] != command[1] )
+        {
+          char s[100];
+          snprintf ( s, 100, "Unexpected reply type: 0x%02x != 0x%02x", 
reply[1], command[1] );
+          throw nxt_error ( s );
+        }
+      else if ( reply[2] != 0 )
+        throw nxt_error ( nxterr_to_str ( reply[2] ) );
+      else
+        return reply;
+    }
+  else
+    return buffer();
+}
+
+buffer brick::prepare_play_tone ( uint16_t tone_Hz, uint16_t duration_ms )
+{
+  return assemble ( direct_command_without_response,
+                    command_play_tone,
+                    buffer().
+                    append_word ( tone_Hz ).
+                    append_word ( duration_ms ) ) ;
+}
+
+buffer brick::prepare_output_state (
+  motors           motor       ,
+  int8_t           power_pct   ,
+  motor_modes      mode        ,
+  regulation_modes regulation  ,
+  int8_t           turn_ratio  ,
+  motor_run_states state       ,
+  uint32_t         tacho_count )
+{
+  union
+    {
+      uint8_t  bytes[4];
+      uint32_t tacho;
+    } aux;
+
+  aux.tacho = htole32 ( tacho_count );
+
+  return
+    assemble
+    ( direct_command_without_response,
+      command_set_output_state,
+      buffer().
+      append_byte ( motor ).
+      append_byte ( power_pct ).
+      append_byte ( mode ).
+      append_byte ( regulation ).
+      append_byte ( turn_ratio ).
+      append_byte ( state ).
+      append_byte ( aux.bytes[0] ).append_byte ( aux.bytes[1] ).
+      append_byte ( aux.bytes[2] ).append_byte ( aux.bytes[3] ) );
+}
+
+buffer brick::prepare_reset_motor_position ( motors motor, bool 
relative_to_last_position )
+{
+  return
+    assemble ( direct_command_without_response,
+               command_reset_motor_position,
+               buffer().
+               append_byte ( motor ).
+               append_byte ( relative_to_last_position ) );
+}
+
+buffer brick::prepare_stop_sound_playback ( void )
+{
+  return
+    assemble ( direct_command_without_response,
+               command_stop_sound_playback,
+               buffer() );
+}
+
+buffer brick::prepare_keep_alive ( void )
+{
+  return
+    assemble ( direct_command_without_response,
+               command_keep_alive,
+               buffer() );
+}
+
+void brick::play_tone ( uint16_t tone_Hz, uint16_t duration_ms )
+{
+  execute ( prepare_play_tone ( tone_Hz, duration_ms ), false );
+}
+
+void brick::set_motor ( motors motor, int8_t power_pct )
+{
+  execute
+  ( assemble
+    ( direct_command_without_response,
+      command_set_output_state,
+      buffer().
+      append_byte ( motor ).
+      append_byte ( power_pct ).
+      append_byte ( motor_brake ).             // Brake uses a bit more power 
but gives finer control at low speeds
+      append_byte ( regulation_motor_speed ). // Better Idle than Running, 
which tries to compensate loads?
+      append_byte ( 0 ).                       // TURN_RATIO
+      append_byte ( power_pct == 0 ? motor_run_state_idle : 
motor_run_state_running ).
+      append_word ( 0 ).append_word ( 0 ) ),   // Tacho count (unlimited)
+    false );
+}
+
+output_state brick::get_motor_state ( motors motor )
+{
+  const buffer reply =
+    execute
+    ( assemble
+      ( direct_command_with_response,
+        command_get_output_state,
+        buffer().append_byte ( motor ) ) , true );
+
+  const output_state state =
+  {
+    reply[3],                                   // motor
+    reply[4],                                   // power_pct
+    static_cast<motor_modes> ( reply[5] ),      // motor_modes
+    static_cast<regulation_modes> ( reply[6] ), // regulation_modes
+    reply[7],                                   // turn_ratio
+    static_cast<motor_run_states> ( reply[8] ), // motor_run_states
+    le32toh ( *reinterpret_cast<const int32_t*> ( &reply[9] ) ),
+    le32toh ( *reinterpret_cast<const int32_t*> ( &reply[13] ) ),
+    le32toh ( *reinterpret_cast<const int32_t*> ( &reply[17] ) ),
+    le32toh ( *reinterpret_cast<const int32_t*> ( &reply[21] ) ),
+  };
+  return state;
+
+}
+
+versions brick::get_version ( void )
+{
+  const buffer reply =
+    execute
+    ( assemble
+      ( system_command_with_response,
+        system_get_firmware_version,
+        buffer() ) , true );
+
+  const versions v = { reply.at ( 3 ), reply.at ( 4 ), reply.at ( 5 ), 
reply.at ( 6 ) };
+  return v;
+}
+
+device_info brick::get_device_info ( void )
+{
+  const buffer reply =
+    execute
+    ( assemble
+      ( system_command_with_response,
+        system_get_device_info,
+        buffer() ) , true );
+
+  device_info info;
+  strncpy ( info.brick_name, reinterpret_cast<const char*> ( &reply[3] ), 14 );
+  info.brick_name[14] = '\0';
+  strncpy ( info.bluetooth_address, reinterpret_cast<const char*> ( &reply[18] 
), 6 );
+  info.bluetooth_address[6] = '\0';
+
+  return info;
+}
+
+uint16_t brick::get_battery_level ( void )
+{
+  const buffer reply =
+    execute ( assemble ( direct_command_with_response,
+                         command_get_battery_level,
+                         buffer() ),
+              true );
+
+  union
+    { const unsigned char * bytes;
+      const uint16_t      * level;
+    } aux;
+
+  aux.bytes = &reply[3];
+  return le16toh ( *aux.level );
+}
+
+void brick::msg_rate_check ( void )
+{
+  struct timeval start, now;
+  int calls=0;
+
+  assert ( gettimeofday ( &start, NULL ) == 0 );
+
+  do
+    {
+      execute ( prepare_play_tone ( 440, 0 ), true );
+      calls++;
+
+      assert ( gettimeofday ( &now, NULL ) == 0 );
+    }
+  while ( ( double ) start.tv_sec + ( double ) start.tv_usec/1000000.0 -
+          ( double ) now.tv_sec - ( double ) now.tv_usec/1000000.0 > -10.0 );
+
+  printf ( "%d calls in 10s (%dms per call)\n", calls, 10000 / calls );
+}
+
+buffer brick::assemble ( telegram_types teltype,
+                         uint8_t        command,
+                         const buffer & payload )
+{
+  return
+    buffer ().
+    append_byte ( teltype ).
+    append_byte ( command ).
+    append ( payload );
+}
+

Added: code/player/trunk/server/drivers/mixed/nxt/src/nxtdc.hh
===================================================================
--- code/player/trunk/server/drivers/mixed/nxt/src/nxtdc.hh                     
        (rev 0)
+++ code/player/trunk/server/drivers/mixed/nxt/src/nxtdc.hh     2010-12-11 
21:51:30 UTC (rev 9005)
@@ -0,0 +1,211 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2010
+ *     Alejandro R. Mosteo
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <libusb.h>
+#include <stdexcept>
+#include <vector>
+
+namespace NXT
+  {
+
+  using namespace std;
+
+  typedef vector<unsigned char> protobuffer;
+
+  class buffer : public protobuffer
+    {
+    public:
+      buffer & append_byte ( uint8_t byte );
+      buffer & append_word ( uint16_t word );
+      buffer & append ( const buffer & buf );
+      // return self to chain calls
+
+      void dump ( const string & header ) const; // Debug to stdout
+    };
+
+  class transport
+    {
+    public:
+      virtual void write ( const buffer &buf ) = 0;
+      virtual buffer read ( void ) = 0;
+    };
+
+  class USB_transport : public transport
+    {
+    public:
+      USB_transport ( void );
+      ~USB_transport ( void );
+      virtual void write ( const buffer &buf );
+      virtual buffer read ( void );
+    private:
+      libusb_context *context_;
+      libusb_device_handle *handle_;
+
+      void usb_check ( int usb_error );
+    };
+
+  class nxt_error : public runtime_error
+    {
+    public :
+      nxt_error ( const char *s )    : runtime_error ( s ) {};
+      nxt_error ( const string & s ) : runtime_error ( s ) {};
+    };
+
+  enum motors
+  {
+    A   = 0x00,
+    B   = 0x01,
+    C   = 0x02,
+    All = 0xFF
+  };
+
+  enum motor_modes
+  {
+    motor_on        = 0x01,
+    motor_brake     = 0x02,
+    motor_regulated = 0x04
+  };
+
+  enum regulation_modes
+  {
+    regulation_motor_idle  = 0x00,
+    regulation_motor_speed = 0x01,
+    regulation_motor_sync  = 0x02
+  };
+
+  enum motor_run_states
+  {
+    motor_run_state_idle     = 0x00,
+    motor_run_state_ramp_up  = 0x10,
+    motor_run_state_running  = 0x20,
+    motor_run_state_rampdown = 0x40
+  };
+
+  typedef struct
+    {
+      uint8_t protocol_minor;
+      uint8_t protocol_major;
+      uint8_t firmware_minor;
+      uint8_t firmware_major;
+    } versions;
+
+  typedef struct
+    {
+      char brick_name[15];       // Null-terminated
+      char bluetooth_address[7]; // Null-terminated
+    } device_info;
+
+  typedef struct
+    {
+      uint8_t          motor;
+      int8_t           power_pct;
+      motor_modes      mode;
+      regulation_modes regulation;
+      int8_t           turn_ratio;
+      motor_run_states state;
+      int32_t          tacho_limit;       // programmed limit for current 
movement, if any
+      int32_t          tacho_count;       // current tacho count since last 
reset (acummulated odometry)
+      int32_t          block_tacho_count; // current pos relative to last 
programmed pos (delta odometry)
+      int32_t          rotation_count;    // current pos relative to the last 
reset of rot. counter
+    } output_state;
+    // Beware: the delta is since last command, not since last reading!    
+
+  class brick
+    {
+
+    public:
+
+      // Connect to the first brick found
+      // TODO: give some means of connecting to a particular brick.
+      // As is, this library serves only for using with a single brick.
+      brick ( void );
+
+      ~brick ( void );
+
+      // Execute a prepared command
+      // When with_feedback, the brick is asked to confirm proper execution
+      // Returns the reply buffer, with the reply flag byte, status, and etc.
+      //   or an empty buffer if !with_feedback
+      buffer execute ( const buffer &command, bool with_feedback = false );
+
+      // PREPARED COMMANDS
+      // That you an store and execute with or without feedback
+
+      buffer prepare_play_tone ( uint16_t tone_Hz, uint16_t duration_ms );
+
+      buffer prepare_output_state (
+        motors           motor,
+        int8_t           power_pct,
+        motor_modes      mode        = motor_brake,
+        regulation_modes regulation  = regulation_motor_speed,
+        int8_t           turn_ratio  = 0,
+        motor_run_states state       = motor_run_state_running,
+        uint32_t         tacho_count = 0 );
+      // Full motor control; refer to NXT docs for precise meanings...
+
+      buffer prepare_reset_motor_position ( motors motor, bool 
relative_to_last_position = false );
+      buffer prepare_stop_sound_playback ( void );
+      buffer prepare_keep_alive ( void );
+
+      // DIRECT PERFORMING (WITH FEEDBACK)
+      // If you don't want the feedback overhead, use execute with prepared 
commands
+      // Errors will be reported as thrown nxt_error
+
+      void play_tone ( uint16_t tone_Hz, uint16_t duration_ms );
+
+      // Simple motor control
+      void set_motor ( motors motor, int8_t power_pct );
+
+      output_state get_motor_state ( motors motor );
+
+      // In millivolts
+      uint16_t get_battery_level ( void );
+
+      versions get_version ( void );
+
+      device_info get_device_info ( void );
+
+      // Run a 10-second loop of play_tone, to get the average time per 
commands
+      // For testing purposes, yes.
+      void msg_rate_check ( void );
+
+    private:
+
+      enum telegram_types
+      {
+        direct_command_with_response    = 0x00,
+        system_command_with_response    = 0x01,
+        reply                           = 0x02,
+        direct_command_without_response = 0x80,
+        system_command_without_response = 0x80
+      };
+
+      buffer assemble ( telegram_types teltype,
+                        uint8_t        command,
+                        const buffer & payload );
+      //  Assembles the full telegram to be sent over usb or bluetooth.
+
+      USB_transport link_;
+      //  For now is a fixed USB transport, but we could easily add bluetooth 
here
+
+    };
+
+}


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

------------------------------------------------------------------------------
Oracle to DB2 Conversion Guide: Learn learn about native support for PL/SQL,
new data types, scalar functions, improved concurrency, built-in packages, 
OCI, SQL*Plus, data movement tools, best practices and more.
http://p.sf.net/sfu/oracle-sfdev2dev 
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to