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