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