blob: 85ad98886a1da5dd0b0c33e0bbdf3881850780e0 [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
Stephan Pleinesf63bde82024-01-13 15:59:33 -080042namespace frc971::jevois {
Brian Silverman246cb222019-02-02 16:38:18 -080043namespace {
44
45constexpr float heading_min() { return -3; }
46constexpr float heading_max() { return 3; }
47constexpr int heading_bits() { return 10; }
48constexpr int heading_offset() { return 0; }
Austin Schuhb72be802022-01-02 12:26:28 -080049void heading_pack(float heading, absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -080050 const auto integer = aos::FloatToIntLinear<heading_bits()>(
51 heading_min(), heading_max(), heading);
52 aos::PackBits<uint32_t, heading_bits(), heading_offset()>(integer,
53 destination);
54}
Austin Schuhb72be802022-01-02 12:26:28 -080055float heading_unpack(absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -080056 const auto integer =
57 aos::UnpackBits<uint32_t, heading_bits(), heading_offset()>(source);
58 return aos::IntToFloatLinear<heading_bits()>(heading_min(), heading_max(),
59 integer);
60}
61
62constexpr float distance_min() { return 0; }
Austin Schuhf61d5e62019-03-03 18:35:02 -080063constexpr float distance_max() { return 10.0; }
Brian Silverman246cb222019-02-02 16:38:18 -080064constexpr int distance_bits() { return 8; }
65constexpr int distance_offset() { return heading_offset() + heading_bits(); }
Austin Schuhb72be802022-01-02 12:26:28 -080066void distance_pack(float distance, absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -080067 const auto integer = aos::FloatToIntLinear<distance_bits()>(
68 distance_min(), distance_max(), distance);
69 aos::PackBits<uint32_t, distance_bits(), distance_offset()>(integer,
70 destination);
71}
Austin Schuhb72be802022-01-02 12:26:28 -080072float distance_unpack(absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -080073 const auto integer =
74 aos::UnpackBits<uint32_t, distance_bits(), distance_offset()>(source);
75 return aos::IntToFloatLinear<distance_bits()>(distance_min(), distance_max(),
76 integer);
77}
78
79constexpr float skew_min() { return -3; }
80constexpr float skew_max() { return 3; }
81constexpr int skew_bits() { return 6; }
82constexpr int skew_offset() { return distance_offset() + distance_bits(); }
Austin Schuhb72be802022-01-02 12:26:28 -080083void skew_pack(float skew, absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -080084 const auto integer =
85 aos::FloatToIntLinear<skew_bits()>(skew_min(), skew_max(), skew);
86 aos::PackBits<uint32_t, skew_bits(), skew_offset()>(integer, destination);
87}
Austin Schuhb72be802022-01-02 12:26:28 -080088float skew_unpack(absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -080089 const auto integer =
90 aos::UnpackBits<uint32_t, skew_bits(), skew_offset()>(source);
91 return aos::IntToFloatLinear<skew_bits()>(skew_min(), skew_max(), integer);
92}
93
94constexpr float height_min() { return 0; }
95constexpr float height_max() { return 1.5; }
96constexpr int height_bits() { return 6; }
97constexpr int height_offset() { return skew_offset() + skew_bits(); }
Austin Schuhb72be802022-01-02 12:26:28 -080098void height_pack(float height, absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -080099 const auto integer =
100 aos::FloatToIntLinear<height_bits()>(height_min(), height_max(), height);
101 aos::PackBits<uint32_t, height_bits(), height_offset()>(integer, destination);
102}
Austin Schuhb72be802022-01-02 12:26:28 -0800103float height_unpack(absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -0800104 const auto integer =
105 aos::UnpackBits<uint32_t, height_bits(), height_offset()>(source);
106 return aos::IntToFloatLinear<height_bits()>(height_min(), height_max(),
107 integer);
108}
109
Austin Schuhb72be802022-01-02 12:26:28 -0800110constexpr int quantity_index_offset() {
111 return height_offset() + height_bits();
112}
113void camera_index_pack(int camera_index, absl::Span<char> destination) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800114 aos::PackBits<uint32_t, 2, quantity_index_offset()>(camera_index & 3,
115 destination);
116 aos::PackBits<uint32_t, 2, quantity_index_offset() + 32>(
117 (camera_index >> 2) & 3, destination);
Brian Silverman246cb222019-02-02 16:38:18 -0800118}
Austin Schuhb72be802022-01-02 12:26:28 -0800119int camera_index_unpack(absl::Span<const char> source) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800120 int result = 0;
121 result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset()>(source);
122 result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32>(source)
123 << 2;
124 return result;
125}
Austin Schuhb72be802022-01-02 12:26:28 -0800126void target_count_pack(int target_count, absl::Span<char> destination) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800127 aos::PackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(target_count,
128 destination);
129}
Austin Schuhb72be802022-01-02 12:26:28 -0800130int target_count_unpack(absl::Span<const char> source) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800131 return aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(source);
Brian Silverman246cb222019-02-02 16:38:18 -0800132}
133
Brian Silvermanc41fb862019-03-02 21:14:46 -0800134constexpr int next_offset() { return quantity_index_offset() + 2; }
Brian Silverman246cb222019-02-02 16:38:18 -0800135static_assert(next_offset() <= 32, "Target is too big");
136
137} // namespace
138
139SpiTransfer SpiPackToRoborio(const TeensyToRoborio &message) {
140 SpiTransfer transfer;
Austin Schuhb72be802022-01-02 12:26:28 -0800141 absl::Span<char> remaining_space = absl::Span<char>(transfer);
Brian Silverman246cb222019-02-02 16:38:18 -0800142 for (int frame = 0; frame < 3; ++frame) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800143 // Zero out all three targets and the age.
144 for (int i = 0; i < 3 * 4 + 1; ++i) {
145 remaining_space[i] = 0;
146 }
Brian Silverman246cb222019-02-02 16:38:18 -0800147
Brian Silvermanc41fb862019-03-02 21:14:46 -0800148 if (static_cast<int>(message.frames.size()) > frame) {
149 camera_index_pack(message.frames[frame].camera_index + 1,
150 remaining_space);
151 target_count_pack(message.frames[frame].targets.size(), remaining_space);
152
153 for (int target = 0; target < 3; ++target) {
Brian Silverman246cb222019-02-02 16:38:18 -0800154 if (static_cast<int>(message.frames[frame].targets.size()) > target) {
155 heading_pack(message.frames[frame].targets[target].heading,
156 remaining_space);
157 distance_pack(message.frames[frame].targets[target].distance,
158 remaining_space);
159 skew_pack(message.frames[frame].targets[target].skew,
160 remaining_space);
161 height_pack(message.frames[frame].targets[target].height,
162 remaining_space);
Brian Silverman246cb222019-02-02 16:38:18 -0800163 }
Brian Silvermanc41fb862019-03-02 21:14:46 -0800164 remaining_space = remaining_space.subspan(4);
Brian Silverman246cb222019-02-02 16:38:18 -0800165 }
166
Brian Silverman246cb222019-02-02 16:38:18 -0800167 const uint8_t age_count = message.frames[frame].age.count();
168 memcpy(&remaining_space[0], &age_count, 1);
Brian Silvermanc41fb862019-03-02 21:14:46 -0800169 remaining_space = remaining_space.subspan(1);
Brian Silverman246cb222019-02-02 16:38:18 -0800170 } else {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800171 remaining_space = remaining_space.subspan(4 * 3 + 1);
Brian Silverman246cb222019-02-02 16:38:18 -0800172 }
Brian Silverman246cb222019-02-02 16:38:18 -0800173 }
174 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800175 uint16_t crc = jevois_crc_init();
176 crc = jevois_crc_update(crc, transfer.data(),
177 transfer.size() - remaining_space.size());
178 crc = jevois_crc_finalize(crc);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700179 AOS_CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800180 memcpy(&remaining_space[0], &crc, sizeof(crc));
181 remaining_space = remaining_space.subspan(sizeof(crc));
182 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700183 AOS_CHECK(remaining_space.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800184 return transfer;
185}
186
James Kuszmaul3ae42262019-11-08 12:33:41 -0800187std::optional<TeensyToRoborio> SpiUnpackToRoborio(
Austin Schuhb72be802022-01-02 12:26:28 -0800188 absl::Span<const char> transfer) {
189 if (transfer.size() != spi_transfer_size()) {
190 return std::nullopt;
191 }
Brian Silverman246cb222019-02-02 16:38:18 -0800192 TeensyToRoborio message;
Austin Schuhb72be802022-01-02 12:26:28 -0800193 absl::Span<const char> remaining_input = transfer;
Brian Silverman246cb222019-02-02 16:38:18 -0800194 for (int frame = 0; frame < 3; ++frame) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800195 const int camera_index_plus = camera_index_unpack(remaining_input);
196 if (camera_index_plus > 0) {
Brian Silverman246cb222019-02-02 16:38:18 -0800197 message.frames.push_back({});
Brian Silvermanc41fb862019-03-02 21:14:46 -0800198 message.frames.back().camera_index = camera_index_plus - 1;
199
200 const int target_count = target_count_unpack(remaining_input);
201 for (int target = 0; target < 3; ++target) {
202 if (target < target_count) {
Brian Silverman246cb222019-02-02 16:38:18 -0800203 message.frames.back().targets.push_back({});
204 message.frames.back().targets.back().heading =
205 heading_unpack(remaining_input);
206 message.frames.back().targets.back().distance =
207 distance_unpack(remaining_input);
208 message.frames.back().targets.back().skew =
209 skew_unpack(remaining_input);
210 message.frames.back().targets.back().height =
211 height_unpack(remaining_input);
212 }
Brian Silvermanc41fb862019-03-02 21:14:46 -0800213 remaining_input = remaining_input.subspan(4);
Brian Silverman246cb222019-02-02 16:38:18 -0800214 }
215
Brian Silvermanc41fb862019-03-02 21:14:46 -0800216 {
217 uint8_t age_count;
218 memcpy(&age_count, &remaining_input[0], 1);
219 message.frames.back().age = camera_duration(age_count);
220 }
221 remaining_input = remaining_input.subspan(1);
222 } else {
223 remaining_input = remaining_input.subspan(4 * 3 + 1);
Brian Silverman246cb222019-02-02 16:38:18 -0800224 }
Brian Silverman246cb222019-02-02 16:38:18 -0800225 }
226 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800227 uint16_t calculated_crc = jevois_crc_init();
Brian Silverman246cb222019-02-02 16:38:18 -0800228 calculated_crc =
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800229 jevois_crc_update(calculated_crc, transfer.data(),
230 transfer.size() - remaining_input.size());
231 calculated_crc = jevois_crc_finalize(calculated_crc);
Brian Silverman246cb222019-02-02 16:38:18 -0800232 uint16_t received_crc;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700233 AOS_CHECK_GE(static_cast<size_t>(remaining_input.size()),
234 sizeof(received_crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800235 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
236 remaining_input = remaining_input.subspan(sizeof(received_crc));
Austin Schuhf257f3c2019-10-27 21:00:43 -0700237 AOS_CHECK(remaining_input.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800238 if (calculated_crc != received_crc) {
James Kuszmaul3ae42262019-11-08 12:33:41 -0800239 return std::nullopt;
Brian Silverman246cb222019-02-02 16:38:18 -0800240 }
241 }
Brian Silverman246cb222019-02-02 16:38:18 -0800242 return message;
243}
244
Brian Silvermana10b87e2019-02-24 15:20:07 -0800245SpiTransfer SpiPackToTeensy(const RoborioToTeensy &message) {
246 SpiTransfer transfer;
Austin Schuhb72be802022-01-02 12:26:28 -0800247 absl::Span<char> remaining_space = absl::Span<char>(transfer);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800248 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
249 remaining_space[0] = message.beacon_brightness[i];
250 remaining_space = remaining_space.subspan(1);
251 }
252 remaining_space[0] = message.light_rings.to_ulong() & 0xFF;
253 remaining_space = remaining_space.subspan(1);
254 {
255 const int64_t realtime_now =
256 message.realtime_now.time_since_epoch().count();
257 memcpy(remaining_space.data(), &realtime_now, sizeof(realtime_now));
258 remaining_space = remaining_space.subspan(sizeof(realtime_now));
259 }
Brian Silvermane9924fd2019-03-02 15:20:42 -0800260 memcpy(remaining_space.data(), &message.camera_command, 1);
261 remaining_space = remaining_space.subspan(1);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800262 {
263 uint16_t crc = jevois_crc_init();
264 crc = jevois_crc_update(crc, transfer.data(),
265 transfer.size() - remaining_space.size());
266 crc = jevois_crc_finalize(crc);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700267 AOS_CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
Brian Silvermana10b87e2019-02-24 15:20:07 -0800268 memcpy(&remaining_space[0], &crc, sizeof(crc));
269 remaining_space = remaining_space.subspan(sizeof(crc));
270 }
271 return transfer;
272}
273
James Kuszmaul3ae42262019-11-08 12:33:41 -0800274std::optional<RoborioToTeensy> SpiUnpackToTeensy(
Austin Schuhb72be802022-01-02 12:26:28 -0800275 absl::Span<const char> transfer) {
276 if (transfer.size() != spi_transfer_size()) {
277 return std::nullopt;
278 }
Brian Silvermana10b87e2019-02-24 15:20:07 -0800279 RoborioToTeensy message;
Austin Schuhb72be802022-01-02 12:26:28 -0800280 absl::Span<const char> remaining_input = transfer;
Brian Silvermana10b87e2019-02-24 15:20:07 -0800281 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
282 message.beacon_brightness[i] = remaining_input[0];
283 remaining_input = remaining_input.subspan(1);
284 }
285 message.light_rings = remaining_input[0];
286 remaining_input = remaining_input.subspan(1);
287 {
288 int64_t realtime_now;
289 memcpy(&realtime_now, remaining_input.data(), sizeof(realtime_now));
290 message.realtime_now = aos::realtime_clock::time_point(
291 aos::realtime_clock::duration(realtime_now));
292 remaining_input = remaining_input.subspan(sizeof(realtime_now));
293 }
Brian Silvermane9924fd2019-03-02 15:20:42 -0800294 memcpy(&message.camera_command, remaining_input.data(), 1);
295 remaining_input = remaining_input.subspan(1);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800296 {
297 uint16_t calculated_crc = jevois_crc_init();
298 calculated_crc =
299 jevois_crc_update(calculated_crc, transfer.data(),
300 transfer.size() - remaining_input.size());
301 calculated_crc = jevois_crc_finalize(calculated_crc);
302 uint16_t received_crc;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700303 AOS_CHECK_GE(static_cast<size_t>(remaining_input.size()),
304 sizeof(received_crc));
Brian Silvermana10b87e2019-02-24 15:20:07 -0800305 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
306 remaining_input = remaining_input.subspan(sizeof(received_crc));
307 if (calculated_crc != received_crc) {
James Kuszmaul3ae42262019-11-08 12:33:41 -0800308 return std::nullopt;
Brian Silvermana10b87e2019-02-24 15:20:07 -0800309 }
310 }
311 return message;
312}
313
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800314} // namespace frc971::jevois