blob: b3b6550ca590eb3564dde347228fc2bbf2d91e13 [file] [log] [blame]
Brian Silverman246cb222019-02-02 16:38:18 -08001#include "y2019/jevois/spi.h"
2
Brian Silverman246cb222019-02-02 16:38:18 -08003#include "aos/util/bitpacking.h"
Brian Silverman0a5e7db2019-02-17 13:45:27 -08004#include "y2019/jevois/jevois_crc.h"
Brian Silvermanfdfb3132019-02-24 15:27:27 -08005#ifdef __linux__
6#include "aos/logging/logging.h"
7#else
Austin Schuhf257f3c2019-10-27 21:00:43 -07008#define AOS_CHECK(...)
9#define AOS_CHECK_GE(...)
Brian Silvermanfdfb3132019-02-24 15:27:27 -080010#endif
Brian Silverman246cb222019-02-02 16:38:18 -080011
12// SPI transfer format (6x 8 bit frames):
13// 1. 1-byte brightness for each beacon channel.
14// 2. 1-byte specifying on/off for each light ring.
15// 3. 2-byte CRC
16//
17// SPI transfer format (41x 8 bit frames):
18// 1. Camera frame 0
19// 2. Camera frame 1
20// 3. Camera frame 2
21// 4. 2-byte CRC-16
22// Each camera frame (13x 8 bit frames):
23// 1. Duration for how old the frame is. This is a value received from the
24// camera, added to the time between the first character being received
25// by the MCU to the CS line being asserted. Specifically it's an 8 bit
26// unsigned number of ms.
27// 2. Target 0
28// 3. Target 1
29// 4. Target 2
30// Each target (4x 8 bit frames):
31// 1. 10 bits heading
32// 2. 8 bits distance
33// 3. 6 bits skew
34// 4. 6 bits height
Brian Silvermanc41fb862019-03-02 21:14:46 -080035// 5. 2 bits of quantity+index
36// The 6 bits of quantity+index (between all three targets) are:
37// 1. 4 bits camera index + 1 (0 means this isn't a valid frame)
38// 2. 2 bits target count
Brian Silverman246cb222019-02-02 16:38:18 -080039// Note that empty frames are still sent to indicate that the camera is
40// still working even though it doesn't see any targets.
41
42namespace frc971 {
43namespace jevois {
44namespace {
45
46constexpr float heading_min() { return -3; }
47constexpr float heading_max() { return 3; }
48constexpr int heading_bits() { return 10; }
49constexpr int heading_offset() { return 0; }
Austin Schuhb72be802022-01-02 12:26:28 -080050void heading_pack(float heading, absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -080051 const auto integer = aos::FloatToIntLinear<heading_bits()>(
52 heading_min(), heading_max(), heading);
53 aos::PackBits<uint32_t, heading_bits(), heading_offset()>(integer,
54 destination);
55}
Austin Schuhb72be802022-01-02 12:26:28 -080056float heading_unpack(absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -080057 const auto integer =
58 aos::UnpackBits<uint32_t, heading_bits(), heading_offset()>(source);
59 return aos::IntToFloatLinear<heading_bits()>(heading_min(), heading_max(),
60 integer);
61}
62
63constexpr float distance_min() { return 0; }
Austin Schuhf61d5e62019-03-03 18:35:02 -080064constexpr float distance_max() { return 10.0; }
Brian Silverman246cb222019-02-02 16:38:18 -080065constexpr int distance_bits() { return 8; }
66constexpr int distance_offset() { return heading_offset() + heading_bits(); }
Austin Schuhb72be802022-01-02 12:26:28 -080067void distance_pack(float distance, absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -080068 const auto integer = aos::FloatToIntLinear<distance_bits()>(
69 distance_min(), distance_max(), distance);
70 aos::PackBits<uint32_t, distance_bits(), distance_offset()>(integer,
71 destination);
72}
Austin Schuhb72be802022-01-02 12:26:28 -080073float distance_unpack(absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -080074 const auto integer =
75 aos::UnpackBits<uint32_t, distance_bits(), distance_offset()>(source);
76 return aos::IntToFloatLinear<distance_bits()>(distance_min(), distance_max(),
77 integer);
78}
79
80constexpr float skew_min() { return -3; }
81constexpr float skew_max() { return 3; }
82constexpr int skew_bits() { return 6; }
83constexpr int skew_offset() { return distance_offset() + distance_bits(); }
Austin Schuhb72be802022-01-02 12:26:28 -080084void skew_pack(float skew, absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -080085 const auto integer =
86 aos::FloatToIntLinear<skew_bits()>(skew_min(), skew_max(), skew);
87 aos::PackBits<uint32_t, skew_bits(), skew_offset()>(integer, destination);
88}
Austin Schuhb72be802022-01-02 12:26:28 -080089float skew_unpack(absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -080090 const auto integer =
91 aos::UnpackBits<uint32_t, skew_bits(), skew_offset()>(source);
92 return aos::IntToFloatLinear<skew_bits()>(skew_min(), skew_max(), integer);
93}
94
95constexpr float height_min() { return 0; }
96constexpr float height_max() { return 1.5; }
97constexpr int height_bits() { return 6; }
98constexpr int height_offset() { return skew_offset() + skew_bits(); }
Austin Schuhb72be802022-01-02 12:26:28 -080099void height_pack(float height, absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -0800100 const auto integer =
101 aos::FloatToIntLinear<height_bits()>(height_min(), height_max(), height);
102 aos::PackBits<uint32_t, height_bits(), height_offset()>(integer, destination);
103}
Austin Schuhb72be802022-01-02 12:26:28 -0800104float height_unpack(absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -0800105 const auto integer =
106 aos::UnpackBits<uint32_t, height_bits(), height_offset()>(source);
107 return aos::IntToFloatLinear<height_bits()>(height_min(), height_max(),
108 integer);
109}
110
Austin Schuhb72be802022-01-02 12:26:28 -0800111constexpr int quantity_index_offset() {
112 return height_offset() + height_bits();
113}
114void camera_index_pack(int camera_index, absl::Span<char> destination) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800115 aos::PackBits<uint32_t, 2, quantity_index_offset()>(camera_index & 3,
116 destination);
117 aos::PackBits<uint32_t, 2, quantity_index_offset() + 32>(
118 (camera_index >> 2) & 3, destination);
Brian Silverman246cb222019-02-02 16:38:18 -0800119}
Austin Schuhb72be802022-01-02 12:26:28 -0800120int camera_index_unpack(absl::Span<const char> source) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800121 int result = 0;
122 result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset()>(source);
123 result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32>(source)
124 << 2;
125 return result;
126}
Austin Schuhb72be802022-01-02 12:26:28 -0800127void target_count_pack(int target_count, absl::Span<char> destination) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800128 aos::PackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(target_count,
129 destination);
130}
Austin Schuhb72be802022-01-02 12:26:28 -0800131int target_count_unpack(absl::Span<const char> source) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800132 return aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(source);
Brian Silverman246cb222019-02-02 16:38:18 -0800133}
134
Brian Silvermanc41fb862019-03-02 21:14:46 -0800135constexpr int next_offset() { return quantity_index_offset() + 2; }
Brian Silverman246cb222019-02-02 16:38:18 -0800136static_assert(next_offset() <= 32, "Target is too big");
137
138} // namespace
139
140SpiTransfer SpiPackToRoborio(const TeensyToRoborio &message) {
141 SpiTransfer transfer;
Austin Schuhb72be802022-01-02 12:26:28 -0800142 absl::Span<char> remaining_space = absl::Span<char>(transfer);
Brian Silverman246cb222019-02-02 16:38:18 -0800143 for (int frame = 0; frame < 3; ++frame) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800144 // Zero out all three targets and the age.
145 for (int i = 0; i < 3 * 4 + 1; ++i) {
146 remaining_space[i] = 0;
147 }
Brian Silverman246cb222019-02-02 16:38:18 -0800148
Brian Silvermanc41fb862019-03-02 21:14:46 -0800149 if (static_cast<int>(message.frames.size()) > frame) {
150 camera_index_pack(message.frames[frame].camera_index + 1,
151 remaining_space);
152 target_count_pack(message.frames[frame].targets.size(), remaining_space);
153
154 for (int target = 0; target < 3; ++target) {
Brian Silverman246cb222019-02-02 16:38:18 -0800155 if (static_cast<int>(message.frames[frame].targets.size()) > target) {
156 heading_pack(message.frames[frame].targets[target].heading,
157 remaining_space);
158 distance_pack(message.frames[frame].targets[target].distance,
159 remaining_space);
160 skew_pack(message.frames[frame].targets[target].skew,
161 remaining_space);
162 height_pack(message.frames[frame].targets[target].height,
163 remaining_space);
Brian Silverman246cb222019-02-02 16:38:18 -0800164 }
Brian Silvermanc41fb862019-03-02 21:14:46 -0800165 remaining_space = remaining_space.subspan(4);
Brian Silverman246cb222019-02-02 16:38:18 -0800166 }
167
Brian Silverman246cb222019-02-02 16:38:18 -0800168 const uint8_t age_count = message.frames[frame].age.count();
169 memcpy(&remaining_space[0], &age_count, 1);
Brian Silvermanc41fb862019-03-02 21:14:46 -0800170 remaining_space = remaining_space.subspan(1);
Brian Silverman246cb222019-02-02 16:38:18 -0800171 } else {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800172 remaining_space = remaining_space.subspan(4 * 3 + 1);
Brian Silverman246cb222019-02-02 16:38:18 -0800173 }
Brian Silverman246cb222019-02-02 16:38:18 -0800174 }
175 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800176 uint16_t crc = jevois_crc_init();
177 crc = jevois_crc_update(crc, transfer.data(),
178 transfer.size() - remaining_space.size());
179 crc = jevois_crc_finalize(crc);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700180 AOS_CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800181 memcpy(&remaining_space[0], &crc, sizeof(crc));
182 remaining_space = remaining_space.subspan(sizeof(crc));
183 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700184 AOS_CHECK(remaining_space.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800185 return transfer;
186}
187
James Kuszmaul3ae42262019-11-08 12:33:41 -0800188std::optional<TeensyToRoborio> SpiUnpackToRoborio(
Austin Schuhb72be802022-01-02 12:26:28 -0800189 absl::Span<const char> transfer) {
190 if (transfer.size() != spi_transfer_size()) {
191 return std::nullopt;
192 }
Brian Silverman246cb222019-02-02 16:38:18 -0800193 TeensyToRoborio message;
Austin Schuhb72be802022-01-02 12:26:28 -0800194 absl::Span<const char> remaining_input = transfer;
Brian Silverman246cb222019-02-02 16:38:18 -0800195 for (int frame = 0; frame < 3; ++frame) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800196 const int camera_index_plus = camera_index_unpack(remaining_input);
197 if (camera_index_plus > 0) {
Brian Silverman246cb222019-02-02 16:38:18 -0800198 message.frames.push_back({});
Brian Silvermanc41fb862019-03-02 21:14:46 -0800199 message.frames.back().camera_index = camera_index_plus - 1;
200
201 const int target_count = target_count_unpack(remaining_input);
202 for (int target = 0; target < 3; ++target) {
203 if (target < target_count) {
Brian Silverman246cb222019-02-02 16:38:18 -0800204 message.frames.back().targets.push_back({});
205 message.frames.back().targets.back().heading =
206 heading_unpack(remaining_input);
207 message.frames.back().targets.back().distance =
208 distance_unpack(remaining_input);
209 message.frames.back().targets.back().skew =
210 skew_unpack(remaining_input);
211 message.frames.back().targets.back().height =
212 height_unpack(remaining_input);
213 }
Brian Silvermanc41fb862019-03-02 21:14:46 -0800214 remaining_input = remaining_input.subspan(4);
Brian Silverman246cb222019-02-02 16:38:18 -0800215 }
216
Brian Silvermanc41fb862019-03-02 21:14:46 -0800217 {
218 uint8_t age_count;
219 memcpy(&age_count, &remaining_input[0], 1);
220 message.frames.back().age = camera_duration(age_count);
221 }
222 remaining_input = remaining_input.subspan(1);
223 } else {
224 remaining_input = remaining_input.subspan(4 * 3 + 1);
Brian Silverman246cb222019-02-02 16:38:18 -0800225 }
Brian Silverman246cb222019-02-02 16:38:18 -0800226 }
227 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800228 uint16_t calculated_crc = jevois_crc_init();
Brian Silverman246cb222019-02-02 16:38:18 -0800229 calculated_crc =
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800230 jevois_crc_update(calculated_crc, transfer.data(),
231 transfer.size() - remaining_input.size());
232 calculated_crc = jevois_crc_finalize(calculated_crc);
Brian Silverman246cb222019-02-02 16:38:18 -0800233 uint16_t received_crc;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700234 AOS_CHECK_GE(static_cast<size_t>(remaining_input.size()),
235 sizeof(received_crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800236 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
237 remaining_input = remaining_input.subspan(sizeof(received_crc));
Austin Schuhf257f3c2019-10-27 21:00:43 -0700238 AOS_CHECK(remaining_input.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800239 if (calculated_crc != received_crc) {
James Kuszmaul3ae42262019-11-08 12:33:41 -0800240 return std::nullopt;
Brian Silverman246cb222019-02-02 16:38:18 -0800241 }
242 }
Brian Silverman246cb222019-02-02 16:38:18 -0800243 return message;
244}
245
Brian Silvermana10b87e2019-02-24 15:20:07 -0800246SpiTransfer SpiPackToTeensy(const RoborioToTeensy &message) {
247 SpiTransfer transfer;
Austin Schuhb72be802022-01-02 12:26:28 -0800248 absl::Span<char> remaining_space = absl::Span<char>(transfer);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800249 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
250 remaining_space[0] = message.beacon_brightness[i];
251 remaining_space = remaining_space.subspan(1);
252 }
253 remaining_space[0] = message.light_rings.to_ulong() & 0xFF;
254 remaining_space = remaining_space.subspan(1);
255 {
256 const int64_t realtime_now =
257 message.realtime_now.time_since_epoch().count();
258 memcpy(remaining_space.data(), &realtime_now, sizeof(realtime_now));
259 remaining_space = remaining_space.subspan(sizeof(realtime_now));
260 }
Brian Silvermane9924fd2019-03-02 15:20:42 -0800261 memcpy(remaining_space.data(), &message.camera_command, 1);
262 remaining_space = remaining_space.subspan(1);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800263 {
264 uint16_t crc = jevois_crc_init();
265 crc = jevois_crc_update(crc, transfer.data(),
266 transfer.size() - remaining_space.size());
267 crc = jevois_crc_finalize(crc);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700268 AOS_CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
Brian Silvermana10b87e2019-02-24 15:20:07 -0800269 memcpy(&remaining_space[0], &crc, sizeof(crc));
270 remaining_space = remaining_space.subspan(sizeof(crc));
271 }
272 return transfer;
273}
274
James Kuszmaul3ae42262019-11-08 12:33:41 -0800275std::optional<RoborioToTeensy> SpiUnpackToTeensy(
Austin Schuhb72be802022-01-02 12:26:28 -0800276 absl::Span<const char> transfer) {
277 if (transfer.size() != spi_transfer_size()) {
278 return std::nullopt;
279 }
Brian Silvermana10b87e2019-02-24 15:20:07 -0800280 RoborioToTeensy message;
Austin Schuhb72be802022-01-02 12:26:28 -0800281 absl::Span<const char> remaining_input = transfer;
Brian Silvermana10b87e2019-02-24 15:20:07 -0800282 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
283 message.beacon_brightness[i] = remaining_input[0];
284 remaining_input = remaining_input.subspan(1);
285 }
286 message.light_rings = remaining_input[0];
287 remaining_input = remaining_input.subspan(1);
288 {
289 int64_t realtime_now;
290 memcpy(&realtime_now, remaining_input.data(), sizeof(realtime_now));
291 message.realtime_now = aos::realtime_clock::time_point(
292 aos::realtime_clock::duration(realtime_now));
293 remaining_input = remaining_input.subspan(sizeof(realtime_now));
294 }
Brian Silvermane9924fd2019-03-02 15:20:42 -0800295 memcpy(&message.camera_command, remaining_input.data(), 1);
296 remaining_input = remaining_input.subspan(1);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800297 {
298 uint16_t calculated_crc = jevois_crc_init();
299 calculated_crc =
300 jevois_crc_update(calculated_crc, transfer.data(),
301 transfer.size() - remaining_input.size());
302 calculated_crc = jevois_crc_finalize(calculated_crc);
303 uint16_t received_crc;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700304 AOS_CHECK_GE(static_cast<size_t>(remaining_input.size()),
305 sizeof(received_crc));
Brian Silvermana10b87e2019-02-24 15:20:07 -0800306 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
307 remaining_input = remaining_input.subspan(sizeof(received_crc));
308 if (calculated_crc != received_crc) {
James Kuszmaul3ae42262019-11-08 12:33:41 -0800309 return std::nullopt;
Brian Silvermana10b87e2019-02-24 15:20:07 -0800310 }
311 }
312 return message;
313}
314
Brian Silverman246cb222019-02-02 16:38:18 -0800315} // namespace jevois
316} // namespace frc971