blob: a987ee743ce0edb0b6e60c8de11f2d2d028ca335 [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
8#define CHECK(...)
9#define CHECK_GE(...)
10#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; }
64constexpr float distance_max() {
65 // The field is 18.4m diagonally.
66 return 18.4;
67}
68constexpr int distance_bits() { return 8; }
69constexpr int distance_offset() { return heading_offset() + heading_bits(); }
70void distance_pack(float distance, gsl::span<char> destination) {
71 const auto integer = aos::FloatToIntLinear<distance_bits()>(
72 distance_min(), distance_max(), distance);
73 aos::PackBits<uint32_t, distance_bits(), distance_offset()>(integer,
74 destination);
75}
76float distance_unpack(gsl::span<const char> source) {
77 const auto integer =
78 aos::UnpackBits<uint32_t, distance_bits(), distance_offset()>(source);
79 return aos::IntToFloatLinear<distance_bits()>(distance_min(), distance_max(),
80 integer);
81}
82
83constexpr float skew_min() { return -3; }
84constexpr float skew_max() { return 3; }
85constexpr int skew_bits() { return 6; }
86constexpr int skew_offset() { return distance_offset() + distance_bits(); }
87void skew_pack(float skew, gsl::span<char> destination) {
88 const auto integer =
89 aos::FloatToIntLinear<skew_bits()>(skew_min(), skew_max(), skew);
90 aos::PackBits<uint32_t, skew_bits(), skew_offset()>(integer, destination);
91}
92float skew_unpack(gsl::span<const char> source) {
93 const auto integer =
94 aos::UnpackBits<uint32_t, skew_bits(), skew_offset()>(source);
95 return aos::IntToFloatLinear<skew_bits()>(skew_min(), skew_max(), integer);
96}
97
98constexpr float height_min() { return 0; }
99constexpr float height_max() { return 1.5; }
100constexpr int height_bits() { return 6; }
101constexpr int height_offset() { return skew_offset() + skew_bits(); }
102void height_pack(float height, gsl::span<char> destination) {
103 const auto integer =
104 aos::FloatToIntLinear<height_bits()>(height_min(), height_max(), height);
105 aos::PackBits<uint32_t, height_bits(), height_offset()>(integer, destination);
106}
107float height_unpack(gsl::span<const char> source) {
108 const auto integer =
109 aos::UnpackBits<uint32_t, height_bits(), height_offset()>(source);
110 return aos::IntToFloatLinear<height_bits()>(height_min(), height_max(),
111 integer);
112}
113
Brian Silvermanc41fb862019-03-02 21:14:46 -0800114constexpr int quantity_index_offset() { return height_offset() + height_bits(); }
115void camera_index_pack(int camera_index, gsl::span<char> destination) {
116 aos::PackBits<uint32_t, 2, quantity_index_offset()>(camera_index & 3,
117 destination);
118 aos::PackBits<uint32_t, 2, quantity_index_offset() + 32>(
119 (camera_index >> 2) & 3, destination);
Brian Silverman246cb222019-02-02 16:38:18 -0800120}
Brian Silvermanc41fb862019-03-02 21:14:46 -0800121int camera_index_unpack(gsl::span<const char> source) {
122 int result = 0;
123 result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset()>(source);
124 result |= aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32>(source)
125 << 2;
126 return result;
127}
128void target_count_pack(int target_count, gsl::span<char> destination) {
129 aos::PackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(target_count,
130 destination);
131}
132int target_count_unpack(gsl::span<const char> source) {
133 return aos::UnpackBits<uint32_t, 2, quantity_index_offset() + 32 * 2>(source);
Brian Silverman246cb222019-02-02 16:38:18 -0800134}
135
Brian Silvermanc41fb862019-03-02 21:14:46 -0800136constexpr int next_offset() { return quantity_index_offset() + 2; }
Brian Silverman246cb222019-02-02 16:38:18 -0800137static_assert(next_offset() <= 32, "Target is too big");
138
139} // namespace
140
141SpiTransfer SpiPackToRoborio(const TeensyToRoborio &message) {
142 SpiTransfer transfer;
143 gsl::span<char> remaining_space = transfer;
144 for (int frame = 0; frame < 3; ++frame) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800145 // Zero out all three targets and the age.
146 for (int i = 0; i < 3 * 4 + 1; ++i) {
147 remaining_space[i] = 0;
148 }
Brian Silverman246cb222019-02-02 16:38:18 -0800149
Brian Silvermanc41fb862019-03-02 21:14:46 -0800150 if (static_cast<int>(message.frames.size()) > frame) {
151 camera_index_pack(message.frames[frame].camera_index + 1,
152 remaining_space);
153 target_count_pack(message.frames[frame].targets.size(), remaining_space);
154
155 for (int target = 0; target < 3; ++target) {
Brian Silverman246cb222019-02-02 16:38:18 -0800156 if (static_cast<int>(message.frames[frame].targets.size()) > target) {
157 heading_pack(message.frames[frame].targets[target].heading,
158 remaining_space);
159 distance_pack(message.frames[frame].targets[target].distance,
160 remaining_space);
161 skew_pack(message.frames[frame].targets[target].skew,
162 remaining_space);
163 height_pack(message.frames[frame].targets[target].height,
164 remaining_space);
Brian Silverman246cb222019-02-02 16:38:18 -0800165 }
Brian Silvermanc41fb862019-03-02 21:14:46 -0800166 remaining_space = remaining_space.subspan(4);
Brian Silverman246cb222019-02-02 16:38:18 -0800167 }
168
Brian Silverman246cb222019-02-02 16:38:18 -0800169 const uint8_t age_count = message.frames[frame].age.count();
170 memcpy(&remaining_space[0], &age_count, 1);
Brian Silvermanc41fb862019-03-02 21:14:46 -0800171 remaining_space = remaining_space.subspan(1);
Brian Silverman246cb222019-02-02 16:38:18 -0800172 } else {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800173 remaining_space = remaining_space.subspan(4 * 3 + 1);
Brian Silverman246cb222019-02-02 16:38:18 -0800174 }
Brian Silverman246cb222019-02-02 16:38:18 -0800175 }
176 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800177 uint16_t crc = jevois_crc_init();
178 crc = jevois_crc_update(crc, transfer.data(),
179 transfer.size() - remaining_space.size());
180 crc = jevois_crc_finalize(crc);
Brian Silverman2eb89762019-02-17 15:16:37 -0800181 CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800182 memcpy(&remaining_space[0], &crc, sizeof(crc));
183 remaining_space = remaining_space.subspan(sizeof(crc));
184 }
Brian Silverman2eb89762019-02-17 15:16:37 -0800185 CHECK(remaining_space.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800186 return transfer;
187}
188
Brian Silvermana10b87e2019-02-24 15:20:07 -0800189tl::optional<TeensyToRoborio> SpiUnpackToRoborio(
190 gsl::span<const char, spi_transfer_size()> transfer) {
Brian Silverman246cb222019-02-02 16:38:18 -0800191 TeensyToRoborio message;
192 gsl::span<const char> remaining_input = transfer;
193 for (int frame = 0; frame < 3; ++frame) {
Brian Silvermanc41fb862019-03-02 21:14:46 -0800194 const int camera_index_plus = camera_index_unpack(remaining_input);
195 if (camera_index_plus > 0) {
Brian Silverman246cb222019-02-02 16:38:18 -0800196 message.frames.push_back({});
Brian Silvermanc41fb862019-03-02 21:14:46 -0800197 message.frames.back().camera_index = camera_index_plus - 1;
198
199 const int target_count = target_count_unpack(remaining_input);
200 for (int target = 0; target < 3; ++target) {
201 if (target < target_count) {
Brian Silverman246cb222019-02-02 16:38:18 -0800202 message.frames.back().targets.push_back({});
203 message.frames.back().targets.back().heading =
204 heading_unpack(remaining_input);
205 message.frames.back().targets.back().distance =
206 distance_unpack(remaining_input);
207 message.frames.back().targets.back().skew =
208 skew_unpack(remaining_input);
209 message.frames.back().targets.back().height =
210 height_unpack(remaining_input);
211 }
Brian Silvermanc41fb862019-03-02 21:14:46 -0800212 remaining_input = remaining_input.subspan(4);
Brian Silverman246cb222019-02-02 16:38:18 -0800213 }
214
Brian Silvermanc41fb862019-03-02 21:14:46 -0800215 {
216 uint8_t age_count;
217 memcpy(&age_count, &remaining_input[0], 1);
218 message.frames.back().age = camera_duration(age_count);
219 }
220 remaining_input = remaining_input.subspan(1);
221 } else {
222 remaining_input = remaining_input.subspan(4 * 3 + 1);
Brian Silverman246cb222019-02-02 16:38:18 -0800223 }
Brian Silverman246cb222019-02-02 16:38:18 -0800224 }
225 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800226 uint16_t calculated_crc = jevois_crc_init();
Brian Silverman246cb222019-02-02 16:38:18 -0800227 calculated_crc =
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800228 jevois_crc_update(calculated_crc, transfer.data(),
229 transfer.size() - remaining_input.size());
230 calculated_crc = jevois_crc_finalize(calculated_crc);
Brian Silverman246cb222019-02-02 16:38:18 -0800231 uint16_t received_crc;
Brian Silverman2eb89762019-02-17 15:16:37 -0800232 CHECK_GE(static_cast<size_t>(remaining_input.size()), sizeof(received_crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800233 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
234 remaining_input = remaining_input.subspan(sizeof(received_crc));
Brian Silverman2eb89762019-02-17 15:16:37 -0800235 CHECK(remaining_input.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800236 if (calculated_crc != received_crc) {
237 return tl::nullopt;
238 }
239 }
Brian Silverman246cb222019-02-02 16:38:18 -0800240 return message;
241}
242
Brian Silvermana10b87e2019-02-24 15:20:07 -0800243SpiTransfer SpiPackToTeensy(const RoborioToTeensy &message) {
244 SpiTransfer transfer;
245 gsl::span<char> remaining_space = transfer;
246 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
247 remaining_space[0] = message.beacon_brightness[i];
248 remaining_space = remaining_space.subspan(1);
249 }
250 remaining_space[0] = message.light_rings.to_ulong() & 0xFF;
251 remaining_space = remaining_space.subspan(1);
252 {
253 const int64_t realtime_now =
254 message.realtime_now.time_since_epoch().count();
255 memcpy(remaining_space.data(), &realtime_now, sizeof(realtime_now));
256 remaining_space = remaining_space.subspan(sizeof(realtime_now));
257 }
Brian Silvermane9924fd2019-03-02 15:20:42 -0800258 memcpy(remaining_space.data(), &message.camera_command, 1);
259 remaining_space = remaining_space.subspan(1);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800260 {
261 uint16_t crc = jevois_crc_init();
262 crc = jevois_crc_update(crc, transfer.data(),
263 transfer.size() - remaining_space.size());
264 crc = jevois_crc_finalize(crc);
265 CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
266 memcpy(&remaining_space[0], &crc, sizeof(crc));
267 remaining_space = remaining_space.subspan(sizeof(crc));
268 }
269 return transfer;
270}
271
272tl::optional<RoborioToTeensy> SpiUnpackToTeensy(
273 gsl::span<const char, spi_transfer_size()> transfer) {
274 RoborioToTeensy message;
275 gsl::span<const char> remaining_input = transfer;
276 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
277 message.beacon_brightness[i] = remaining_input[0];
278 remaining_input = remaining_input.subspan(1);
279 }
280 message.light_rings = remaining_input[0];
281 remaining_input = remaining_input.subspan(1);
282 {
283 int64_t realtime_now;
284 memcpy(&realtime_now, remaining_input.data(), sizeof(realtime_now));
285 message.realtime_now = aos::realtime_clock::time_point(
286 aos::realtime_clock::duration(realtime_now));
287 remaining_input = remaining_input.subspan(sizeof(realtime_now));
288 }
Brian Silvermane9924fd2019-03-02 15:20:42 -0800289 memcpy(&message.camera_command, remaining_input.data(), 1);
290 remaining_input = remaining_input.subspan(1);
Brian Silvermana10b87e2019-02-24 15:20:07 -0800291 {
292 uint16_t calculated_crc = jevois_crc_init();
293 calculated_crc =
294 jevois_crc_update(calculated_crc, transfer.data(),
295 transfer.size() - remaining_input.size());
296 calculated_crc = jevois_crc_finalize(calculated_crc);
297 uint16_t received_crc;
298 CHECK_GE(static_cast<size_t>(remaining_input.size()), sizeof(received_crc));
299 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
300 remaining_input = remaining_input.subspan(sizeof(received_crc));
301 if (calculated_crc != received_crc) {
302 return tl::nullopt;
303 }
304 }
305 return message;
306}
307
Brian Silverman246cb222019-02-02 16:38:18 -0800308} // namespace jevois
309} // namespace frc971