blob: b6a281e10312bcff30850abeef07ae64ff9776e8 [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; }
50void heading_pack(float heading, gsl::span<char> destination) {
51 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}
56float heading_unpack(gsl::span<const char> source) {
57 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(); }
67void distance_pack(float distance, gsl::span<char> destination) {
68 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}
73float distance_unpack(gsl::span<const char> source) {
74 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(); }
84void skew_pack(float skew, gsl::span<char> destination) {
85 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}
89float skew_unpack(gsl::span<const char> source) {
90 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(); }
99void height_pack(float height, gsl::span<char> destination) {
100 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}
104float height_unpack(gsl::span<const char> source) {
105 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
Brian Silvermanc41fb862019-03-02 21:14:46 -0800111constexpr int quantity_index_offset() { return height_offset() + height_bits(); }
112void camera_index_pack(int camera_index, gsl::span<char> destination) {
113 aos::PackBits<uint32_t, 2, quantity_index_offset()>(camera_index & 3,
114 destination);
115 aos::PackBits<uint32_t, 2, quantity_index_offset() + 32>(
116 (camera_index >> 2) & 3, destination);
Brian Silverman246cb222019-02-02 16:38:18 -0800117}
Brian Silvermanc41fb862019-03-02 21:14:46 -0800118int camera_index_unpack(gsl::span<const char> source) {
119 int result = 0;
120 result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset()>(source);
121 result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32>(source)
122 << 2;
123 return result;
124}
125void target_count_pack(int target_count, gsl::span<char> destination) {
126 aos::PackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(target_count,
127 destination);
128}
129int target_count_unpack(gsl::span<const char> source) {
130 return aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(source);
Brian Silverman246cb222019-02-02 16:38:18 -0800131}
132
Brian Silvermanc41fb862019-03-02 21:14:46 -0800133constexpr int next_offset() { return quantity_index_offset() + 2; }
Brian Silverman246cb222019-02-02 16:38:18 -0800134static_assert(next_offset() <= 32, "Target is too big");
135
136} // namespace
137
138SpiTransfer SpiPackToRoborio(const TeensyToRoborio &message) {
139 SpiTransfer transfer;
140 gsl::span<char> remaining_space = transfer;
141 for (int frame = 0; frame < 3; ++frame) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800142 // Zero out all three targets and the age.
143 for (int i = 0; i < 3 * 4 + 1; ++i) {
144 remaining_space[i] = 0;
145 }
Brian Silverman246cb222019-02-02 16:38:18 -0800146
Brian Silvermanc41fb862019-03-02 21:14:46 -0800147 if (static_cast<int>(message.frames.size()) > frame) {
148 camera_index_pack(message.frames[frame].camera_index + 1,
149 remaining_space);
150 target_count_pack(message.frames[frame].targets.size(), remaining_space);
151
152 for (int target = 0; target < 3; ++target) {
Brian Silverman246cb222019-02-02 16:38:18 -0800153 if (static_cast<int>(message.frames[frame].targets.size()) > target) {
154 heading_pack(message.frames[frame].targets[target].heading,
155 remaining_space);
156 distance_pack(message.frames[frame].targets[target].distance,
157 remaining_space);
158 skew_pack(message.frames[frame].targets[target].skew,
159 remaining_space);
160 height_pack(message.frames[frame].targets[target].height,
161 remaining_space);
Brian Silverman246cb222019-02-02 16:38:18 -0800162 }
Brian Silvermanc41fb862019-03-02 21:14:46 -0800163 remaining_space = remaining_space.subspan(4);
Brian Silverman246cb222019-02-02 16:38:18 -0800164 }
165
Brian Silverman246cb222019-02-02 16:38:18 -0800166 const uint8_t age_count = message.frames[frame].age.count();
167 memcpy(&remaining_space[0], &age_count, 1);
Brian Silvermanc41fb862019-03-02 21:14:46 -0800168 remaining_space = remaining_space.subspan(1);
Brian Silverman246cb222019-02-02 16:38:18 -0800169 } else {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800170 remaining_space = remaining_space.subspan(4 * 3 + 1);
Brian Silverman246cb222019-02-02 16:38:18 -0800171 }
Brian Silverman246cb222019-02-02 16:38:18 -0800172 }
173 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800174 uint16_t crc = jevois_crc_init();
175 crc = jevois_crc_update(crc, transfer.data(),
176 transfer.size() - remaining_space.size());
177 crc = jevois_crc_finalize(crc);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700178 AOS_CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800179 memcpy(&remaining_space[0], &crc, sizeof(crc));
180 remaining_space = remaining_space.subspan(sizeof(crc));
181 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700182 AOS_CHECK(remaining_space.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800183 return transfer;
184}
185
Brian Silvermana10b87e2019-02-24 15:20:07 -0800186tl::optional<TeensyToRoborio> SpiUnpackToRoborio(
187 gsl::span<const char, spi_transfer_size()> transfer) {
Brian Silverman246cb222019-02-02 16:38:18 -0800188 TeensyToRoborio message;
189 gsl::span<const char> remaining_input = transfer;
190 for (int frame = 0; frame < 3; ++frame) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800191 const int camera_index_plus = camera_index_unpack(remaining_input);
192 if (camera_index_plus > 0) {
Brian Silverman246cb222019-02-02 16:38:18 -0800193 message.frames.push_back({});
Brian Silvermanc41fb862019-03-02 21:14:46 -0800194 message.frames.back().camera_index = camera_index_plus - 1;
195
196 const int target_count = target_count_unpack(remaining_input);
197 for (int target = 0; target < 3; ++target) {
198 if (target < target_count) {
Brian Silverman246cb222019-02-02 16:38:18 -0800199 message.frames.back().targets.push_back({});
200 message.frames.back().targets.back().heading =
201 heading_unpack(remaining_input);
202 message.frames.back().targets.back().distance =
203 distance_unpack(remaining_input);
204 message.frames.back().targets.back().skew =
205 skew_unpack(remaining_input);
206 message.frames.back().targets.back().height =
207 height_unpack(remaining_input);
208 }
Brian Silvermanc41fb862019-03-02 21:14:46 -0800209 remaining_input = remaining_input.subspan(4);
Brian Silverman246cb222019-02-02 16:38:18 -0800210 }
211
Brian Silvermanc41fb862019-03-02 21:14:46 -0800212 {
213 uint8_t age_count;
214 memcpy(&age_count, &remaining_input[0], 1);
215 message.frames.back().age = camera_duration(age_count);
216 }
217 remaining_input = remaining_input.subspan(1);
218 } else {
219 remaining_input = remaining_input.subspan(4 * 3 + 1);
Brian Silverman246cb222019-02-02 16:38:18 -0800220 }
Brian Silverman246cb222019-02-02 16:38:18 -0800221 }
222 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800223 uint16_t calculated_crc = jevois_crc_init();
Brian Silverman246cb222019-02-02 16:38:18 -0800224 calculated_crc =
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800225 jevois_crc_update(calculated_crc, transfer.data(),
226 transfer.size() - remaining_input.size());
227 calculated_crc = jevois_crc_finalize(calculated_crc);
Brian Silverman246cb222019-02-02 16:38:18 -0800228 uint16_t received_crc;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700229 AOS_CHECK_GE(static_cast<size_t>(remaining_input.size()),
230 sizeof(received_crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800231 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
232 remaining_input = remaining_input.subspan(sizeof(received_crc));
Austin Schuhf257f3c2019-10-27 21:00:43 -0700233 AOS_CHECK(remaining_input.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800234 if (calculated_crc != received_crc) {
235 return tl::nullopt;
236 }
237 }
Brian Silverman246cb222019-02-02 16:38:18 -0800238 return message;
239}
240
Brian Silvermana10b87e2019-02-24 15:20:07 -0800241SpiTransfer SpiPackToTeensy(const RoborioToTeensy &message) {
242 SpiTransfer transfer;
243 gsl::span<char> remaining_space = transfer;
244 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
245 remaining_space[0] = message.beacon_brightness[i];
246 remaining_space = remaining_space.subspan(1);
247 }
248 remaining_space[0] = message.light_rings.to_ulong() & 0xFF;
249 remaining_space = remaining_space.subspan(1);
250 {
251 const int64_t realtime_now =
252 message.realtime_now.time_since_epoch().count();
253 memcpy(remaining_space.data(), &realtime_now, sizeof(realtime_now));
254 remaining_space = remaining_space.subspan(sizeof(realtime_now));
255 }
Brian Silvermane9924fd2019-03-02 15:20:42 -0800256 memcpy(remaining_space.data(), &message.camera_command, 1);
257 remaining_space = remaining_space.subspan(1);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800258 {
259 uint16_t crc = jevois_crc_init();
260 crc = jevois_crc_update(crc, transfer.data(),
261 transfer.size() - remaining_space.size());
262 crc = jevois_crc_finalize(crc);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700263 AOS_CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
Brian Silvermana10b87e2019-02-24 15:20:07 -0800264 memcpy(&remaining_space[0], &crc, sizeof(crc));
265 remaining_space = remaining_space.subspan(sizeof(crc));
266 }
267 return transfer;
268}
269
270tl::optional<RoborioToTeensy> SpiUnpackToTeensy(
271 gsl::span<const char, spi_transfer_size()> transfer) {
272 RoborioToTeensy message;
273 gsl::span<const char> remaining_input = transfer;
274 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
275 message.beacon_brightness[i] = remaining_input[0];
276 remaining_input = remaining_input.subspan(1);
277 }
278 message.light_rings = remaining_input[0];
279 remaining_input = remaining_input.subspan(1);
280 {
281 int64_t realtime_now;
282 memcpy(&realtime_now, remaining_input.data(), sizeof(realtime_now));
283 message.realtime_now = aos::realtime_clock::time_point(
284 aos::realtime_clock::duration(realtime_now));
285 remaining_input = remaining_input.subspan(sizeof(realtime_now));
286 }
Brian Silvermane9924fd2019-03-02 15:20:42 -0800287 memcpy(&message.camera_command, remaining_input.data(), 1);
288 remaining_input = remaining_input.subspan(1);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800289 {
290 uint16_t calculated_crc = jevois_crc_init();
291 calculated_crc =
292 jevois_crc_update(calculated_crc, transfer.data(),
293 transfer.size() - remaining_input.size());
294 calculated_crc = jevois_crc_finalize(calculated_crc);
295 uint16_t received_crc;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700296 AOS_CHECK_GE(static_cast<size_t>(remaining_input.size()),
297 sizeof(received_crc));
Brian Silvermana10b87e2019-02-24 15:20:07 -0800298 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
299 remaining_input = remaining_input.subspan(sizeof(received_crc));
300 if (calculated_crc != received_crc) {
301 return tl::nullopt;
302 }
303 }
304 return message;
305}
306
Brian Silverman246cb222019-02-02 16:38:18 -0800307} // namespace jevois
308} // namespace frc971