22 #include <boost/assert.hpp> 31 #include <sys/ioctl.h> 37 #define kDefaultTimeout (300) 38 #define kBufferSize (10) // keep this small, or 1000Hz is not attainable 40 #define u8(x) static_cast<uint8_t>((x)) 42 #define COMMAND_CLASS_BASE u8(0x01) 43 #define COMMAND_CLASS_3DM u8(0x0C) 44 #define COMMAND_CLASS_FILTER u8(0x0D) 46 #define DATA_CLASS_IMU u8(0x80) 47 #define DATA_CLASS_FILTER u8(0x82) 49 #define FUNCTION_APPLY u8(0x01) 51 #define SELECTOR_IMU u8(0x01) 52 #define SELECTOR_FILTER u8(0x03) 55 #define DEVICE_PING u8(0x01) 56 #define DEVICE_IDLE u8(0x02) 57 #define DEVICE_RESUME u8(0x06) 60 #define COMMAND_GET_DEVICE_INFO u8(0x03) 61 #define COMMAND_GET_IMU_BASE_RATE u8(0x06) 62 #define COMMAND_GET_FILTER_BASE_RATE u8(0x0B) 63 #define COMMAND_IMU_MESSAGE_FORMAT u8(0x08) 64 #define COMAMND_FILTER_MESSAGE_FORMAT u8(0x0A) 65 #define COMMAND_ENABLE_DATA_STREAM u8(0x11) 66 #define COMMAND_FILTER_CONTROL_FLAGS u8(0x14) 67 #define COMMAND_UART_BAUD_RATE u8(0x40) 68 #define COMMAND_SET_HARD_IRON u8(0x3A) 69 #define COMMAND_SET_SOFT_IRON u8(0x3B) 70 #define COMMAND_ENABLE_MEASUREMENTS u8(0x41) 71 #define COMMAND_DEVICE_STATUS u8(0x64) 74 #define FIELD_QUATERNION u8(0x03) 75 #define FIELD_ACCELEROMETER u8(0x04) 76 #define FIELD_GYROSCOPE u8(0x05) 77 #define FIELD_GYRO_BIAS u8(0x06) 78 #define FIELD_MAGNETOMETER u8(0x06) 79 #define FIELD_ANGLE_UNCERTAINTY u8(0x0A) 80 #define FIELD_BIAS_UNCERTAINTY u8(0x0B) 81 #define FIELD_BAROMETER u8(0x17) 82 #define FIELD_DEVICE_INFO u8(0x81) 83 #define FIELD_IMU_BASERATE u8(0x83) 84 #define FIELD_FILTER_BASERATE u8(0x8A) 85 #define FIELD_STATUS_REPORT u8(0x90) 86 #define FIELD_ACK_OR_NACK u8(0xF1) 91 static inline std::string
ltrim(std::string s) {
93 std::find_if(s.begin(), s.end(),
94 std::not1(std::ptr_fun<int, int>(std::isspace))));
100 #ifdef HOST_LITTLE_ENDIAN 101 for (
size_t i = 0; i < sz / 2; i++) {
102 std::swap(buffer[i], buffer[sz - i - 1]);
109 template <
typename T>
size_t encode(uint8_t *buffer,
const T &t) {
110 static_assert(std::is_fundamental<T>::value,
111 "Only fundamental types may be encoded");
113 const size_t szT =
sizeof(T);
120 to_device_order<szT>(bytes);
123 for (
size_t i = 0; i < szT; i++) {
124 buffer[i] = bytes[i];
130 template <
typename T,
typename... Ts>
131 size_t encode(uint8_t *buffer,
const T &t,
const Ts &... ts) {
132 const size_t sz =
encode(buffer, t);
134 return sz +
encode(buffer +
sizeof(T), ts...);
137 template <
typename T>
void decode(
const uint8_t *buffer,
size_t count,
139 const size_t szT =
sizeof(T);
140 static_assert(std::is_fundamental<T>::value,
141 "Only fundamental types may be decoded");
147 for (
size_t i = 0; i < count; i++) {
148 for (
size_t j = 0; j < szT; j++) {
149 bytes[j] = buffer[j];
151 to_device_order<szT>(&bytes[0]);
173 assert(p_.length <
sizeof(p_.payload) - 2);
175 p_.payload[fs_+1] = desc;
180 template <
typename ...Ts>
183 p_.length +=
encode(p_.payload+p_.length, args...);
188 p_.payload[fs_] = p_.length - fs_;
208 if (fs_ >
sizeof(p_.payload)-2) {
211 if (p_.payload[fs_] == 0) {
214 return p_.payload[fs_ + 1];
218 assert(fs_ <
sizeof(p_.payload));
219 return p_.payload[fs_];
223 const int desc = fieldDescriptor();
231 for (
int d; (
d = fieldDescriptor()) > 0; advance()) {
232 if (
d == static_cast<int>(field)) {
240 fs_ += p_.payload[fs_];
244 template <
typename T>
246 BOOST_VERIFY(fs_ + pos_ +
sizeof(T) * count <=
sizeof(p_.payload));
247 decode(&p_.payload[fs_ + pos_], count, output);
248 pos_ +=
sizeof(T) * count;
268 const uint8_t sentField = command.
payload[1];
281 if (cmd == sentField) {
292 uint8_t byte1 = 0, byte2 = 0;
294 #define add_byte(x) \ 303 for (
size_t i = 0; i <
length; i++) {
308 checksum = (
static_cast<uint16_t
>(byte1) << 8) +
static_cast<uint16_t
>(byte2);
309 #ifdef HOST_LITTLE_ENDIAN 323 std::stringstream ss;
325 ss <<
"SyncMSB: " <<
static_cast<int>(
syncMSB) <<
"\n";
326 ss <<
"SyncLSB: " <<
static_cast<int>(
syncLSB) <<
"\n";
327 ss <<
"Descriptor: " <<
static_cast<int>(
descriptor) <<
"\n";
328 ss <<
"Length: " <<
static_cast<int>(
length) <<
"\n";
331 ss << static_cast<int>(
payload[
s]) <<
" ";
333 ss <<
"\nCheck MSB: " <<
static_cast<int>(
checkMSB) <<
"\n";
334 ss <<
"Check LSB: " <<
static_cast<int>(
checkLSB);
339 std::map<std::string, std::string> map;
340 map[
"Firmware version"] = std::to_string(firmwareVersion);
341 map[
"Model name"] = modelName;
343 map[
"Serial number"] = serialNumber;
344 map[
"Device options"] = deviceOptions;
350 std::map<std::string, unsigned int> map;
376 std::runtime_error(generateString(p, code)) {}
379 std::stringstream ss;
380 ss <<
"Received NACK with error code " << std::hex << static_cast<int>(code);
381 ss <<
". Command Packet:\n" << p.
toString();
390 std::stringstream ss;
391 ss <<
"Timed-out while " << ((write) ?
"writing" :
"reading") <<
". ";
392 ss <<
"Time-out limit is " << to <<
"ms.";
409 throw std::runtime_error(
"Socket is already open");
412 const char *path =
device_.c_str();
413 fd_ = ::open(path, O_RDWR | O_NOCTTY | O_NDELAY);
417 throw std::runtime_error(
"Failed to open device : " +
device_);
421 if (fcntl(
fd_, F_SETFL, FNONBLOCK) < 0) {
426 struct termios toptions;
427 if (tcgetattr(
fd_, &toptions) < 0) {
433 cfsetispeed(&toptions, B115200);
434 cfsetospeed(&toptions, B115200);
436 toptions.c_cflag &= ~PARENB;
437 toptions.c_cflag &= ~CSTOPB;
438 toptions.c_cflag &= ~CSIZE;
439 toptions.c_cflag |= HUPCL;
440 toptions.c_cflag |= CS8;
442 toptions.c_cflag &= ~CRTSCTS;
447 toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
448 toptions.c_iflag &= ~(INLCR|ICRNL);
454 toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
455 toptions.c_oflag &= ~OPOST;
456 toptions.c_oflag &= ~(ONLCR|OCRNL);
458 toptions.c_cc[VMIN] = 0;
459 toptions.c_cc[VTIME] = 0;
462 if (tcsetattr(
fd_, TCSAFLUSH, &toptions) < 0) {
478 struct termios toptions;
479 if (tcgetattr(
fd_, &toptions) < 0) {
504 throw std::invalid_argument(
"Invalid Baud Rate" );
508 cfsetispeed(&toptions, speed);
509 cfsetospeed(&toptions, speed);
511 if (tcsetattr(
fd_, TCSAFLUSH, &toptions) < 0) {
530 const size_t num_rates = 6;
531 unsigned int rates[num_rates] = {
532 9600, 19200, 115200, 230400, 460800, 921600
535 if (!std::binary_search(rates, rates + num_rates, baud)) {
537 std::stringstream ss;
538 ss <<
"Baud rate unsupported: " << baud;
539 throw std::invalid_argument(ss.str());
551 bool foundRate =
false;
552 for (i = 0; i < num_rates; i++) {
554 std::cout <<
"Switching to baud rate " << rates[i] << std::endl;
561 std::cout <<
"Switched baud rate to " << rates[i] << std::endl;
562 std::cout <<
"Sending a ping packet.\n" << std::flush;
571 std::cout <<
"Timed out waiting for ping response.\n" << std::flush;
576 std::cout <<
"IMU returned error code for ping.\n" << std::flush;
582 std::cout <<
"Found correct baudrate.\n" << std::flush;
591 throw std::runtime_error(
"Failed to reach device " +
device_);
600 encoder.
append(static_cast<uint32_t>(baud));
602 assert(comm.
length == 0x07);
608 std::cout <<
"Instructing device to change to " << baud << std::endl
612 }
catch (std::exception& e) {
613 std::stringstream ss;
614 ss <<
"Device rejected baud rate " << baud <<
".\n";
616 throw std::runtime_error(ss.str());
627 }
catch (std::exception& e) {
628 std::string err(
"Device did not respond to ping.\n");
630 throw std::runtime_error(err);
680 decoder.
extract(
sizeof(buffer), &buffer[0]);
682 decoder.
extract(
sizeof(buffer), &buffer[0]);
684 decoder.
extract(
sizeof(buffer), &buffer[0]);
686 decoder.
extract(
sizeof(buffer), &buffer[0]);
688 decoder.
extract(
sizeof(buffer), &buffer[0]);
723 encoder.
append(static_cast<uint16_t>(6234));
742 const std::bitset<4>& sources) {
751 assert(
sizeof(fieldDescs) == sources.size());
752 std::vector<uint8_t>
fields;
754 for (
size_t i=0; i < sources.size(); i++) {
756 fields.push_back(fieldDescs[i]);
762 for (
const uint8_t& field : fields) {
763 encoder.
append(field, decimation);
779 assert(
sizeof(fieldDescs) == sources.size());
780 std::vector<uint8_t>
fields;
782 for (
size_t i=0; i < sources.size(); i++) {
784 fields.push_back(fieldDescs[i]);
791 for (
const uint8_t& field : fields) {
792 encoder.
append(field, decimation);
820 uint16_t flag = 0xFFFE;
846 for (
int i=0; i < 9; i++) {
847 encoder.
append(matrix[i]);
899 int rPoll = poll(&p, 1, to);
904 }
else if (amt == 0) {
908 }
else if (rPoll == 0) {
912 if (errno == EAGAIN || errno == EINTR) {
975 std::cout <<
"Warning: Dropped packet with mismatched checksum\n" 978 std::cout <<
"Expected " << std::hex <<
980 static_cast<int>(sum) << std::endl;
982 std::cout <<
"Queue content: \n";
983 for (
const uint8_t& q :
queue_) {
984 std::cout << static_cast<int>(q) <<
" ";
986 std::cout <<
"\n" << std::flush;
1006 std::stringstream ss;
1007 ss <<
"Handling read : " << std::hex;
1008 for (
size_t i = 0; i < bytes_transferred; i++) {
1010 ss << static_cast<int>(
buffer_[i]) <<
" ";
1014 std::cout << ss.str() << std::flush;
1020 const size_t clear =
handleByte(head, found);
1022 for (
size_t i=0; i < clear; i++) {
1064 std::stringstream ss;
1065 ss <<
"Unsupported field in IMU packet: " << std::hex <<
d;
1066 throw std::runtime_error(ss.str());
1098 std::stringstream ss;
1099 ss <<
"Unsupported field in filter packet: " << std::hex <<
d;
1100 throw std::runtime_error(ss.str());
1112 uint8_t cmd_code[2];
1113 decoder.
extract(2, &cmd_code[0]);
1114 if (cmd_code[1] != 0) {
1116 std::cout <<
"Received NACK packet (class, command, code): ";
1118 std::cout << static_cast<int>(cmd_code[0]) <<
", ";
1119 std::cout << static_cast<int>(cmd_code[1]) <<
"\n" << std::flush;
1127 using namespace std::chrono;
1130 std::vector<uint8_t> v;
1137 for (
size_t i = 0; i < p.
length; i++) {
1143 auto tstart = high_resolution_clock::now();
1144 auto tstop = tstart + milliseconds(to);
1147 while (written < v.size()) {
1148 const ssize_t amt = ::write(
fd_, &v[written], v.size() - written);
1151 }
else if (amt < 0) {
1152 if (errno == EAGAIN || errno == EINTR) {
1159 if (tstop < high_resolution_clock::now()) {
1164 return static_cast<int>(written);
1171 }
else if (wrote == 0) {
1178 const auto tstart = std::chrono::high_resolution_clock::now();
1179 const auto tend = tstart + std::chrono::milliseconds(to);
1181 while (std::chrono::high_resolution_clock::now() <= tend) {
1189 }
else if (ack > 0) {
1193 std::cout <<
"Not interested in this [N]ACK!\n";
1198 }
else if (resp < 0) {
1205 std::cout <<
"Timed out reading response to:\n";
1206 std::cout << command.
toString() << std::endl << std::flush;
1214 std::cout <<
"Sending command:\n";
1215 std::cout << p.
toString() << std::endl;
#define COMMAND_SET_HARD_IRON
void setIMUDataRate(uint16_t decimation, const std::bitset< 4 > &sources)
setIMUDataRate Set imu data rate for different sources.
#define FIELD_IMU_BASERATE
void extract(size_t count, T *output)
command_error(const Packet &p, uint8_t code)
uint32_t imuPacketsDropped
std::map< std::string, std::string > toMap() const
Device options (range of the sensor)
void getDeviceInfo(Imu::Info &info)
getDeviceInfo Get hardware information about the device.
#define COMMAND_IMU_MESSAGE_FORMAT
bool advanceTo(uint8_t field)
uint32_t usbNumReadOverruns
command_error Generated when device replies with NACK.
Tool for decoding packets by iterating through fields.
static constexpr uint8_t kSyncLSB
void setHardIronOffset(float offset[3])
setHardIronOffset Set the hard-iron bias vector for the magnetometer.
std::string deviceOptions
Lot number - appears to be unused.
std::string modelNumber
Model name.
#define COMMAND_UART_BAUD_RATE
uint32_t totalIMUMessages
void runOnce()
runOnce Poll for input and read packets if available.
void connect()
connect Open a file descriptor to the serial device.
#define COMMAND_CLASS_FILTER
std::string modelName
Firmware version.
static constexpr uint8_t kSyncLSB
uint32_t usbNumWriteOverruns
static constexpr uint8_t kSyncMSB
#define FIELD_ACCELEROMETER
int fieldDescriptor() const
uint32_t imuPacketsDropped
int pollInput(unsigned int to)
int writePacket(const Packet &p, unsigned int to)
void enableFilterStream(bool enabled)
enableFilterStream Enable/disable streaming of estimation filter data.
std::map< std::string, unsigned int > toMap() const
Convert to map of human readable strings and integers.
void append(const Ts &...args)
static constexpr uint8_t kSyncMSB
std::vector< uint8_t > buffer_
io_error Generated when a low-level IO command fails.
void enableBiasEstimation(bool enabled)
enableBiasEstimation Enable gyroscope bias estimation
#define COMMAND_SET_SOFT_IRON
bool fieldIsAckOrNack() const
const std::string device_
void calcChecksum()
Calculate the packet checksum. Sets the checksum variable.
DiagnosticFields struct (See 3DM documentation for these fields)
bool isIMUData() const
True if this packet corresponds to an imu data message.
uint16_t biasUncertaintyStatus
uint16_t quaternionStatus
#define DATA_CLASS_FILTER
#define COMAMND_FILTER_MESSAGE_FORMAT
timeout_error(bool write, unsigned int to)
#define FIELD_DEVICE_INFO
Tool for encoding packets more easily by simply appending fields.
void getDiagnosticInfo(Imu::DiagnosticFields &fields)
getDiagnosticInfo Get diagnostic information from the IMU.
Imu::DiagnosticFields fields
void sendPacket(const Packet &p, unsigned int to)
std::deque< uint8_t > queue_
std::string generateString(bool write, unsigned int to)
#define COMMAND_GET_FILTER_BASE_RATE
std::function< void(const Imu::FilterData &)> filterDataCallback_
Called with IMU data is ready.
#define FIELD_ACK_OR_NACK
FilterData Estimator readings produced by the sensor.
#define COMMAND_DEVICE_STATUS
#define COMMAND_ENABLE_DATA_STREAM
uint8_t filterStreamEnabled
std::string lotNumber
Serial number.
#define FIELD_BIAS_UNCERTAINTY
uint32_t comNumReadOverruns
#define COMMAND_CLASS_BASE
enum imu_3dm_gx4::Imu::@0 state_
Called when filter data is ready.
bool termiosBaudRate(unsigned int baud)
PacketDecoder(const Imu::Packet &p)
virtual ~Imu()
~Imu Destructor
int ackErrorCodeFor(const Packet &command) const
Extract the ACK code from this packet.
IMUData IMU readings produced by the sensor.
void disconnect()
disconnect Close the file descriptor, sending the IDLE command first.
void getFilterDataBaseRate(uint16_t &baseRate)
getFilterDataBaseRate Get the filter data base rate (should be 500)
timeout_error Generated when read or write times out, usually indicates device hang up...
PacketEncoder(Imu::Packet &p)
void ping()
ping Ping the device.
std::string toString() const
Make a 'human-readable' version of the packet.
uint16_t angleUncertaintyStatus
std::function< void(const Imu::IMUData &)> imuDataCallback_
#define COMMAND_CLASS_3DM
#define COMMAND_GET_DEVICE_INFO
void receiveResponse(const Packet &command, unsigned int to)
bool isFilterData() const
True if this packet corresponds to a filter data message.
std::size_t handleByte(const uint8_t &byte, bool &found)
#define FIELD_MAGNETOMETER
void beginField(uint8_t desc)
void setFilterDataCallback(const std::function< void(const Imu::FilterData &)> &)
Set the onboard filter data callback.
static constexpr uint8_t kHeaderLength
void decode(const uint8_t *buffer, size_t count, T *output)
std::string generateString(const Packet &p, uint8_t code)
uint32_t filterPacketsDropped
void setSoftIronMatrix(float matrix[9])
setSoftIronMatrix Set the soft-iron matrix for the magnetometer.
void setFilterDataRate(uint16_t decimation, const std::bitset< 4 > &sources)
setFilterDataRate Set estimator data rate for different sources.
uint32_t numIMUParseErrors
void resume()
resume Resume the device.
void to_device_order< 1 >(uint8_t buffer[1])
void getIMUDataBaseRate(uint16_t &baseRate)
getIMUDataBaseRate Get the imu data base rate (should be 1000)
#define COMMAND_GET_IMU_BASE_RATE
void setIMUDataCallback(const std::function< void(const Imu::IMUData &)> &)
Set the IMU data callback.
#define FIELD_STATUS_REPORT
void enableMeasurements(bool accel, bool magnetometer)
enableMeasurements Set which measurements to enable in the filter
#define COMMAND_FILTER_CONTROL_FLAGS
void selectBaudRate(unsigned int baud)
selectBaudRate Select baud rate.
static std::string ltrim(std::string s)
void enableIMUStream(bool enabled)
enableIMUStream Enable/disable streaming of IMU data
void sendCommand(const Packet &p, bool readReply=true)
Imu(const std::string &device, bool verbose)
Imu Constructor.
#define FIELD_ANGLE_UNCERTAINTY
#define COMMAND_ENABLE_MEASUREMENTS
size_t encode(uint8_t *buffer, const T &t)
Info Structure generated by the getDeviceInfo command.
float angleUncertainty[3]
void idle(bool needReply=true)
idle Switch the device to idle mode.
Packet(uint8_t desc=0)
Constructor.
std::string serialNumber
Model number.
void to_device_order(uint8_t buffer[sz])