blob: 1a74adf2a89f59976750738dd27db541029b06af [file] [log] [blame]
Austin Schuh18cb1df2023-01-28 11:13:35 -08001#include "frc971/imu_reader/imu.h"
Ravago Jonese12b7902022-02-04 22:50:44 -08002
Austin Schuh99f7c6a2024-06-25 22:07:44 -07003#include "absl/log/check.h"
4#include "absl/log/log.h"
Ravago Jonese12b7902022-02-04 22:50:44 -08005
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "aos/util/crc32.h"
7
Austin Schuh18cb1df2023-01-28 11:13:35 -08008namespace frc971::imu {
Ravago Jonese12b7902022-02-04 22:50:44 -08009
10namespace {
11
12constexpr size_t kReadSize = 50;
13constexpr double kGyroScale = 1 / 655360.0 / 360.0 * (2 * M_PI);
14constexpr double kAccelScale = 1 / 26756268.0 / 9.80665;
15constexpr double kTempScale = 0.1;
16
17} // namespace
18
Austin Schuh18cb1df2023-01-28 11:13:35 -080019Imu::Imu(aos::ShmEventLoop *event_loop, double encoder_scalar)
Ravago Jonese12b7902022-02-04 22:50:44 -080020 : event_loop_(event_loop),
21 imu_sender_(
Austin Schuh18cb1df2023-01-28 11:13:35 -080022 event_loop_->MakeSender<frc971::IMUValuesBatch>("/localizer")),
23 encoder_scalar_(encoder_scalar) {
Ravago Jonese12b7902022-02-04 22:50:44 -080024 imu_fd_ = open("/dev/adis16505", O_RDONLY | O_NONBLOCK);
25 PCHECK(imu_fd_ != -1) << ": Failed to open SPI device for IMU.";
26 aos::internal::EPoll *epoll = event_loop_->epoll();
27 epoll->OnReadable(imu_fd_, [this]() {
28 uint8_t buf[kReadSize];
29 ssize_t read_len = read(imu_fd_, buf, kReadSize);
30 // TODO: Do we care about gracefully handling EAGAIN or anything else?
31 // This should only get called when there is data.
32 PCHECK(read_len != -1);
33 CHECK_EQ(read_len, static_cast<ssize_t>(kReadSize))
34 << ": Read incorrect number of bytes.";
35
36 auto sender = imu_sender_.MakeBuilder();
37
38 const flatbuffers::Offset<frc971::IMUValues> values_offset =
39 ProcessReading(sender.fbb(), absl::Span(buf, kReadSize));
40 const flatbuffers::Offset<
41 flatbuffers::Vector<flatbuffers::Offset<frc971::IMUValues>>>
42 readings_offset = sender.fbb()->CreateVector(&values_offset, 1);
43 frc971::IMUValuesBatch::Builder batch_builder =
44 sender.MakeBuilder<frc971::IMUValuesBatch>();
45 batch_builder.add_readings(readings_offset);
46 imu_sender_.CheckOk(sender.Send(batch_builder.Finish()));
47 });
48}
49
50flatbuffers::Offset<frc971::IMUValues> Imu::ProcessReading(
51 flatbuffers::FlatBufferBuilder *fbb, const absl::Span<uint8_t> message) {
52 absl::Span<const uint8_t> buf = message;
53
54 uint64_t driver_timestamp;
55 memcpy(&driver_timestamp, buf.data(), sizeof(driver_timestamp));
56 buf = buf.subspan(8);
57
58 uint16_t diag_stat;
59 memcpy(&diag_stat, buf.data(), sizeof(diag_stat));
60 buf = buf.subspan(2);
61
62 double x_gyro = ConvertValue32(buf, kGyroScale);
63 buf = buf.subspan(4);
64 double y_gyro = ConvertValue32(buf, kGyroScale);
65 buf = buf.subspan(4);
66 double z_gyro = ConvertValue32(buf, kGyroScale);
67 buf = buf.subspan(4);
68 double x_accel = ConvertValue32(buf, kAccelScale);
69 buf = buf.subspan(4);
70 double y_accel = ConvertValue32(buf, kAccelScale);
71 buf = buf.subspan(4);
72 double z_accel = ConvertValue32(buf, kAccelScale);
73 buf = buf.subspan(4);
74 double temp = ConvertValue16(buf, kTempScale);
75 buf = buf.subspan(2);
76 uint16_t data_counter;
77 memcpy(&data_counter, buf.data(), sizeof(data_counter));
78 buf = buf.subspan(2);
79 uint32_t pico_timestamp;
80 memcpy(&pico_timestamp, buf.data(), sizeof(pico_timestamp));
81 buf = buf.subspan(4);
82 int16_t encoder1_count;
83 memcpy(&encoder1_count, buf.data(), sizeof(encoder1_count));
84 buf = buf.subspan(2);
85 int16_t encoder2_count;
86 memcpy(&encoder2_count, buf.data(), sizeof(encoder2_count));
87 buf = buf.subspan(2);
88 uint32_t checksum;
89 memcpy(&checksum, buf.data(), sizeof(checksum));
90 buf = buf.subspan(4);
91
92 CHECK(buf.empty()) << "Have leftover bytes: " << buf.size();
93
94 u_int32_t calculated_checksum = aos::ComputeCrc32(message.subspan(8, 38));
95
96 if (checksum != calculated_checksum) {
97 this->failed_checksums_++;
98 }
99
100 const auto diag_stat_offset = PackDiagStat(fbb, diag_stat);
101
102 frc971::IMUValues::Builder imu_builder(*fbb);
103
104 if (checksum == calculated_checksum) {
105 constexpr uint16_t kChecksumMismatch = 1 << 0;
106 bool imu_checksum_matched = !(diag_stat & kChecksumMismatch);
107
108 // data from the IMU packet
109 if (imu_checksum_matched) {
110 imu_builder.add_gyro_x(x_gyro);
111 imu_builder.add_gyro_y(y_gyro);
112 imu_builder.add_gyro_z(z_gyro);
113
114 imu_builder.add_accelerometer_x(x_accel);
115 imu_builder.add_accelerometer_y(y_accel);
116 imu_builder.add_accelerometer_z(z_accel);
117
118 imu_builder.add_temperature(temp);
119
120 imu_builder.add_data_counter(data_counter);
121 }
122
123 // extra data from the pico
124 imu_builder.add_pico_timestamp_us(pico_timestamp);
Austin Schuh18cb1df2023-01-28 11:13:35 -0800125 imu_builder.add_left_encoder(-encoder_scalar_ * encoder2_count);
126 imu_builder.add_right_encoder(encoder_scalar_ * encoder1_count);
Ravago Jonese12b7902022-02-04 22:50:44 -0800127 imu_builder.add_previous_reading_diag_stat(diag_stat_offset);
128 }
129
130 // extra data from us
131 imu_builder.add_monotonic_timestamp_ns(driver_timestamp);
132 imu_builder.add_failed_checksums(failed_checksums_);
133 imu_builder.add_checksum_failed(checksum != calculated_checksum);
134
135 return imu_builder.Finish();
136}
137
138flatbuffers::Offset<frc971::ADIS16470DiagStat> Imu::PackDiagStat(
139 flatbuffers::FlatBufferBuilder *fbb, uint16_t value) {
140 frc971::ADIS16470DiagStat::Builder diag_stat_builder(*fbb);
141 diag_stat_builder.add_clock_error(value & (1 << 7));
142 diag_stat_builder.add_memory_failure(value & (1 << 6));
143 diag_stat_builder.add_sensor_failure(value & (1 << 5));
144 diag_stat_builder.add_standby_mode(value & (1 << 4));
145 diag_stat_builder.add_spi_communication_error(value & (1 << 3));
146 diag_stat_builder.add_flash_memory_update_error(value & (1 << 2));
147 diag_stat_builder.add_data_path_overrun(value & (1 << 1));
148 diag_stat_builder.add_checksum_mismatch(value & (1 << 0));
149 return diag_stat_builder.Finish();
150}
151
152double Imu::ConvertValue32(absl::Span<const uint8_t> data,
153 double lsb_per_output) {
154 int32_t value;
155 memcpy(&value, data.data(), sizeof(value));
156 return static_cast<double>(value) * lsb_per_output;
157}
158
159double Imu::ConvertValue16(absl::Span<const uint8_t> data,
160 double lsb_per_output) {
161 int16_t value;
162 memcpy(&value, data.data(), sizeof(value));
163 return static_cast<double>(value) * lsb_per_output;
164}
165
166Imu::~Imu() { PCHECK(0 == close(imu_fd_)); }
Austin Schuh18cb1df2023-01-28 11:13:35 -0800167
168} // namespace frc971::imu