001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005/*----------------------------------------------------------------------------*/ 006/* Copyright (c) 2016-2020 Analog Devices Inc. All Rights Reserved. */ 007/* Open Source Software - may be modified and shared by FRC teams. The code */ 008/* must be accompanied by the FIRST BSD license file in the root directory of */ 009/* the project. */ 010/* */ 011/* Modified by Juan Chong - frcsupport@analog.com */ 012/*----------------------------------------------------------------------------*/ 013 014package edu.wpi.first.wpilibj; 015 016import edu.wpi.first.hal.FRCNetComm.tResourceType; 017import edu.wpi.first.hal.HAL; 018import edu.wpi.first.hal.SimBoolean; 019import edu.wpi.first.hal.SimDevice; 020import edu.wpi.first.hal.SimDouble; 021import edu.wpi.first.util.sendable.Sendable; 022import edu.wpi.first.util.sendable.SendableBuilder; 023 024// CHECKSTYLE.OFF: TypeName 025// CHECKSTYLE.OFF: MemberName 026// CHECKSTYLE.OFF: SummaryJavadoc 027// CHECKSTYLE.OFF: UnnecessaryParentheses 028// CHECKSTYLE.OFF: OverloadMethodsDeclarationOrder 029// CHECKSTYLE.OFF: NonEmptyAtclauseDescription 030// CHECKSTYLE.OFF: MissingOverride 031// CHECKSTYLE.OFF: AtclauseOrder 032// CHECKSTYLE.OFF: LocalVariableName 033// CHECKSTYLE.OFF: RedundantModifier 034// CHECKSTYLE.OFF: AbbreviationAsWordInName 035// CHECKSTYLE.OFF: ParameterName 036// CHECKSTYLE.OFF: EmptyCatchBlock 037// CHECKSTYLE.OFF: MissingJavadocMethod 038// CHECKSTYLE.OFF: MissingSwitchDefault 039// CHECKSTYLE.OFF: VariableDeclarationUsageDistance 040// CHECKSTYLE.OFF: ArrayTypeStyle 041 042/** This class is for the ADIS16448 IMU that connects to the RoboRIO MXP port. */ 043@SuppressWarnings({ 044 "unused", 045 "PMD.RedundantFieldInitializer", 046 "PMD.ImmutableField", 047 "PMD.SingularField", 048 "PMD.CollapsibleIfStatements", 049 "PMD.MissingOverride", 050 "PMD.EmptyIfStmt", 051 "PMD.EmptyStatementNotInLoop" 052}) 053public class ADIS16448_IMU implements AutoCloseable, Sendable { 054 /** ADIS16448 Register Map Declaration */ 055 private static final int FLASH_CNT = 0x00; // Flash memory write count 056 057 private static final int XGYRO_OUT = 0x04; // X-axis gyroscope output 058 private static final int YGYRO_OUT = 0x06; // Y-axis gyroscope output 059 private static final int ZGYRO_OUT = 0x08; // Z-axis gyroscope output 060 private static final int XACCL_OUT = 0x0A; // X-axis accelerometer output 061 private static final int YACCL_OUT = 0x0C; // Y-axis accelerometer output 062 private static final int ZACCL_OUT = 0x0E; // Z-axis accelerometer output 063 private static final int XMAGN_OUT = 0x10; // X-axis magnetometer output 064 private static final int YMAGN_OUT = 0x12; // Y-axis magnetometer output 065 private static final int ZMAGN_OUT = 0x14; // Z-axis magnetometer output 066 private static final int BARO_OUT = 0x16; // Barometer pressure measurement, high word 067 private static final int TEMP_OUT = 0x18; // Temperature output 068 private static final int XGYRO_OFF = 0x1A; // X-axis gyroscope bias offset factor 069 private static final int YGYRO_OFF = 0x1C; // Y-axis gyroscope bias offset factor 070 private static final int ZGYRO_OFF = 0x1E; // Z-axis gyroscope bias offset factor 071 private static final int XACCL_OFF = 0x20; // X-axis acceleration bias offset factor 072 private static final int YACCL_OFF = 0x22; // Y-axis acceleration bias offset factor 073 private static final int ZACCL_OFF = 0x24; // Z-axis acceleration bias offset factor 074 private static final int XMAGN_HIC = 0x26; // X-axis magnetometer, hard iron factor 075 private static final int YMAGN_HIC = 0x28; // Y-axis magnetometer, hard iron factor 076 private static final int ZMAGN_HIC = 0x2A; // Z-axis magnetometer, hard iron factor 077 private static final int XMAGN_SIC = 0x2C; // X-axis magnetometer, soft iron factor 078 private static final int YMAGN_SIC = 0x2E; // Y-axis magnetometer, soft iron factor 079 private static final int ZMAGN_SIC = 0x30; // Z-axis magnetometer, soft iron factor 080 private static final int GPIO_CTRL = 0x32; // GPIO control 081 private static final int MSC_CTRL = 0x34; // MISC control 082 private static final int SMPL_PRD = 0x36; // Sample clock/Decimation filter control 083 private static final int SENS_AVG = 0x38; // Digital filter control 084 private static final int SEQ_CNT = 0x3A; // MAGN_OUT and BARO_OUT counter 085 private static final int DIAG_STAT = 0x3C; // System status 086 private static final int GLOB_CMD = 0x3E; // System command 087 private static final int ALM_MAG1 = 0x40; // Alarm 1 amplitude threshold 088 private static final int ALM_MAG2 = 0x42; // Alarm 2 amplitude threshold 089 private static final int ALM_SMPL1 = 0x44; // Alarm 1 sample size 090 private static final int ALM_SMPL2 = 0x46; // Alarm 2 sample size 091 private static final int ALM_CTRL = 0x48; // Alarm control 092 private static final int LOT_ID1 = 0x52; // Lot identification number 093 private static final int LOT_ID2 = 0x54; // Lot identification number 094 private static final int PROD_ID = 0x56; // Product identifier 095 private static final int SERIAL_NUM = 0x58; // Lot-specific serial number 096 097 /** ADIS16448 calibration times. */ 098 public enum CalibrationTime { 099 /** 32 ms calibration time */ 100 _32ms(0), 101 /** 64 ms calibration time */ 102 _64ms(1), 103 /** 128 ms calibration time */ 104 _128ms(2), 105 /** 256 ms calibration time */ 106 _256ms(3), 107 /** 512 ms calibration time */ 108 _512ms(4), 109 /** 1 s calibration time */ 110 _1s(5), 111 /** 2 s calibration time */ 112 _2s(6), 113 /** 4 s calibration time */ 114 _4s(7), 115 /** 8 s calibration time */ 116 _8s(8), 117 /** 16 s calibration time */ 118 _16s(9), 119 /** 32 s calibration time */ 120 _32s(10), 121 /** 64 s calibration time */ 122 _64s(11); 123 124 private final int value; 125 126 CalibrationTime(int value) { 127 this.value = value; 128 } 129 } 130 131 /** IMU axes. */ 132 public enum IMUAxis { 133 /** The IMU's X axis. */ 134 kX, 135 /** The IMU's Y axis. */ 136 kY, 137 /** The IMU's Z axis. */ 138 kZ 139 } 140 141 // * Static Constants */ 142 private static final double rad_to_deg = 57.2957795; 143 private static final double deg_to_rad = 0.0174532; 144 private static final double grav = 9.81; 145 146 /* User-specified yaw axis */ 147 private IMUAxis m_yaw_axis; 148 149 /* Offset data storage */ 150 private double[] m_offset_data_gyro_rate_x; 151 private double[] m_offset_data_gyro_rate_y; 152 private double[] m_offset_data_gyro_rate_z; 153 154 /* Instant raw output variables */ 155 private double m_gyro_rate_x = 0.0; 156 private double m_gyro_rate_y = 0.0; 157 private double m_gyro_rate_z = 0.0; 158 private double m_accel_x = 0.0; 159 private double m_accel_y = 0.0; 160 private double m_accel_z = 0.0; 161 private double m_mag_x = 0.0; 162 private double m_mag_y = 0.0; 163 private double m_mag_z = 0.0; 164 private double m_baro = 0.0; 165 private double m_temp = 0.0; 166 167 /* IMU gyro offset variables */ 168 private double m_gyro_rate_offset_x = 0.0; 169 private double m_gyro_rate_offset_y = 0.0; 170 private double m_gyro_rate_offset_z = 0.0; 171 private int m_avg_size = 0; 172 private int m_accum_count = 0; 173 174 /* Integrated gyro angle variables */ 175 private double m_integ_gyro_angle_x = 0.0; 176 private double m_integ_gyro_angle_y = 0.0; 177 private double m_integ_gyro_angle_z = 0.0; 178 179 /* Complementary filter variables */ 180 private double m_dt = 0.0; 181 private double m_alpha = 0.0; 182 private double m_tau = 1.0; 183 private double m_compAngleX = 0.0; 184 private double m_compAngleY = 0.0; 185 private double m_accelAngleX = 0.0; 186 private double m_accelAngleY = 0.0; 187 188 /* State variables */ 189 private volatile boolean m_thread_active = false; 190 private CalibrationTime m_calibration_time = CalibrationTime._32ms; 191 private volatile boolean m_first_run = true; 192 private volatile boolean m_thread_idle = false; 193 private boolean m_auto_configured = false; 194 private boolean m_start_up_mode = true; 195 196 /* Resources */ 197 private SPI m_spi; 198 private SPI.Port m_spi_port; 199 private DigitalInput m_auto_interrupt; 200 private DigitalOutput m_reset_out; 201 private DigitalInput m_reset_in; 202 private DigitalOutput m_status_led; 203 private Thread m_acquire_task; 204 private boolean m_connected; 205 206 private SimDevice m_simDevice; 207 private SimBoolean m_simConnected; 208 private SimDouble m_simGyroAngleX; 209 private SimDouble m_simGyroAngleY; 210 private SimDouble m_simGyroAngleZ; 211 private SimDouble m_simGyroRateX; 212 private SimDouble m_simGyroRateY; 213 private SimDouble m_simGyroRateZ; 214 private SimDouble m_simAccelX; 215 private SimDouble m_simAccelY; 216 private SimDouble m_simAccelZ; 217 218 /* CRC-16 Look-Up Table */ 219 int[] adiscrc = 220 new int[] { 221 0x0000, 0x17CE, 0x0FDF, 0x1811, 0x1FBE, 0x0870, 0x1061, 0x07AF, 222 0x1F3F, 0x08F1, 0x10E0, 0x072E, 0x0081, 0x174F, 0x0F5E, 0x1890, 223 0x1E3D, 0x09F3, 0x11E2, 0x062C, 0x0183, 0x164D, 0x0E5C, 0x1992, 224 0x0102, 0x16CC, 0x0EDD, 0x1913, 0x1EBC, 0x0972, 0x1163, 0x06AD, 225 0x1C39, 0x0BF7, 0x13E6, 0x0428, 0x0387, 0x1449, 0x0C58, 0x1B96, 226 0x0306, 0x14C8, 0x0CD9, 0x1B17, 0x1CB8, 0x0B76, 0x1367, 0x04A9, 227 0x0204, 0x15CA, 0x0DDB, 0x1A15, 0x1DBA, 0x0A74, 0x1265, 0x05AB, 228 0x1D3B, 0x0AF5, 0x12E4, 0x052A, 0x0285, 0x154B, 0x0D5A, 0x1A94, 229 0x1831, 0x0FFF, 0x17EE, 0x0020, 0x078F, 0x1041, 0x0850, 0x1F9E, 230 0x070E, 0x10C0, 0x08D1, 0x1F1F, 0x18B0, 0x0F7E, 0x176F, 0x00A1, 231 0x060C, 0x11C2, 0x09D3, 0x1E1D, 0x19B2, 0x0E7C, 0x166D, 0x01A3, 232 0x1933, 0x0EFD, 0x16EC, 0x0122, 0x068D, 0x1143, 0x0952, 0x1E9C, 233 0x0408, 0x13C6, 0x0BD7, 0x1C19, 0x1BB6, 0x0C78, 0x1469, 0x03A7, 234 0x1B37, 0x0CF9, 0x14E8, 0x0326, 0x0489, 0x1347, 0x0B56, 0x1C98, 235 0x1A35, 0x0DFB, 0x15EA, 0x0224, 0x058B, 0x1245, 0x0A54, 0x1D9A, 236 0x050A, 0x12C4, 0x0AD5, 0x1D1B, 0x1AB4, 0x0D7A, 0x156B, 0x02A5, 237 0x1021, 0x07EF, 0x1FFE, 0x0830, 0x0F9F, 0x1851, 0x0040, 0x178E, 238 0x0F1E, 0x18D0, 0x00C1, 0x170F, 0x10A0, 0x076E, 0x1F7F, 0x08B1, 239 0x0E1C, 0x19D2, 0x01C3, 0x160D, 0x11A2, 0x066C, 0x1E7D, 0x09B3, 240 0x1123, 0x06ED, 0x1EFC, 0x0932, 0x0E9D, 0x1953, 0x0142, 0x168C, 241 0x0C18, 0x1BD6, 0x03C7, 0x1409, 0x13A6, 0x0468, 0x1C79, 0x0BB7, 242 0x1327, 0x04E9, 0x1CF8, 0x0B36, 0x0C99, 0x1B57, 0x0346, 0x1488, 243 0x1225, 0x05EB, 0x1DFA, 0x0A34, 0x0D9B, 0x1A55, 0x0244, 0x158A, 244 0x0D1A, 0x1AD4, 0x02C5, 0x150B, 0x12A4, 0x056A, 0x1D7B, 0x0AB5, 245 0x0810, 0x1FDE, 0x07CF, 0x1001, 0x17AE, 0x0060, 0x1871, 0x0FBF, 246 0x172F, 0x00E1, 0x18F0, 0x0F3E, 0x0891, 0x1F5F, 0x074E, 0x1080, 247 0x162D, 0x01E3, 0x19F2, 0x0E3C, 0x0993, 0x1E5D, 0x064C, 0x1182, 248 0x0912, 0x1EDC, 0x06CD, 0x1103, 0x16AC, 0x0162, 0x1973, 0x0EBD, 249 0x1429, 0x03E7, 0x1BF6, 0x0C38, 0x0B97, 0x1C59, 0x0448, 0x1386, 250 0x0B16, 0x1CD8, 0x04C9, 0x1307, 0x14A8, 0x0366, 0x1B77, 0x0CB9, 251 0x0A14, 0x1DDA, 0x05CB, 0x1205, 0x15AA, 0x0264, 0x1A75, 0x0DBB, 252 0x152B, 0x02E5, 0x1AF4, 0x0D3A, 0x0A95, 0x1D5B, 0x054A, 0x1284 253 }; 254 255 private static class AcquireTask implements Runnable { 256 private final ADIS16448_IMU imu; 257 258 public AcquireTask(final ADIS16448_IMU imu) { 259 this.imu = imu; 260 } 261 262 @Override 263 public void run() { 264 imu.acquire(); 265 } 266 } 267 268 /** Creates a new ADIS16448_IMU object. */ 269 public ADIS16448_IMU() { 270 this(IMUAxis.kZ, SPI.Port.kMXP, CalibrationTime._512ms); 271 } 272 273 /** 274 * Creates a new ADIS16448_IMU object. 275 * 276 * @param yaw_axis The axis that measures the yaw 277 * @param port The SPI Port the gyro is plugged into 278 * @param cal_time Calibration time 279 */ 280 @SuppressWarnings("this-escape") 281 public ADIS16448_IMU(final IMUAxis yaw_axis, SPI.Port port, CalibrationTime cal_time) { 282 m_yaw_axis = yaw_axis; 283 m_spi_port = port; 284 285 m_acquire_task = new Thread(new AcquireTask(this)); 286 287 m_simDevice = SimDevice.create("Gyro:ADIS16448", port.value); 288 if (m_simDevice != null) { 289 m_simConnected = m_simDevice.createBoolean("connected", SimDevice.Direction.kInput, true); 290 m_simGyroAngleX = m_simDevice.createDouble("gyro_angle_x", SimDevice.Direction.kInput, 0.0); 291 m_simGyroAngleY = m_simDevice.createDouble("gyro_angle_y", SimDevice.Direction.kInput, 0.0); 292 m_simGyroAngleZ = m_simDevice.createDouble("gyro_angle_z", SimDevice.Direction.kInput, 0.0); 293 m_simGyroRateX = m_simDevice.createDouble("gyro_rate_x", SimDevice.Direction.kInput, 0.0); 294 m_simGyroRateY = m_simDevice.createDouble("gyro_rate_y", SimDevice.Direction.kInput, 0.0); 295 m_simGyroRateZ = m_simDevice.createDouble("gyro_rate_z", SimDevice.Direction.kInput, 0.0); 296 m_simAccelX = m_simDevice.createDouble("accel_x", SimDevice.Direction.kInput, 0.0); 297 m_simAccelY = m_simDevice.createDouble("accel_y", SimDevice.Direction.kInput, 0.0); 298 m_simAccelZ = m_simDevice.createDouble("accel_z", SimDevice.Direction.kInput, 0.0); 299 } 300 301 if (m_simDevice == null) { 302 // Force the IMU reset pin to toggle on startup (doesn't require DS enable) 303 m_reset_out = new DigitalOutput(18); // Drive MXP DIO8 low 304 Timer.delay(0.01); // Wait 10ms 305 m_reset_out.close(); 306 m_reset_in = new DigitalInput(18); // Set MXP DIO8 high 307 Timer.delay(0.25); // Wait 250ms 308 309 configCalTime(cal_time); 310 311 if (!switchToStandardSPI()) { 312 return; 313 } 314 315 // Set IMU internal decimation to 819.2 SPS 316 writeRegister(SMPL_PRD, 0x0001); 317 // Enable Data Ready (LOW = Good Data) on DIO1 (PWM0 on MXP) 318 writeRegister(MSC_CTRL, 0x0016); 319 // Disable IMU internal Bartlett filter 320 writeRegister(SENS_AVG, 0x0400); 321 // Clear offset registers 322 writeRegister(XGYRO_OFF, 0x0000); 323 writeRegister(YGYRO_OFF, 0x0000); 324 writeRegister(ZGYRO_OFF, 0x0000); 325 326 // Configure standard SPI 327 if (!switchToAutoSPI()) { 328 return; 329 } 330 // Notify DS that IMU calibration delay is active 331 DriverStation.reportWarning( 332 "ADIS16448 IMU Detected. Starting initial calibration delay.", false); 333 // Wait for whatever time the user set as the start-up delay 334 try { 335 Thread.sleep((long) (m_calibration_time.value * 1.2 * 1000)); 336 } catch (InterruptedException e) { 337 } 338 // Execute calibration routine 339 calibrate(); 340 // Reset accumulated offsets 341 reset(); 342 // Indicate to the acquire loop that we're done starting up 343 m_start_up_mode = false; 344 // Let the user know the IMU was initiallized successfully 345 DriverStation.reportWarning("ADIS16448 IMU Successfully Initialized!", false); 346 // Drive MXP PWM5 (IMU ready LED) low (active low) 347 m_status_led = new DigitalOutput(19); 348 } 349 350 // Report usage and post data to DS 351 HAL.report(tResourceType.kResourceType_ADIS16448, 0); 352 m_connected = true; 353 } 354 355 /** 356 * Checks the connection status of the IMU. 357 * 358 * @return True if the IMU is connected, false otherwise. 359 */ 360 public boolean isConnected() { 361 if (m_simConnected != null) { 362 return m_simConnected.get(); 363 } 364 return m_connected; 365 } 366 367 private static int toUShort(byte[] buf) { 368 return (((buf[0] & 0xFF) << 8) + ((buf[1] & 0xFF) << 0)); 369 } 370 371 private static int toUByte(int... buf) { 372 return (buf[0] & 0xFF); 373 } 374 375 private static int toUShort(int... buf) { 376 return (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF)); 377 } 378 379 /** */ 380 private static long toULong(int sint) { 381 return sint & 0x00000000FFFFFFFFL; 382 } 383 384 /** */ 385 private static int toShort(int... buf) { 386 return (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF)); 387 } 388 389 /** */ 390 private static int toInt(int... buf) { 391 return (buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF); 392 } 393 394 /** */ 395 private boolean switchToStandardSPI() { 396 // Check to see whether the acquire thread is active. If so, wait for it to stop 397 // producing data. 398 if (m_thread_active) { 399 m_thread_active = false; 400 while (!m_thread_idle) { 401 try { 402 Thread.sleep(10); 403 } catch (InterruptedException e) { 404 } 405 } 406 System.out.println("Paused the IMU processing thread successfully!"); 407 // Maybe we're in auto SPI mode? If so, kill auto SPI, and then SPI. 408 if (m_spi != null && m_auto_configured) { 409 m_spi.stopAuto(); 410 // We need to get rid of all the garbage left in the auto SPI buffer after 411 // stopping it. 412 // Sometimes data magically reappears, so we have to check the buffer size a 413 // couple of times 414 // to be sure we got it all. Yuck. 415 int[] trashBuffer = new int[200]; 416 try { 417 Thread.sleep(100); 418 } catch (InterruptedException e) { 419 } 420 int data_count = m_spi.readAutoReceivedData(trashBuffer, 0, 0); 421 while (data_count > 0) { 422 /* Dequeue 200 at a time, or the remainder of the buffer if less than 200 */ 423 m_spi.readAutoReceivedData(trashBuffer, Math.min(200, data_count), 0); 424 /* Update remaining buffer count */ 425 data_count = m_spi.readAutoReceivedData(trashBuffer, 0, 0); 426 } 427 System.out.println("Paused auto SPI successfully."); 428 } 429 } 430 // There doesn't seem to be a SPI port active. Let's try to set one up 431 if (m_spi == null) { 432 System.out.println("Setting up a new SPI port."); 433 m_spi = new SPI(m_spi_port); 434 m_spi.setClockRate(1000000); 435 m_spi.setMode(SPI.Mode.kMode3); 436 m_spi.setChipSelectActiveLow(); 437 readRegister(PROD_ID); // Dummy read 438 439 // Validate the product ID 440 if (readRegister(PROD_ID) != 16448) { 441 DriverStation.reportError("Could not find ADIS16448", false); 442 close(); 443 return false; 444 } 445 return true; 446 } else { 447 // Maybe the SPI port is active, but not in auto SPI mode? Try to read the 448 // product ID. 449 readRegister(PROD_ID); // dummy read 450 if (readRegister(PROD_ID) != 16448) { 451 DriverStation.reportError("Could not find an ADIS16448", false); 452 close(); 453 return false; 454 } else { 455 return true; 456 } 457 } 458 } 459 460 /** */ 461 boolean switchToAutoSPI() { 462 // No SPI port has been set up. Go set one up first. 463 if (m_spi == null) { 464 if (!switchToStandardSPI()) { 465 DriverStation.reportError("Failed to start/restart auto SPI", false); 466 return false; 467 } 468 } 469 // Only set up the interrupt if needed. 470 if (m_auto_interrupt == null) { 471 m_auto_interrupt = new DigitalInput(10); // MXP DIO0 472 } 473 // The auto SPI controller gets angry if you try to set up two instances on one 474 // bus. 475 if (!m_auto_configured) { 476 m_spi.initAuto(8200); 477 m_auto_configured = true; 478 } 479 // Set auto SPI packet data and size 480 m_spi.setAutoTransmitData(new byte[] {GLOB_CMD}, 27); 481 // Configure auto stall time 482 m_spi.configureAutoStall(100, 1000, 255); 483 // Kick off auto SPI (Note: Device configuration impossible after auto SPI is 484 // activated) 485 m_spi.startAutoTrigger(m_auto_interrupt, true, false); 486 487 // Check to see if the acquire thread is running. If not, kick one off. 488 if (!m_acquire_task.isAlive()) { 489 m_first_run = true; 490 m_thread_active = true; 491 m_acquire_task.start(); 492 System.out.println("New IMU Processing thread activated!"); 493 } else { 494 // The thread was running, re-init run variables and start it up again. 495 m_first_run = true; 496 m_thread_active = true; 497 System.out.println("Old IMU Processing thread re-activated!"); 498 } 499 // Looks like the thread didn't start for some reason. Abort. 500 if (!m_acquire_task.isAlive()) { 501 DriverStation.reportError("Failed to start/restart the acquire() thread.", false); 502 close(); 503 return false; 504 } 505 return true; 506 } 507 508 /** 509 * Configures the decimation rate of the IMU. 510 * 511 * @param decimationRate The new decimation value. 512 * @return 0 if success, 1 if no change, 2 if error. 513 */ 514 public int configDecRate(int decimationRate) { 515 // Switches the active SPI port to standard SPI mode, writes a new value to 516 // the DECIMATE register in the IMU, and re-enables auto SPI. 517 // 518 // This function enters standard SPI mode, writes a new DECIMATE setting to 519 // the IMU, adjusts the sample scale factor, and re-enters auto SPI mode. 520 int writeValue; 521 int readbackValue; 522 if (!switchToStandardSPI()) { 523 DriverStation.reportError("Failed to configure/reconfigure standard SPI.", false); 524 return 2; 525 } 526 527 /* Check max */ 528 if (decimationRate > 9) { 529 DriverStation.reportError( 530 "Attempted to write an invalid decimation value. Capping at 9", false); 531 decimationRate = 9; 532 } 533 if (decimationRate < 0) { 534 DriverStation.reportError( 535 "Attempted to write an invalid decimation value. Capping at 0", false); 536 decimationRate = 0; 537 } 538 539 /* Shift decimation setting to correct position and select internal sync */ 540 writeValue = (decimationRate << 8) | 0x1; 541 542 /* Apply to IMU */ 543 writeRegister(SMPL_PRD, writeValue); 544 545 /* Perform read back to verify write */ 546 readbackValue = readRegister(SMPL_PRD); 547 548 /* Throw error for invalid write */ 549 if (readbackValue != writeValue) { 550 DriverStation.reportError("ADIS16448 SMPL_PRD write failed.", false); 551 } 552 553 if (!switchToAutoSPI()) { 554 DriverStation.reportError("Failed to configure/reconfigure auto SPI.", false); 555 return 2; 556 } 557 return 0; 558 } 559 560 /** 561 * Configures calibration time 562 * 563 * @param new_cal_time New calibration time 564 * @return 1 if the new calibration time is the same as the current one else 0 565 */ 566 public final int configCalTime(CalibrationTime new_cal_time) { 567 if (m_calibration_time == new_cal_time) { 568 return 1; 569 } else { 570 m_calibration_time = new_cal_time; 571 m_avg_size = m_calibration_time.value * 819; 572 initOffsetBuffer(m_avg_size); 573 return 0; 574 } 575 } 576 577 private void initOffsetBuffer(int size) { 578 // Avoid exceptions in the case of bad arguments 579 if (size < 1) { 580 size = 1; 581 } 582 // Set average size to size (correct bad values) 583 m_avg_size = size; 584 synchronized (this) { 585 // Resize vector 586 m_offset_data_gyro_rate_x = new double[size]; 587 m_offset_data_gyro_rate_y = new double[size]; 588 m_offset_data_gyro_rate_z = new double[size]; 589 // Set accumulate count to 0 590 m_accum_count = 0; 591 } 592 } 593 594 /** 595 * Calibrate the gyro. It's important to make sure that the robot is not moving while the 596 * calibration is in progress, this is typically done when the robot is first turned on while it's 597 * sitting at rest before the match starts. 598 */ 599 public void calibrate() { 600 synchronized (this) { 601 int gyroAverageSize = Math.min(m_accum_count, m_avg_size); 602 double accum_gyro_rate_x = 0.0; 603 double accum_gyro_rate_y = 0.0; 604 double accum_gyro_rate_z = 0.0; 605 for (int i = 0; i < gyroAverageSize; i++) { 606 accum_gyro_rate_x += m_offset_data_gyro_rate_x[i]; 607 accum_gyro_rate_y += m_offset_data_gyro_rate_y[i]; 608 accum_gyro_rate_z += m_offset_data_gyro_rate_z[i]; 609 } 610 m_gyro_rate_offset_x = accum_gyro_rate_x / gyroAverageSize; 611 m_gyro_rate_offset_y = accum_gyro_rate_y / gyroAverageSize; 612 m_gyro_rate_offset_z = accum_gyro_rate_z / gyroAverageSize; 613 m_integ_gyro_angle_x = 0.0; 614 m_integ_gyro_angle_y = 0.0; 615 m_integ_gyro_angle_z = 0.0; 616 // System.out.println("Avg Size: " + gyroAverageSize + "X Off: " + 617 // m_gyro_rate_offset_x + "Y Off: " + m_gyro_rate_offset_y + "Z Off: " + 618 // m_gyro_rate_offset_z); 619 } 620 } 621 622 /** 623 * Sets the yaw axis 624 * 625 * @param yaw_axis The new yaw axis to use 626 * @return 1 if the new yaw axis is the same as the current one else 0. 627 */ 628 public int setYawAxis(IMUAxis yaw_axis) { 629 if (m_yaw_axis == yaw_axis) { 630 return 1; 631 } 632 m_yaw_axis = yaw_axis; 633 reset(); 634 return 0; 635 } 636 637 private int readRegister(final int reg) { 638 // ByteBuffer buf = ByteBuffer.allocateDirect(2); 639 final byte[] buf = new byte[2]; 640 // buf.order(ByteOrder.BIG_ENDIAN); 641 buf[0] = (byte) (reg & 0x7f); 642 buf[1] = (byte) 0; 643 644 m_spi.write(buf, 2); 645 m_spi.read(false, buf, 2); 646 647 return toUShort(buf); 648 } 649 650 private void writeRegister(final int reg, final int val) { 651 final byte[] buf = new byte[2]; 652 // low byte 653 buf[0] = (byte) (0x80 | reg); 654 buf[1] = (byte) (val & 0xff); 655 m_spi.write(buf, 2); 656 // high byte 657 buf[0] = (byte) (0x81 | reg); 658 buf[1] = (byte) (val >> 8); 659 m_spi.write(buf, 2); 660 } 661 662 /** 663 * Reset the gyro. 664 * 665 * <p>Resets the gyro accumulations to a heading of zero. This can be used if there is significant 666 * drift in the gyro and it needs to be recalibrated after running. 667 */ 668 public void reset() { 669 synchronized (this) { 670 m_integ_gyro_angle_x = 0.0; 671 m_integ_gyro_angle_y = 0.0; 672 m_integ_gyro_angle_z = 0.0; 673 } 674 } 675 676 /** Delete (free) the spi port used for the IMU. */ 677 @Override 678 public void close() { 679 if (m_thread_active) { 680 m_thread_active = false; 681 try { 682 if (m_acquire_task != null) { 683 m_acquire_task.join(); 684 m_acquire_task = null; 685 } 686 } catch (InterruptedException e) { 687 } 688 if (m_spi != null) { 689 if (m_auto_configured) { 690 m_spi.stopAuto(); 691 } 692 m_spi.close(); 693 m_auto_configured = false; 694 if (m_auto_interrupt != null) { 695 m_auto_interrupt.close(); 696 m_auto_interrupt = null; 697 } 698 m_spi = null; 699 } 700 } 701 if (m_simDevice != null) { 702 m_simDevice.close(); 703 m_simDevice = null; 704 } 705 System.out.println("Finished cleaning up after the IMU driver."); 706 } 707 708 /** */ 709 private void acquire() { 710 // Set data packet length 711 final int dataset_len = 29; // 18 data points + timestamp 712 final int BUFFER_SIZE = 4000; 713 714 // Set up buffers and variables 715 int[] buffer = new int[BUFFER_SIZE]; 716 int data_count = 0; 717 int data_remainder = 0; 718 int data_to_read = 0; 719 int bufferAvgIndex = 0; 720 double previous_timestamp = 0.0; 721 double delta_angle = 0.0; 722 double gyro_rate_x = 0.0; 723 double gyro_rate_y = 0.0; 724 double gyro_rate_z = 0.0; 725 double accel_x = 0.0; 726 double accel_y = 0.0; 727 double accel_z = 0.0; 728 double mag_x = 0.0; 729 double mag_y = 0.0; 730 double mag_z = 0.0; 731 double baro = 0.0; 732 double temp = 0.0; 733 double gyro_rate_x_si = 0.0; 734 double gyro_rate_y_si = 0.0; 735 double gyro_rate_z_si = 0.0; 736 double accel_x_si = 0.0; 737 double accel_y_si = 0.0; 738 double accel_z_si = 0.0; 739 double compAngleX = 0.0; 740 double compAngleY = 0.0; 741 double accelAngleX = 0.0; 742 double accelAngleY = 0.0; 743 744 while (true) { 745 // Sleep loop for 5ms 746 try { 747 Thread.sleep(5); 748 } catch (InterruptedException e) { 749 } 750 751 if (m_thread_active) { 752 m_thread_idle = false; 753 754 data_count = 755 m_spi.readAutoReceivedData( 756 buffer, 0, 0); // Read number of bytes currently stored in the buffer 757 data_remainder = 758 data_count % dataset_len; // Check if frame is incomplete. Add 1 because of timestamp 759 data_to_read = data_count - data_remainder; // Remove incomplete data from read count 760 if (data_to_read > BUFFER_SIZE) { 761 DriverStation.reportWarning( 762 "ADIS16448 data processing thread overrun has occurred!", false); 763 data_to_read = BUFFER_SIZE - (BUFFER_SIZE % dataset_len); 764 } 765 m_spi.readAutoReceivedData( 766 buffer, data_to_read, 0); // Read data from DMA buffer (only complete sets) 767 768 // Could be multiple data sets in the buffer. Handle each one. 769 for (int i = 0; i < data_to_read; i += dataset_len) { 770 // Calculate CRC-16 on each data packet 771 int calc_crc = 0x0000FFFF; // Starting word 772 int read_byte = 0; 773 int imu_crc = 0; 774 for (int k = 5; 775 k < 27; 776 k += 2) { // Cycle through XYZ GYRO, XYZ ACCEL, XYZ MAG, BARO, TEMP (Ignore Status & 777 // CRC) 778 read_byte = buffer[i + k + 1]; // Process LSB 779 calc_crc = (calc_crc >>> 8) ^ adiscrc[(calc_crc & 0x000000FF) ^ read_byte]; 780 read_byte = buffer[i + k]; // Process MSB 781 calc_crc = (calc_crc >>> 8) ^ adiscrc[(calc_crc & 0x000000FF) ^ read_byte]; 782 } 783 calc_crc = ~calc_crc & 0xFFFF; // Complement 784 calc_crc = ((calc_crc << 8) | (calc_crc >> 8)) & 0xFFFF; // Flip LSB & MSB 785 imu_crc = toUShort(buffer[i + 27], buffer[i + 28]); // Extract DUT CRC from data buffer 786 787 if (calc_crc == imu_crc) { 788 // Timestamp is at buffer[i] 789 m_dt = ((double) buffer[i] - previous_timestamp) / 1000000.0; 790 791 // Scale sensor data 792 gyro_rate_x = (toShort(buffer[i + 5], buffer[i + 6]) * 0.04); 793 gyro_rate_y = (toShort(buffer[i + 7], buffer[i + 8]) * 0.04); 794 gyro_rate_z = (toShort(buffer[i + 9], buffer[i + 10]) * 0.04); 795 accel_x = (toShort(buffer[i + 11], buffer[i + 12]) * 0.833); 796 accel_y = (toShort(buffer[i + 13], buffer[i + 14]) * 0.833); 797 accel_z = (toShort(buffer[i + 15], buffer[i + 16]) * 0.833); 798 mag_x = (toShort(buffer[i + 17], buffer[i + 18]) * 0.1429); 799 mag_y = (toShort(buffer[i + 19], buffer[i + 20]) * 0.1429); 800 mag_z = (toShort(buffer[i + 21], buffer[i + 22]) * 0.1429); 801 baro = (toShort(buffer[i + 23], buffer[i + 24]) * 0.02); 802 temp = (toShort(buffer[i + 25], buffer[i + 26]) * 0.07386 + 31.0); 803 804 // Convert scaled sensor data to SI units (for tilt calculations) 805 // TODO: Should the unit outputs be selectable? 806 gyro_rate_x_si = gyro_rate_x * deg_to_rad; 807 gyro_rate_y_si = gyro_rate_y * deg_to_rad; 808 gyro_rate_z_si = gyro_rate_z * deg_to_rad; 809 accel_x_si = accel_x * grav; 810 accel_y_si = accel_y * grav; 811 accel_z_si = accel_z * grav; 812 // Store timestamp for next iteration 813 previous_timestamp = buffer[i]; 814 // Calculate alpha for use with the complementary filter 815 m_alpha = m_tau / (m_tau + m_dt); 816 // Calculate complementary filter 817 if (m_first_run) { 818 // Set up inclinometer calculations for first run 819 accelAngleX = 820 Math.atan2( 821 -accel_x_si, 822 Math.sqrt((accel_y_si * accel_y_si) + (-accel_z_si * -accel_z_si))); 823 accelAngleY = 824 Math.atan2( 825 accel_y_si, 826 Math.sqrt((-accel_x_si * -accel_x_si) + (-accel_z_si * -accel_z_si))); 827 compAngleX = accelAngleX; 828 compAngleY = accelAngleY; 829 } else { 830 // Run inclinometer calculations 831 accelAngleX = 832 Math.atan2( 833 -accel_x_si, 834 Math.sqrt((accel_y_si * accel_y_si) + (-accel_z_si * -accel_z_si))); 835 accelAngleY = 836 Math.atan2( 837 accel_y_si, 838 Math.sqrt((-accel_x_si * -accel_x_si) + (-accel_z_si * -accel_z_si))); 839 accelAngleX = formatAccelRange(accelAngleX, -accel_z_si); 840 accelAngleY = formatAccelRange(accelAngleY, -accel_z_si); 841 compAngleX = compFilterProcess(compAngleX, accelAngleX, -gyro_rate_y_si); 842 compAngleY = compFilterProcess(compAngleY, accelAngleY, -gyro_rate_x_si); 843 } 844 845 // Update global variables and state 846 synchronized (this) { 847 // Ignore first, integrated sample 848 if (m_first_run) { 849 m_integ_gyro_angle_x = 0.0; 850 m_integ_gyro_angle_y = 0.0; 851 m_integ_gyro_angle_z = 0.0; 852 } else { 853 // Accumulate gyro for offset calibration 854 // Add to buffer 855 bufferAvgIndex = m_accum_count % m_avg_size; 856 m_offset_data_gyro_rate_x[bufferAvgIndex] = gyro_rate_x; 857 m_offset_data_gyro_rate_y[bufferAvgIndex] = gyro_rate_y; 858 m_offset_data_gyro_rate_z[bufferAvgIndex] = gyro_rate_z; 859 // Increment counter 860 m_accum_count++; 861 } 862 if (!m_start_up_mode) { 863 m_gyro_rate_x = gyro_rate_x; 864 m_gyro_rate_y = gyro_rate_y; 865 m_gyro_rate_z = gyro_rate_z; 866 m_accel_x = accel_x; 867 m_accel_y = accel_y; 868 m_accel_z = accel_z; 869 m_mag_x = mag_x; 870 m_mag_y = mag_y; 871 m_mag_z = mag_z; 872 m_baro = baro; 873 m_temp = temp; 874 m_compAngleX = compAngleX * rad_to_deg; 875 m_compAngleY = compAngleY * rad_to_deg; 876 m_accelAngleX = accelAngleX * rad_to_deg; 877 m_accelAngleY = accelAngleY * rad_to_deg; 878 // Accumulate gyro for angle integration and publish to global variables 879 m_integ_gyro_angle_x += (gyro_rate_x - m_gyro_rate_offset_x) * m_dt; 880 m_integ_gyro_angle_y += (gyro_rate_y - m_gyro_rate_offset_y) * m_dt; 881 m_integ_gyro_angle_z += (gyro_rate_z - m_gyro_rate_offset_z) * m_dt; 882 } 883 // System.out.println("Good CRC"); 884 } 885 m_first_run = false; 886 } else { 887 // System.out.println("Bad CRC"); 888 /* 889 * System.out.println("Calc CRC: " + calc_crc); 890 * System.out.println("IMU CRC: " + imu_crc); 891 * System.out.println( 892 * buffer[i] + " " + 893 * (buffer[i + 1]) + " " + (buffer[i + 2]) + " " + 894 * (buffer[i + 3]) + " " + (buffer[i + 4]) + " " + 895 * (buffer[i + 5]) + " " + (buffer[i + 6]) + " " + 896 * (buffer[i + 7]) + " " + (buffer[i + 8]) + " " + 897 * (buffer[i + 9]) + " " + (buffer[i + 10]) + " " + 898 * (buffer[i + 11]) + " " + (buffer[i + 12]) + " " + 899 * (buffer[i + 13]) + " " + (buffer[i + 14]) + " " + 900 * (buffer[i + 15]) + " " + (buffer[i + 16]) + " " + 901 * (buffer[i + 17]) + " " + (buffer[i + 18]) + " " + 902 * (buffer[i + 19]) + " " + (buffer[i + 20]) + " " + 903 * (buffer[i + 21]) + " " + (buffer[i + 22]) + " " + 904 * (buffer[i + 23]) + " " + (buffer[i + 24]) + " " + 905 * (buffer[i + 25]) + " " + (buffer[i + 26]) + " " + 906 * (buffer[i + 27]) + " " + (buffer[i + 28])); 907 */ 908 } 909 } 910 } else { 911 m_thread_idle = true; 912 data_count = 0; 913 data_remainder = 0; 914 data_to_read = 0; 915 previous_timestamp = 0.0; 916 delta_angle = 0.0; 917 gyro_rate_x = 0.0; 918 gyro_rate_y = 0.0; 919 gyro_rate_z = 0.0; 920 accel_x = 0.0; 921 accel_y = 0.0; 922 accel_z = 0.0; 923 mag_x = 0.0; 924 mag_y = 0.0; 925 mag_z = 0.0; 926 baro = 0.0; 927 temp = 0.0; 928 gyro_rate_x_si = 0.0; 929 gyro_rate_y_si = 0.0; 930 gyro_rate_z_si = 0.0; 931 accel_x_si = 0.0; 932 accel_y_si = 0.0; 933 accel_z_si = 0.0; 934 compAngleX = 0.0; 935 compAngleY = 0.0; 936 accelAngleX = 0.0; 937 accelAngleY = 0.0; 938 } 939 } 940 } 941 942 /** 943 * @param compAngle 944 * @param accAngle 945 * @return 946 */ 947 private double formatFastConverge(double compAngle, double accAngle) { 948 if (compAngle > accAngle + Math.PI) { 949 compAngle = compAngle - 2.0 * Math.PI; 950 } else if (accAngle > compAngle + Math.PI) { 951 compAngle = compAngle + 2.0 * Math.PI; 952 } 953 return compAngle; 954 } 955 956 /** 957 * @param compAngle 958 * @return 959 */ 960 private double formatRange0to2PI(double compAngle) { 961 while (compAngle >= 2 * Math.PI) { 962 compAngle = compAngle - 2.0 * Math.PI; 963 } 964 while (compAngle < 0.0) { 965 compAngle = compAngle + 2.0 * Math.PI; 966 } 967 return compAngle; 968 } 969 970 /** 971 * @param accelAngle 972 * @param accelZ 973 * @return 974 */ 975 private double formatAccelRange(double accelAngle, double accelZ) { 976 if (accelZ < 0.0) { 977 accelAngle = Math.PI - accelAngle; 978 } else if (accelZ > 0.0 && accelAngle < 0.0) { 979 accelAngle = 2.0 * Math.PI + accelAngle; 980 } 981 return accelAngle; 982 } 983 984 /** 985 * @param compAngle 986 * @param accelAngle 987 * @param omega 988 * @return 989 */ 990 private double compFilterProcess(double compAngle, double accelAngle, double omega) { 991 compAngle = formatFastConverge(compAngle, accelAngle); 992 compAngle = m_alpha * (compAngle + omega * m_dt) + (1.0 - m_alpha) * accelAngle; 993 compAngle = formatRange0to2PI(compAngle); 994 if (compAngle > Math.PI) { 995 compAngle = compAngle - 2.0 * Math.PI; 996 } 997 return compAngle; 998 } 999 1000 /** 1001 * Returns the yaw axis angle in degrees (CCW positive). 1002 * 1003 * @return Yaw axis angle in degrees (CCW positive). 1004 */ 1005 public synchronized double getAngle() { 1006 switch (m_yaw_axis) { 1007 case kX: 1008 return getGyroAngleX(); 1009 case kY: 1010 return getGyroAngleY(); 1011 case kZ: 1012 return getGyroAngleZ(); 1013 default: 1014 return 0.0; 1015 } 1016 } 1017 1018 /** 1019 * Returns the yaw axis angular rate in degrees per second (CCW positive). 1020 * 1021 * @return Yaw axis angular rate in degrees per second (CCW positive). 1022 */ 1023 public synchronized double getRate() { 1024 switch (m_yaw_axis) { 1025 case kX: 1026 return getGyroRateX(); 1027 case kY: 1028 return getGyroRateY(); 1029 case kZ: 1030 return getGyroRateZ(); 1031 default: 1032 return 0.0; 1033 } 1034 } 1035 1036 /** 1037 * Returns which axis, kX, kY, or kZ, is set to the yaw axis. 1038 * 1039 * @return IMUAxis Yaw Axis 1040 */ 1041 public IMUAxis getYawAxis() { 1042 return m_yaw_axis; 1043 } 1044 1045 /** 1046 * Returns the accumulated gyro angle in the X axis in degrees. 1047 * 1048 * @return The accumulated gyro angle in the X axis in degrees. 1049 */ 1050 public synchronized double getGyroAngleX() { 1051 if (m_simGyroAngleX != null) { 1052 return m_simGyroAngleX.get(); 1053 } 1054 return m_integ_gyro_angle_x; 1055 } 1056 1057 /** 1058 * Returns the accumulated gyro angle in the Y axis in degrees. 1059 * 1060 * @return The accumulated gyro angle in the Y axis in degrees. 1061 */ 1062 public synchronized double getGyroAngleY() { 1063 if (m_simGyroAngleY != null) { 1064 return m_simGyroAngleY.get(); 1065 } 1066 return m_integ_gyro_angle_y; 1067 } 1068 1069 /** 1070 * Returns the accumulated gyro angle in the Z axis in degrees. 1071 * 1072 * @return The accumulated gyro angle in the Z axis in degrees. 1073 */ 1074 public synchronized double getGyroAngleZ() { 1075 if (m_simGyroAngleZ != null) { 1076 return m_simGyroAngleZ.get(); 1077 } 1078 return m_integ_gyro_angle_z; 1079 } 1080 1081 /** 1082 * Returns the gyro angular rate in the X axis in degrees per second. 1083 * 1084 * @return The gyro angular rate in the X axis in degrees per second. 1085 */ 1086 public synchronized double getGyroRateX() { 1087 if (m_simGyroRateX != null) { 1088 return m_simGyroRateX.get(); 1089 } 1090 return m_gyro_rate_x; 1091 } 1092 1093 /** 1094 * Returns the gyro angular rate in the Y axis in degrees per second. 1095 * 1096 * @return The gyro angular rate in the Y axis in degrees per second. 1097 */ 1098 public synchronized double getGyroRateY() { 1099 if (m_simGyroRateY != null) { 1100 return m_simGyroRateY.get(); 1101 } 1102 return m_gyro_rate_y; 1103 } 1104 1105 /** 1106 * Returns the gyro angular rate in the Z axis in degrees per second. 1107 * 1108 * @return The gyro angular rate in the Z axis in degrees per second. 1109 */ 1110 public synchronized double getGyroRateZ() { 1111 if (m_simGyroRateZ != null) { 1112 return m_simGyroRateZ.get(); 1113 } 1114 return m_gyro_rate_z; 1115 } 1116 1117 /** 1118 * Returns the acceleration in the X axis in meters per second squared. 1119 * 1120 * @return The acceleration in the X axis in meters per second squared. 1121 */ 1122 public synchronized double getAccelX() { 1123 if (m_simAccelX != null) { 1124 return m_simAccelX.get(); 1125 } 1126 return m_accel_x * 9.81; 1127 } 1128 1129 /** 1130 * Returns the acceleration in the Y axis in meters per second squared. 1131 * 1132 * @return The acceleration in the Y axis in meters per second squared. 1133 */ 1134 public synchronized double getAccelY() { 1135 if (m_simAccelY != null) { 1136 return m_simAccelY.get(); 1137 } 1138 return m_accel_y * 9.81; 1139 } 1140 1141 /** 1142 * Returns the acceleration in the Z axis in meters per second squared. 1143 * 1144 * @return The acceleration in the Z axis in meters per second squared. 1145 */ 1146 public synchronized double getAccelZ() { 1147 if (m_simAccelZ != null) { 1148 return m_simAccelZ.get(); 1149 } 1150 return m_accel_z * 9.81; 1151 } 1152 1153 /** 1154 * Returns the magnetic field strength in the X axis in Tesla. 1155 * 1156 * @return The magnetic field strength in the X axis in Tesla. 1157 */ 1158 public synchronized double getMagneticFieldX() { 1159 // mG to T 1160 return m_mag_x * 1e-7; 1161 } 1162 1163 /** 1164 * Returns the magnetic field strength in the Y axis in Tesla. 1165 * 1166 * @return The magnetic field strength in the Y axis in Tesla. 1167 */ 1168 public synchronized double getMagneticFieldY() { 1169 // mG to T 1170 return m_mag_y * 1e-7; 1171 } 1172 1173 /** 1174 * Returns the magnetic field strength in the Z axis in Tesla. 1175 * 1176 * @return The magnetic field strength in the Z axis in Tesla. 1177 */ 1178 public synchronized double getMagneticFieldZ() { 1179 // mG to T 1180 return m_mag_z * 1e-7; 1181 } 1182 1183 /** 1184 * Returns the complementary angle around the X axis computed from accelerometer and gyro rate 1185 * measurements. 1186 * 1187 * @return The X-axis complementary angle in degrees. 1188 */ 1189 public synchronized double getXComplementaryAngle() { 1190 return m_compAngleX; 1191 } 1192 1193 /** 1194 * Returns the complementary angle around the Y axis computed from accelerometer and gyro rate 1195 * measurements. 1196 * 1197 * @return The Y-axis complementary angle in degrees. 1198 */ 1199 public synchronized double getYComplementaryAngle() { 1200 return m_compAngleY; 1201 } 1202 1203 /** 1204 * Returns the X-axis filtered acceleration angle in degrees. 1205 * 1206 * @return The X-axis filtered acceleration angle in degrees. 1207 */ 1208 public synchronized double getXFilteredAccelAngle() { 1209 return m_accelAngleX; 1210 } 1211 1212 /** 1213 * Returns the Y-axis filtered acceleration angle in degrees. 1214 * 1215 * @return The Y-axis filtered acceleration angle in degrees. 1216 */ 1217 public synchronized double getYFilteredAccelAngle() { 1218 return m_accelAngleY; 1219 } 1220 1221 /** 1222 * Returns the barometric pressure in PSI. 1223 * 1224 * @return The barometric pressure in PSI. 1225 */ 1226 public synchronized double getBarometricPressure() { 1227 // mbar to PSI conversion 1228 return m_baro * 0.0145; 1229 } 1230 1231 /** 1232 * Returns the temperature in degrees Celsius. 1233 * 1234 * @return The temperature in degrees Celsius. 1235 */ 1236 public synchronized double getTemperature() { 1237 return m_temp; 1238 } 1239 1240 /** 1241 * Get the SPI port number. 1242 * 1243 * @return The SPI port number. 1244 */ 1245 public int getPort() { 1246 return m_spi_port.value; 1247 } 1248 1249 @Override 1250 public void initSendable(SendableBuilder builder) { 1251 builder.setSmartDashboardType("Gyro"); 1252 builder.addDoubleProperty("Value", this::getAngle, null); 1253 } 1254}