DUDS
Distributed Update of Data from Something
LSM9DS1.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2017 Jeff Jackowski
9  */
13 #include <duds/data/Constants.hpp>
14 #include <thread>
15 
16 #include <iostream> // for debugging
17 
18 namespace duds { namespace hardware { namespace devices { namespace instruments {
19 
25 namespace LSM9DS1_internal {
26 
33  AgrDeviceId = 0xF, // stores 0x68
37  AgrTemp = 0x15,
43  AgrGyroSampleY = 0x1A,
44  AgrGyroSampleZ = 0x1C,
45  AgrGyroConfig4 = 0x1E, // don't change its contents
56  AgrAccelSampleY = 0x2A,
57  AgrAccelSampleZ = 0x2C,
58  AgrFifoConfig = 0x2E,
60 };
61 
67 enum MagRegs {
71  MagDeviceId = 0xF, // stores 0x3D
72  MagConfig1 = 0x20,
77  MagStatus = 0x27,
79  MagSampleY = 0x2A,
80  MagSampleZ = 0x2C
81 };
82 
87 static const std::map<float, int> AccelDataRateVals = {
88  { 0, 0 }, // use to suspend sampling
89  { 10.0, 1 },
90  { 50.0, 2 },
91  { 119.0, 3 },
92  { 238.0, 4 },
93  { 476.0, 5 },
94  { 952.0, 6 }
95 };
96 
101 static const std::map<float, int> AccelGyroDataRateVals = {
102  { 0, 0 }, // use to suspend sampling
103  { 14.9, 1 },
104  { 59.5, 2 },
105  { 119.0, 3 },
106  { 238.0, 4 },
107  { 476.0, 5 },
108  { 952.0, 6 }
109 };
110 
114 static const std::map<float, int> MagDataRateVals = {
115  { 0.625, 0 }, // low power (device) mode always uses this rate
116  { 1.25, 1 },
117  { 2.5, 2 },
118  { 5.0, 3 },
119  { 10.0, 4 },
120  { 20.0, 5 },
121  { 40.0, 6 },
122  { 80.0, 7 }
123 };
124 
140 static bool matchDataRate(
141  std::int8_t &destVal,
142  float &actualRate,
143  float reqRate,
144  const std::map<float, int> &rateMap
145 ) {
146  std::map<float, int>::const_iterator dri = rateMap.lower_bound(reqRate);
147  if (dri == rateMap.cend()) {
148  return false;
149  }
150  destVal = dri->second;
151  actualRate = dri->first;
152  return true;
153 }
154 
155 // documentation uses milli-G; converted here to m/(s*s)
156 static const double AccelScaleToUnits[] = { // also resolution
161 };
162 
163 // documentation uses millidegrees per second; convert to radians per second
164 static const double GyroScaleToUnits[] = { // also resolution
165  8.75 * 1e-3 * M_PI / 180.0,
166  17.5 * 1e-3 * M_PI / 180.0,
167  0, // bogus; index not used by the device
168  70.0 * 1e-3 * M_PI / 180.0
169 };
170 
171 // documentation uses milli(?)gauss; converted here to Tesla
172 static const double MagScaleToUnits[] = { // also resolution
173  0.14 * 1e-7,
174  0.29 * 1e-7,
175  0.43 * 1e-7,
176  0.58 * 1e-7
177 };
178 
179 }
180 
181 using namespace LSM9DS1_internal;
182 
183 // Device uses little-endian data, some unaligned on magnetometer,
184 // to address value
185 
186 // ----- Accelerometer and gyroscope -----
187 
189  std::unique_ptr<duds::hardware::interface::I2c> &i2c
190 ) : agcom(std::move(i2c)), agdatarate(0)
191 {
192  try {
194  // verify ID
195  firstcon.addOutputVector() << (std::int8_t)AgrDeviceId;
196  firstcon.addInputVector(1);
197  agcom->converse(firstcon);
199  std::uint8_t id;
200  ex >> id;
201  if (id != 0x68) {
204  );
205  }
206  firstcon.clear();
207  // reset
208  firstcon.addOutputVector() << (std::int8_t)AgrConfig8 <<
209  // Set the BOOT and SW_RESET flags, since they both seem to be a
210  // reset of the device, and the docs aren't specific on the
211  // differences. Also, set the IF_ADD_INC (address increment)
212  // flag. Should be set by default; keep it set.
213  (std::int8_t)0x85;
214  try {
215  agcom->converse(firstcon);
216  std::cout << "LSM9DS1AccelGyro accel reset; got ack ?!?" << std::endl;
218  // bother; ignore it
219  //std::cout << "LSM9DS1AccelGyro accel reset; no ack" << std::endl;
220  }
221  // give the device(s) time to complete the reset
222  // big guess here
223  std::this_thread::sleep_for(std::chrono::milliseconds(2));
224  } catch (...) {
225  // move the I2C communicator back
226  i2c = std::move(agcom);
227  throw;
228  }
229  // devices should be suspended by the reset operations
230 }
231 
233  suspend();
234 }
235 
236 void LSM9DS1AccelGyro::configure(float freq, Settings settings) {
237  // one of the instruments must be enabled
238  if (!settings.accelerometer && !settings.gyroscope) {
240  }
241  // gyroscope can only be used if the accelerometer is used
242  if (settings.gyroscope) {
243  // provide something close to the request
244  settings.accelerometer = 1;
245  }
246  // invalid value, two isn't used by the device
247  if (settings.gyroRange == 2) {
249  }
250  // accelerometer data rate
251  if (!settings.accelerometer) {
252  freq = 0;
253  } else if (
254  // the sampling rate cannot be negative or zero
255  (freq <= 0) ||
256  ( // data rate for accelerometer only
257  !settings.gyroscope &&
259  ) ||
260  ( // data rate for accelerometer and gyroscope
261  settings.gyroscope &&
263  )
264  ) {
265  // the sampling rate cannot be negative or zero
268  );
269  }
270  // prevent device from doing much prior to changing config
271  suspend(); // ensures it is inactive
272 
274  // start with accel/gyro; in power-down state after suspend()
275  if (settings.accelerometer) {
276  // no FIFO; already done by reset
277  // config gyro
278  std::uint8_t tmpregs[3] = {
279  (std::uint8_t)((agdrval << 5) | settings.gyroRange),
280  0,
281  (std::uint8_t)(
282  (settings.gyroLowPower << 7) |
283  (settings.gyroHighPass << 6)
284  )
285  };
286  conv.addOutputVector() << (std::uint8_t)AgrGyroConfig1 <<
287  tmpregs[0] << tmpregs[1] << tmpregs[2];
288  agcom->converse(conv);
289  conv.clear();
290  // config accel
291  tmpregs[0] = (agdrval << 5) | (settings.accelRange << 3);
292  conv.addOutputVector() << (std::uint8_t)AgrAccelConfig6 << tmpregs[0];
293  agcom->converse(conv);
294  conv.clear();
295  // gyro sleep
296  if (!settings.gyroscope) {
297  conv.addOutputVector() << (std::uint8_t)AgrConfig9 <<
298  (std::uint8_t)0x40;
299  agcom->converse(conv);
300  conv.clear();
301  }
302  }
303  cfg = settings;
304 
305  // make the conversation to read in the device's status
306  statq.clear();
307  statq.addOutputVector() << (std::uint8_t)AgrStatusPreAccel;
309  // conversation to read accelerometer and gyroscope samples
310  agsample.clear();
311  if (settings.accelerometer) {
312  if (settings.gyroscope) {
313  agsample.addOutputVector() << (std::uint8_t)AgrGyroSampleX;
315  } else {
316  agsample.addOutputVector() << (std::uint8_t)AgrAccelSampleX;
318  }
319  }
320 }
321 
324  // for accel & gyro, set sample rates (ODR) to zero
325  conv.addOutputVector() << (std::uint8_t)AgrGyroConfig1 << (std::uint8_t)0;
326  (
327  conv.addOutputVector() << (std::uint8_t)AgrAccelConfig6 <<
328  (std::uint8_t)0
329  ).breakBefore();
330  agcom->converse(conv);
331 }
332 
334  if (cfg.accelerometer) {
335  agcom->converse(statq);
337  std::int8_t status;
338  ex >> status;
339  if (status & 3) {
340  agcom->converse(agsample);
341  ex.reset(agsample);
342  if (cfg.gyroscope) {
343  ex >> gyro.x >> gyro.y >> gyro.z;
344  }
345  ex >> accl.x >> accl.y >> accl.z;
346  return true;
347  }
348  }
349  return false;
350 }
351 
357 }
358 
360  ps.x() = gyro.x * GyroScaleToUnits[cfg.gyroRange];
361  ps.y() = gyro.y * GyroScaleToUnits[cfg.gyroRange];
362  ps.z() = gyro.z * GyroScaleToUnits[cfg.gyroRange];
364 }
365 
366 // ----- Magnetometer -----
367 
369  std::unique_ptr<duds::hardware::interface::I2c> &i2c
370 ) : magcom(std::move(i2c)), mdatarate(0)
371 {
372  try {
374  // verify ID
375  firstcon.addOutputVector() << (std::int8_t)MagDeviceId;
376  firstcon.addInputVector(1);
377  // test the accelerometer/gyroscope first
378  magcom->converse(firstcon);
380  std::uint8_t id;
381  ex >> id;
382  if (id != 0x3D) {
385  );
386  }
387  firstcon.clear();
388  // reset
389  firstcon.addOutputVector() << (std::int8_t)MagConfig2 <<
390  // Set the REBOOT and SOFT_RST flags. Love how they are slight
391  // varitions on the names used in the accelerometer; who needs
392  // consistency? The docs are consistent in not documenting the
393  // flags well. Reboot memory content? Does that mean kill power
394  // to memory, then reapply power? Just try them both.
395  (std::int8_t)0xC0;
396  try {
397  magcom->converse(firstcon);
398  std::cout << "LSM9DS1Mag mag reset; got ack ?!?" << std::endl;
400  // bother; ignore it
401  //std::cout << "LSM9DS1Mag mag reset; no ack" << std::endl;
402  }
403  // give the device(s) time to complete the reset
404  // big guess here
405  std::this_thread::sleep_for(std::chrono::milliseconds(2));
406  } catch (...) {
407  // move the I2C communicators back
408  i2c = std::move(magcom);
409  throw;
410  }
411  // devices should be suspended by the reset operations
412 }
413 
415  suspend();
416 }
417 
418 void LSM9DS1Mag::configure(float freq, Settings settings) {
419  // one of the instruments must be enabled
420  if (!settings.magnetometer) {
422  }
423  // magnetometer data rate
424  if (
425  // rate must be positive
426  (freq < 0) ||
427  // rate must have a suitable match with device capabilities
429  // rate must be the slowest if low-power mode is used
430  (settings.magnetometer && settings.magLowPower && mdrval)
431  ) {
434  );
435  }
436  if (settings.magnetometer && settings.magLowPower) {
437  freq = 0;
438  }
439  // prevent device from doing much prior to changing config
440  suspend(); // ensures it is inactive
441 
443  // configure magnetometer; in power-down state after suspend()
444  if (settings.magnetometer) {
445  std::uint8_t tmpregs[4] = {
446  (std::uint8_t)(
447  (settings.magTempComp << 7) |
448  (settings.xyMagMode << 5) |
449  (mdrval << 2)
450  ),
451  (std::uint8_t)(settings.magRange << 5),
452  (std::uint8_t)(settings.magLowPower << 6),
453  (std::uint8_t)(settings.zMagMode << 2)
454  };
455  conv.addOutputVector() << (std::uint8_t)MagConfig1 << tmpregs[0] <<
456  tmpregs[1] << tmpregs[2] << tmpregs[3];
457  magcom->converse(conv);
458  }
459  cfg = settings;
460 
461  // make the conversation to read in the device's status; works for both
462  // devices
463  statq.clear();
464  statq.addOutputVector() << (std::uint8_t)MagStatus;
466  // conversation to read magnetometer samples
467  magsample.clear();
468  if (settings.magnetometer) {
469  magsample.addOutputVector() << (std::uint8_t)MagSampleX;
471  }
472 }
473 
476  // for mag, set config reg 3 to 3; operating mode power-down
477  conv.addOutputVector() << (std::uint8_t)MagConfig3 << (std::uint8_t)3;
478  magcom->converse(conv);
479 }
480 
482  if (cfg.magnetometer) {
483  magcom->converse(statq);
485  std::int8_t status;
486  ex >> status;
487  if (status & 7) {
488  magcom->converse(magsample);
489  ex.reset(magsample);
490  ex >> magn.x >> magn.y >> magn.z;
491  // The documentation has a confusing diagram of the axes showing that
492  // the magnetometer's axes are different from the others while rotating
493  // the chip 90 degrees. Modify the axes to match the axes of the others.
494  magn.x *= -1;
495  return true;
496  }
497  }
498  return false;
499 }
500 
502  ps.x() = magn.x * MagScaleToUnits[cfg.magRange];
503  ps.y() = magn.y * MagScaleToUnits[cfg.magRange];
504  ps.z() = magn.z * MagScaleToUnits[cfg.magRange];
506 }
507 
508 } } } }
void clear()
Makes the conversation empty.
MagAxesMode zMagMode
Operating mode for the Z axis.
Definition: LSM9DS1.hpp:360
MagRange magRange
The maximum magnetude setting.
Definition: LSM9DS1.hpp:345
duds::hardware::interface::Conversation statq
Definition: LSM9DS1.hpp:380
duds::hardware::interface::Conversation magsample
The conversation used to read in samples from the device.
Definition: LSM9DS1.hpp:380
void suspend()
Suspends operation by putting the device into a low-power mode that discontinues sampling.
Definition: LSM9DS1.cpp:474
float mdatarate
The currently configured sample rate.
Definition: LSM9DS1.hpp:388
double & y()
Returns the scalar value for the Y-axis.
The requested data rate is unsupported.
Definition: LSM9DS1.hpp:24
unsigned int accelerometer
Flag to use the accelerometer.
Definition: LSM9DS1.hpp:115
boost::error_info< struct Info_i2cdevaddr, int > I2cDeviceAddr
Provides the device (slave) address along with an error.
Definition: I2cErrors.hpp:83
Unit unit
The units describing the value.
Definition: Quantity.hpp:45
ConversationVector & addInputVector(std::size_t len)
Creates a new ConversationVector for fixed length input and initializes it with the given length...
Header for ConversationExtractor; includes all other conversation related header files.
void reset()
Prepares the object to extract another time from the same Conversation object used previously...
void accelerometerQuantity(ConvertedQuantity &ps) const
Provides the accelerometer data in meters per second squared.
Definition: LSM9DS1.cpp:352
float agdatarate
The currently configured sample rate.
Definition: LSM9DS1.hpp:157
double & z()
Returns the scalar value for the Z-axis.
void gyroscopeQuantity(ConvertedQuantity &ps) const
Provides the gyroscope data in radians per second.
Definition: LSM9DS1.cpp:359
GyroRange gyroRange
The maximum magnetude setting for the gyroscope.
Definition: LSM9DS1.hpp:128
unsigned int magTempComp
Enables temperature compensation using a temperature sensor inside the device.
Definition: LSM9DS1.hpp:365
boost::error_info< struct Info_UpdateRate, float > RequestedUpdateRate
Definition: FXOS8700CQ.hpp:35
bool sample()
Reads sampled data from the device.
Definition: LSM9DS1.cpp:481
A general status register; same data as AgrStatusPreAccel.
Definition: LSM9DS1.cpp:41
unsigned int gyroLowPower
Enable the low-power mode of the gyroscope.
Definition: LSM9DS1.hpp:133
STL namespace.
move_impl move(unsigned int c, unsigned int r)
Display stream manipulator that moves the display cursor to the given location.
unsigned int magLowPower
A low-power mode that forces the 0.625Hz sample rate.
Definition: LSM9DS1.hpp:352
AccelGyroRegs
The registers for the accelerometer and gyroscope device.
Definition: LSM9DS1.cpp:32
The device did not respond to its address (NACK).
Definition: I2cErrors.hpp:47
LSM9DS1AccelGyro(std::unique_ptr< duds::hardware::interface::I2c > &i2c)
Attempts to identify the device, then suspends the device&#39;s operation.
Definition: LSM9DS1.cpp:188
void suspend()
Suspends operation by putting the device into a low-power mode that discontinues sampling.
Definition: LSM9DS1.cpp:322
bool sample()
Reads sampled data from the device.
Definition: LSM9DS1.cpp:333
constexpr Unit Tesla(DUDS_UNIT_VALUE(-1, 0, 0, 1, 0, 0, -2, 0, 0))
std::unique_ptr< duds::hardware::interface::I2c > magcom
The I2C communication interface.
Definition: LSM9DS1.hpp:371
unsigned int magnetometer
Flag to use the magnetometer.
Definition: LSM9DS1.hpp:341
void quantity(ConvertedQuantity &ps) const
Definition: LSM9DS1.cpp:501
MagAxesMode xyMagMode
Operating mode for the X and Y axes.
Definition: LSM9DS1.hpp:356
AccelRange accelRange
The maximum magnetude setting for the accelerometer.
Definition: LSM9DS1.hpp:124
double & x()
Returns the scalar value for the X-axis.
static bool matchDataRate(std::int8_t &destVal, float &actualRate, float reqRate, const std::map< float, int > &rateMap)
Finds the lowest data rate that is at least as fast as requested, and the value needed to select this...
Definition: LSM9DS1.cpp:140
duds::hardware::interface::Conversation statq
Definition: LSM9DS1.hpp:149
std::unique_ptr< duds::hardware::interface::I2c > agcom
The I2C communication interface.
Definition: LSM9DS1.hpp:140
static const std::map< float, int > MagDataRateVals
Sample rates for the magnetometer.
Definition: LSM9DS1.cpp:114
MagRegs
The registers for the magnetometer device.
Definition: LSM9DS1.cpp:67
Unit unit
The units of all values in the array.
static const std::map< float, int > AccelGyroDataRateVals
Available sampling rates for the accelerometer and gyroscope when both are in use.
Definition: LSM9DS1.cpp:101
RawSample accl
The values supplied by the device.
Definition: LSM9DS1.hpp:153
duds::hardware::interface::Conversation agsample
The conversation used to read in samples from the device.
Definition: LSM9DS1.hpp:149
void configure(float freq, Settings settings)
Configures the device.
Definition: LSM9DS1.cpp:418
constexpr Unit Radian(DUDS_UNIT_VALUE(0, 0, 0, 0, 0, 0, 0, 1, 0))
static std::wstring_convert< std::codecvt_utf8< char32_t >, char32_t > conv
String converter; UTF-8 to/from UTF-32.
Definition: BppFont.cpp:165
The various settings for the device packed into an integer to avoid passing a lot of parameters...
Definition: LSM9DS1.hpp:110
static const std::map< float, int > AccelDataRateVals
Available sampling rates for the accelerometer when the gyroscope is not in use.
Definition: LSM9DS1.cpp:87
std::int8_t mdrval
The data rate value given to the device.
Definition: LSM9DS1.hpp:396
Settings cfg
The current various configuration options.
Definition: LSM9DS1.hpp:392
RawSample magn
The values supplied by the device.
Definition: LSM9DS1.hpp:384
unsigned int gyroscope
Flag to use the gyroscope.
Definition: LSM9DS1.hpp:120
std::int8_t agdrval
The data rate value given to the device.
Definition: LSM9DS1.hpp:165
A QuantityArray for the common usage of a three dimentional coordinate or a triple axis sample...
The configuration requested that neither the accelerometer or magnetometer be used.
Definition: LSM9DS1.hpp:33
Settings cfg
The current various configuration options.
Definition: LSM9DS1.hpp:161
LSM9DS1Mag(std::unique_ptr< duds::hardware::interface::I2c > &i2c)
Attempts to identify the device, then suspends the device&#39;s operation.
Definition: LSM9DS1.cpp:368
constexpr Quantity EarthSurfaceGravity(9.80665,)
A definition for Earth-normal acceleration from gravity that comes from the 1901 General Conference o...
double value
Some value; probably something measured.
Definition: Quantity.hpp:41
An attempt was made to use a device that seems to exist, but the responding device is not the type th...
void configure(float freq, Settings settings)
Configures the device.
Definition: LSM9DS1.cpp:236
ConversationVector & addOutputVector()
Creates a new ConversationVector for output and returns it for modification.
A general status register; same data as AgrStatusPreGyro.
Definition: LSM9DS1.cpp:54
Extracts data from a Conversation without modifying the Conversation or copying from it...
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
The requested maximum magnitude is either unsupported or an invalid value.
Definition: LSM9DS1.hpp:28
constexpr Unit Second(DUDS_UNIT_VALUE(0, 0, 0, 0, 0, 0, 1, 0, 0))
Represents a two-way conversation with a device.
The various settings for the device packed into an integer to avoid passing a lot of parameters...
Definition: LSM9DS1.hpp:336