Fawkes API  Fawkes Development Version
imu_cruizcore_xg1010.cpp
1 
2 /***************************************************************************
3  * imu_cruizcore_xg1010.cpp - Retrieve IMU data from CruizCore XG1010
4  *
5  * Created: Sun Jun 22 21:44:17 2014
6  * Copyright 2008-2014 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include "imu_cruizcore_xg1010.h"
23 
24 #include <core/threading/mutex.h>
25 #include <core/threading/mutex_locker.h>
26 #include <tf/types.h>
27 #include <utils/math/angle.h>
28 #ifdef USE_TIMETRACKER
29 # include <utils/time/tracker.h>
30 #endif
31 #include <utils/time/tracker_macros.h>
32 
33 #include <boost/bind/bind.hpp>
34 #include <cstdio>
35 #include <cstdlib>
36 #include <cstring>
37 #include <unistd.h>
38 
39 using namespace fawkes;
40 
41 #define RECONNECT_INTERVAL 2000
42 
43 /** @class CruizCoreXG1010AcquisitionThread "imu_cruizcore_xg1010.h"
44  * IMU acquisition thread for CruizCore XG1010 gyros.
45  * @author Tim Niemueller
46  */
47 
48 /// @cond OLD_BOOST_COMPAT
49 
50 #if BOOST_VERSION < 104800
51 
52 // The default maximum number of bytes to transfer in a single operation.
53 enum { default_max_transfer_size = 65536 };
54 
55 class transfer_exactly_t
56 {
57 public:
58  typedef std::size_t result_type;
59 
60  explicit transfer_exactly_t(std::size_t size) : size_(size)
61  {
62  }
63 
64  template <typename Error>
65  std::size_t
66  operator()(const Error &err, std::size_t bytes_transferred)
67  {
68  return (!!err || bytes_transferred >= size_)
69  ? 0
70  : (size_ - bytes_transferred < default_max_transfer_size
71  ? size_ - bytes_transferred
72  : std::size_t(default_max_transfer_size));
73  }
74 
75 private:
76  std::size_t size_;
77 };
78 
79 inline transfer_exactly_t
80 transfer_exactly(std::size_t size)
81 {
82  return transfer_exactly_t(size);
83 }
84 #endif
85 
86 /// @endcond
87 
88 /** Constructor.
89  * @param cfg_name short name of configuration group
90  * @param cfg_prefix configuration path prefix
91  * @param continuous true to run continuous, false otherwise
92  */
94  std::string &cfg_prefix,
95  bool continuous)
96 : IMUAcquisitionThread(cfg_name.c_str(), continuous, cfg_name, cfg_prefix),
97  serial_(io_service_),
98  io_service_work_(io_service_),
99  deadline_(io_service_)
100 {
101  set_name("CruizCoreXG1010(%s)", cfg_name.c_str());
102 }
103 
104 void
106 {
107  deadline_.expires_at(boost::posix_time::pos_infin);
108  check_deadline();
109 
110  cfg_serial_ = config->get_string((cfg_prefix_ + "device").c_str());
111  cfg_baud_rate_ = config->get_uint((cfg_prefix_ + "baud_rate").c_str());
112  cfg_freq_ = config->get_uint((cfg_prefix_ + "data_frequency").c_str());
113 
114  if (cfg_freq_ != 25 && cfg_freq_ != 50 && cfg_freq_ != 100) {
115  throw Exception("Invalid data frequency, must be 25, 50, or 100");
116  }
117  if (cfg_baud_rate_ != 115200 && cfg_baud_rate_ != 57600 && cfg_baud_rate_ != 38400
118  && cfg_baud_rate_ != 28800 && cfg_baud_rate_ != 19200 && cfg_baud_rate_ != 9600
119  && cfg_baud_rate_ != 4800) {
120  throw Exception("Invalid baud rate");
121  }
122 
123  if ((cfg_freq_ > 25 && cfg_baud_rate_ < 9600) || (cfg_freq_ > 50 && cfg_baud_rate_ < 19200)) {
124  throw Exception("Baud rate too low for frequency");
125  }
126 
127  // wait up to two expected packets
128  receive_timeout_ = (1000 / cfg_freq_) * 2;
129  //logger->log_debug(name(), "Receive timeout: %u ms", receive_timeout_);
130 
131  // No acceleration data available, set to -1
132  linear_acceleration_[0] = -1.;
133 
134  // from XG1010 data sheet
136 
137  open_device();
138 
139  if (cfg_continuous_)
141 
142 #ifdef USE_TIMETRACKER
143  tt_ = new TimeTracker();
144  tt_loopcount_ = 0;
145  ttc_full_loop_ = tt_->add_class("Full Loop");
146  ttc_read_ = tt_->add_class("Read");
147  ttc_catch_up_ = tt_->add_class("Catch up");
148  ttc_parse_ = tt_->add_class("Parse");
149 #endif
150 }
151 
152 void
154 {
155  close_device();
156  if (cfg_continuous_)
158 #ifdef USE_TIMETRACKER
159  delete tt_;
160 #endif
161 }
162 
163 void
165 {
166  TIMETRACK_START(ttc_full_loop_);
167 
168  if (serial_.is_open()) {
169  // reset data for the case that we timeout or fail
170  angular_velocity_[2] = 0;
171  orientation_[0] = orientation_[1] = orientation_[2] = orientation_[3] = 0.;
172 
173  try {
174  TIMETRACK_START(ttc_read_);
175  deadline_.expires_from_now(boost::posix_time::milliseconds(receive_timeout_));
176 
177  ec_ = boost::asio::error::would_block;
178  bytes_read_ = 0;
179 
180  size_t to_read = CRUIZCORE_XG1010_PACKET_SIZE;
181  // if there is partial data in the buffer we are running behind
182  // read off old packet, catch up later will trigger if we had more data
183  if (input_buffer_.size() > 0) {
184  const size_t bsize = input_buffer_.size();
185  size_t full_frames = bsize / CRUIZCORE_XG1010_PACKET_SIZE;
186  size_t remaining =
187  CRUIZCORE_XG1010_PACKET_SIZE - (bsize - full_frames * CRUIZCORE_XG1010_PACKET_SIZE);
188  to_read = remaining;
189  }
190 
191  //logger->log_debug(name(), "Read %zu bytes", to_read);
192  boost::asio::async_read(serial_,
193  input_buffer_,
194 #if BOOST_VERSION >= 104800
195  boost::asio::transfer_exactly(to_read),
196  (boost::lambda::var(ec_) = boost::lambda::_1,
197  boost::lambda::var(bytes_read_) = boost::lambda::_2));
198 #else
199  transfer_exactly(to_read),
200  boost::bind(&CruizCoreXG1010AcquisitionThread::handle_read,
201  this,
202  boost::asio::placeholders::error,
203  boost::asio::placeholders::bytes_transferred));
204 #endif
205 
206  do
207  io_service_.run_one();
208  while (ec_ == boost::asio::error::would_block);
209 
210  //logger->log_debug(name(), "Done");
211 
212  TIMETRACK_END(ttc_read_);
213 
214  data_mutex_->lock();
215  timestamp_->stamp();
216  data_mutex_->unlock();
217 
218  if (ec_) {
219  if (ec_.value() == boost::system::errc::operation_canceled) {
220  logger->log_error(name(), "Data timeout, will try to reconnect");
221  } else {
222  logger->log_warn(name(), "Data read error: %s\n", ec_.message().c_str());
223  }
224  data_mutex_->lock();
225  new_data_ = true;
226  data_mutex_->unlock();
227  close_device();
228  } else {
229  TIMETRACK_START(ttc_catch_up_);
230  bytes_read_ = 0;
231  bool catch_up = false;
232  size_t read_size = 0;
233  do {
234  ec_ = boost::asio::error::would_block;
235 
236  size_t to_read = CRUIZCORE_XG1010_PACKET_SIZE;
237  if (catch_up) {
238  deadline_.expires_from_now(boost::posix_time::milliseconds(receive_timeout_));
239  size_t full_frames = read_size / CRUIZCORE_XG1010_PACKET_SIZE;
240  size_t remaining = CRUIZCORE_XG1010_PACKET_SIZE
241  - (read_size - full_frames * CRUIZCORE_XG1010_PACKET_SIZE);
242  to_read = remaining;
243  } else {
244  deadline_.expires_from_now(boost::posix_time::microseconds(10));
245  }
246  catch_up = false;
247 
248  bytes_read_ = 0;
249 
250  //logger->log_debug(name(), "Catch up read %zu bytes", to_read);
251  boost::asio::async_read(serial_,
252  input_buffer_,
253 #if BOOST_VERSION >= 104800
254  boost::asio::transfer_exactly(to_read),
255  (boost::lambda::var(ec_) = boost::lambda::_1,
256  boost::lambda::var(bytes_read_) = boost::lambda::_2));
257 #else
258  transfer_exactly(to_read),
259  boost::bind(&CruizCoreXG1010AcquisitionThread::handle_read,
260  this,
261  boost::asio::placeholders::error,
262  boost::asio::placeholders::bytes_transferred));
263 #endif
264 
265  do
266  io_service_.run_one();
267  while (ec_ == boost::asio::error::would_block);
268 
269  if (bytes_read_ > 0) {
270  read_size += bytes_read_;
271  catch_up = (read_size % CRUIZCORE_XG1010_PACKET_SIZE != 0);
272  ec_ = boost::system::error_code();
273  }
274  } while (bytes_read_ > 0);
275  }
276 
277  TIMETRACK_END(ttc_catch_up_);
278 
279  if (ec_ && ec_.value() != boost::system::errc::operation_canceled) {
280  logger->log_warn(name(), "Data read error: %s\n", ec_.message().c_str());
281 
282  data_mutex_->lock();
283  new_data_ = true;
284  data_mutex_->unlock();
285  close_device();
286  } else {
287  if (input_buffer_.size() >= CRUIZCORE_XG1010_PACKET_SIZE) {
288  TIMETRACK_START(ttc_parse_);
289  if (input_buffer_.size() > CRUIZCORE_XG1010_PACKET_SIZE) {
290  input_buffer_.consume(input_buffer_.size() - CRUIZCORE_XG1010_PACKET_SIZE);
291  }
292  std::istream in_stream(&input_buffer_);
293  in_stream.read((char *)in_packet_, CRUIZCORE_XG1010_PACKET_SIZE);
294 
295  /*
296  printf("Packet (%zu): ", bytes_read_);
297  for (size_t i = 0; i < bytes_read_; ++i) {
298  printf("%x ", in_packet_[i] & 0xff);
299  }
300  printf("\n");
301  */
302 
303  try {
304  parse_packet();
305  } catch (Exception &e) {
306  logger->log_warn(name(), e);
307  try {
308  resync();
309  logger->log_info(name(), "Successfully resynced");
310  } catch (Exception &e) {
311  logger->log_warn(name(), "Resync failed, trying to re-open");
312  close_device();
313  }
314  }
315  TIMETRACK_END(ttc_parse_);
316  } else {
317  logger->log_warn(name(),
318  "*** INVALID number of bytes in buffer: %zu\n",
319  input_buffer_.size());
320  }
321  }
322  } catch (boost::system::system_error &e) {
323  if (e.code() == boost::asio::error::eof) {
324  close_device();
325  logger->log_warn(name(), "CruizCoreXG1010 connection lost, trying to re-open");
326  } else {
327  logger->log_warn(name(), "CruizCore failed read: %s", e.what());
328  }
329  }
330  } else {
331  try {
332  open_device();
333  logger->log_warn(name(), "Reconnected to device");
334  } catch (Exception &e) {
335  // ignore, keep trying
336  usleep(RECONNECT_INTERVAL * 1000);
337  }
338  }
339 
340  if (cfg_continuous_)
342 
343  yield();
344  TIMETRACK_END(ttc_full_loop_);
345 #ifdef USE_TIMETRACKER
346  if (++tt_loopcount_ >= 50) {
347  tt_loopcount_ = 0;
348  tt_->print_to_stdout();
349  }
350 #endif
351 }
352 
353 void
354 CruizCoreXG1010AcquisitionThread::open_device()
355 {
356  try {
357  input_buffer_.consume(input_buffer_.size());
358 
359  serial_.open(cfg_serial_);
360  // according to CruizCore R1050K (sensor in XG1010) technical manual
361  serial_.set_option(
362  boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::one));
363  serial_.set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::none));
364  serial_.set_option(boost::asio::serial_port::baud_rate(cfg_baud_rate_));
365 
366  send_init_packet(/* enable transfer */ true);
367 
368  resync();
369  } catch (boost::system::system_error &e) {
370  throw Exception("CruizCore-XG1010 failed I/O: %s", e.what());
371  }
372 }
373 
374 void
375 CruizCoreXG1010AcquisitionThread::send_init_packet(bool enable_transfer)
376 {
377  /* Format according to Technical Manual of R1050K for XG1010
378  Field Command Separator Example
379  INIT $MIA COMMA (,) $MIA,
380  FORMAT F, I or A COMMA (,) I,
381  BAUD RATE B,BAUDRATE COMMA (,) B,115200,
382  OUTPUT RATE R COMMA (,) R,100,
383  TYPE D or R COMMA (,) D,
384  OUTPUT Y or N COMMA (,) Y,
385  FLASH Y or N COMMA (,) Y,
386  CHECKSUM SUM of COMMAND ASTERISK(*) *C4
387  */
388  char *cmd_packet;
389  if (asprintf(&cmd_packet,
390  "$MIA,I,B,%u,R,%u,D,%s,N* ",
391  cfg_baud_rate_,
392  cfg_freq_,
393  enable_transfer ? "Y" : "N")
394  == -1) {
395  throw Exception("Failed to create command packet");
396  }
397 
398  size_t cmd_packet_len = strlen(cmd_packet);
399 
400  // calculate checksum
401  unsigned int checksum = 0;
402  for (size_t i = 1; i < cmd_packet_len - 3; ++i)
403  checksum += cmd_packet[i];
404  checksum &= 0xFF;
405 
406  char checksum_str[3];
407  snprintf(checksum_str, 3, "%X", checksum);
408  cmd_packet[cmd_packet_len - 2] = checksum_str[0];
409  cmd_packet[cmd_packet_len - 1] = checksum_str[1];
410 
411  std::string cmd_packet_s(cmd_packet, cmd_packet_len);
412  free(cmd_packet);
413 
414  logger->log_debug(name(), "Sending init packet: %s", cmd_packet_s.c_str());
415 
416  boost::asio::write(serial_, boost::asio::buffer(cmd_packet_s.c_str(), cmd_packet_len));
417 }
418 
419 void
420 CruizCoreXG1010AcquisitionThread::resync()
421 {
422  /*
423  // the idea here would be to:
424  // 1. stop data transfer
425  // 2. read all remaining data, i.e. flush serial line
426  // 3. start data transfer again
427  // 4. synchronize with packet stream
428  // the only problem being that once the transfer has been stopped it
429  // cannot be enabled again, this seems to be a bug in the CruizCore XG1010.
430 
431  // stop transfer and sniff off any data still arriving
432  send_init_packet(/ enable transfer / false);
433  try {
434  do {
435  ec_ = boost::asio::error::would_block;
436  deadline_.expires_from_now(boost::posix_time::milliseconds(receive_timeout_));
437  bytes_read_ = 0;
438 
439  boost::asio::async_read(serial_, input_buffer_,
440 #if BOOST_VERSION >= 104800
441  (boost::lambda::var(ec_) = boost::lambda::_1,
442  boost::lambda::var(bytes_read_) = boost::lambda::_2));
443 #else
444  boost::bind(
445  &CruizCoreXG1010AcquisitionThread::handle_read,
446  this,
447  boost::asio::placeholders::error,
448  boost::asio::placeholders::bytes_transferred
449  ));
450 #endif
451 
452  do io_service_.run_one(); while (ec_ == boost::asio::error::would_block);
453 
454  input_buffer_.consume(input_buffer_.size());
455 
456  logger->log_warn(name(), "EC: %s\n", ec_.message().c_str());
457  } while (!ec_ && bytes_read_ > 0);
458  } catch (boost::system::system_error &e) {
459  // ignore, just assume done, if there really is an error we'll
460  // catch it later on
461  }
462 
463  send_init_packet(/ enable transfer / true);
464  */
465 
466 #if BOOST_VERSION >= 104700
467  tcflush(serial_.lowest_layer().native_handle(), TCIOFLUSH);
468 #else
469  tcflush(serial_.lowest_layer().native(), TCIOFLUSH);
470 #endif
471 
472  // Try 10 times
473  const int NUM_TRIES = 10;
474  for (int t = 1; t <= NUM_TRIES; ++t) {
475  try {
476  ec_ = boost::asio::error::would_block;
477  bytes_read_ = 0;
478 
479  deadline_.expires_from_now(boost::posix_time::milliseconds(receive_timeout_ * 10));
480  boost::asio::async_read_until(serial_,
481  input_buffer_,
482  std::string("\xff\xff"),
483 #if BOOST_VERSION >= 104800
484  (boost::lambda::var(ec_) = boost::lambda::_1,
485  boost::lambda::var(bytes_read_) = boost::lambda::_2));
486 #else
487  boost::bind(&CruizCoreXG1010AcquisitionThread::handle_read,
488  this,
489  boost::asio::placeholders::error,
490  boost::asio::placeholders::bytes_transferred));
491 #endif
492 
493  do
494  io_service_.run_one();
495  while (ec_ == boost::asio::error::would_block);
496 
497  if (ec_) {
498  if (ec_.value() == boost::system::errc::operation_canceled) {
499  throw Exception("Timeout (1) on initial synchronization");
500  } else {
501  throw Exception("Error (1) on initial synchronization: %s", ec_.message().c_str());
502  }
503  }
504 
505  input_buffer_.consume(bytes_read_ - 2);
506 
507  deadline_.expires_from_now(boost::posix_time::milliseconds(receive_timeout_));
508  ec_ = boost::asio::error::would_block;
509  bytes_read_ = 0;
510  boost::asio::async_read(serial_,
511  input_buffer_,
512 #if BOOST_VERSION >= 104800
513  boost::asio::transfer_exactly(CRUIZCORE_XG1010_PACKET_SIZE - 2),
514  (boost::lambda::var(ec_) = boost::lambda::_1,
515  boost::lambda::var(bytes_read_) = boost::lambda::_2));
516 #else
517  transfer_exactly(CRUIZCORE_XG1010_PACKET_SIZE - 2),
518  boost::bind(&CruizCoreXG1010AcquisitionThread::handle_read,
519  this,
520  boost::asio::placeholders::error,
521  boost::asio::placeholders::bytes_transferred));
522 #endif
523 
524  do
525  io_service_.run_one();
526  while (ec_ == boost::asio::error::would_block);
527 
528  if (ec_) {
529  if (ec_.value() == boost::system::errc::operation_canceled) {
530  throw Exception("Timeout (2) on initial synchronization");
531  } else {
532  throw Exception("Error (2) on initial synchronization: %s", ec_.message().c_str());
533  }
534  }
535 
536  std::istream in_stream(&input_buffer_);
537  in_stream.read((char *)in_packet_, CRUIZCORE_XG1010_PACKET_SIZE);
538 
539  parse_packet();
540  } catch (Exception &e) {
541  if (t == NUM_TRIES) {
542  e.append("Resync failed after %d tries", NUM_TRIES);
543  throw;
544  }
545  //else {
546  //logger->log_warn(name(), "Resync retry %i failed, retrying %i more time%s",
547  // t, NUM_TRIES - t, (NUM_TRIES - t == 1) ? "" : "s");
548  //}
549  }
550  }
551  deadline_.expires_at(boost::posix_time::pos_infin);
552 }
553 
554 void
555 CruizCoreXG1010AcquisitionThread::close_device()
556 {
557  serial_.close();
558 }
559 
560 void
561 CruizCoreXG1010AcquisitionThread::parse_packet()
562 {
563  /*
564  printf("Packet: ");
565  for (size_t i = 0; i < CRUIZCORE_XG1010_PACKET_SIZE; ++i) {
566  printf("%x ", in_packet_[i] & 0xff);
567  }
568  printf("\n");
569  */
570 
571  if (in_packet_[0] != 0xFF || in_packet_[1] != 0xFF) {
572  throw Exception("Failed to parse packet, invalid header");
573  }
574 
575  short int rate = (in_packet_[2] & 0xFF) | ((in_packet_[3] << 8) & 0xFF00);
576  short int angle = (in_packet_[4] & 0xFF) | ((in_packet_[5] << 8) & 0XFF00);
577 
578  int checksum = 0xffff + rate + angle;
579  if (((unsigned char)(checksum & 0xFF) != in_packet_[6])
580  || ((unsigned char)((checksum >> 8) & 0xFF) != in_packet_[7])) {
581  /*
582  printf("Packet: ");
583  for (size_t i = 0; i < CRUIZCORE_XG1010_PACKET_SIZE; ++i) {
584  printf("%x ", in_packet_[i] & 0xff);
585  }
586  printf("\n");
587  */
588  throw Exception("Failed to parse packet, checksum mismatch");
589  }
590 
591  data_mutex_->lock();
592  new_data_ = true;
593 
594  angular_velocity_[2] = -deg2rad(rate / 100.f);
595 
596  tf::Quaternion q = tf::create_quaternion_from_yaw(-deg2rad(angle / 100.f));
597  orientation_[0] = q.x();
598  orientation_[1] = q.y();
599  orientation_[2] = q.z();
600  orientation_[3] = q.w();
601 
602  data_mutex_->unlock();
603 }
604 
605 /** Check whether the deadline has passed.
606  * We compare the deadline against
607  * the current time since a new asynchronous operation may have moved the
608  * deadline before this actor had a chance to run.
609  */
610 void
611 CruizCoreXG1010AcquisitionThread::check_deadline()
612 {
613  if (deadline_.expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
614  serial_.cancel();
615  deadline_.expires_at(boost::posix_time::pos_infin);
616  }
617 
618 #if BOOST_VERSION >= 104800
619  deadline_.async_wait(
620  boost::lambda::bind(&CruizCoreXG1010AcquisitionThread::check_deadline, this));
621 #else
622  deadline_.async_wait(boost::bind(&CruizCoreXG1010AcquisitionThread::check_deadline, this));
623 #endif
624 }
virtual void finalize()
Finalize the thread.
CruizCoreXG1010AcquisitionThread(std::string &cfg_name, std::string &cfg_prefix, bool continuous)
Constructor.
virtual void init()
Initialize the thread.
virtual void loop()
Code to execute in the thread.
IMU acqusition thread.
float linear_acceleration_[3]
Pre-allocated linear acceleration as array, 3 entries ordered (x,y,z).
fawkes::Mutex * data_mutex_
Lock while writing to distances or echoes array or marking new data.
double angular_velocity_covariance_[9]
Pre-allocated angular velocity covariance, row major matrix ordered x, y, z.
virtual void loop()
Code to execute in the thread.
float orientation_[4]
Pre-allocated orientation quaternion as array, 4 entries ordered (x,y,z,w).
virtual void finalize()
Finalize the thread.
std::string cfg_prefix_
Configuration path prefix.
bool cfg_continuous_
True if running continuous.
float angular_velocity_[3]
Pre-allocated angular velocities as array, 3 entries ordered (x,y,z).
bool new_data_
Set to true in your loop if new data is available.
virtual void init()
Initialize the thread.
fawkes::Time * timestamp_
Time when the most recent data was received.
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
virtual unsigned int get_uint(const char *path)=0
Get value from configuration which is of type unsigned int.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
Base class for exceptions in Fawkes.
Definition: exception.h:36
void append(const char *format,...) noexcept
Append messages to the message list.
Definition: exception.cpp:333
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
void lock()
Lock this mutex.
Definition: mutex.cpp:87
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
const char * name() const
Get name of thread.
Definition: thread.h:100
void yield()
Yield the processor to another thread or process.
Definition: thread.cpp:883
void set_name(const char *format,...)
Set name of thread.
Definition: thread.cpp:748
Time tracking utility.
Definition: tracker.h:37
Time & stamp()
Set this time to the current time.
Definition: time.cpp:704
Fawkes library namespace.
float deg2rad(float deg)
Convert an angle given in degrees to radians.
Definition: angle.h:36