blob: 3223742c52b7631555a98dcada3bf779d5767d36 [file] [log] [blame]
Brian Silverman246cb222019-02-02 16:38:18 -08001#include "y2019/jevois/spi.h"
2
Brian Silverman2eb89762019-02-17 15:16:37 -08003#include "aos/logging/logging.h"
Brian Silverman246cb222019-02-02 16:38:18 -08004#include "aos/util/bitpacking.h"
Brian Silverman0a5e7db2019-02-17 13:45:27 -08005#include "y2019/jevois/jevois_crc.h"
Brian Silverman246cb222019-02-02 16:38:18 -08006
7// SPI transfer format (6x 8 bit frames):
8// 1. 1-byte brightness for each beacon channel.
9// 2. 1-byte specifying on/off for each light ring.
10// 3. 2-byte CRC
11//
12// SPI transfer format (41x 8 bit frames):
13// 1. Camera frame 0
14// 2. Camera frame 1
15// 3. Camera frame 2
16// 4. 2-byte CRC-16
17// Each camera frame (13x 8 bit frames):
18// 1. Duration for how old the frame is. This is a value received from the
19// camera, added to the time between the first character being received
20// by the MCU to the CS line being asserted. Specifically it's an 8 bit
21// unsigned number of ms.
22// 2. Target 0
23// 3. Target 1
24// 4. Target 2
25// Each target (4x 8 bit frames):
26// 1. 10 bits heading
27// 2. 8 bits distance
28// 3. 6 bits skew
29// 4. 6 bits height
30// 5. 1 bit target valid (a present frame has all-valid targets)
31// 6. 1 bit target present (a present frame can have from 0 to 3
32// targets, depending on how many were found)
33// Note that empty frames are still sent to indicate that the camera is
34// still working even though it doesn't see any targets.
35
36namespace frc971 {
37namespace jevois {
38namespace {
39
40constexpr float heading_min() { return -3; }
41constexpr float heading_max() { return 3; }
42constexpr int heading_bits() { return 10; }
43constexpr int heading_offset() { return 0; }
44void heading_pack(float heading, gsl::span<char> destination) {
45 const auto integer = aos::FloatToIntLinear<heading_bits()>(
46 heading_min(), heading_max(), heading);
47 aos::PackBits<uint32_t, heading_bits(), heading_offset()>(integer,
48 destination);
49}
50float heading_unpack(gsl::span<const char> source) {
51 const auto integer =
52 aos::UnpackBits<uint32_t, heading_bits(), heading_offset()>(source);
53 return aos::IntToFloatLinear<heading_bits()>(heading_min(), heading_max(),
54 integer);
55}
56
57constexpr float distance_min() { return 0; }
58constexpr float distance_max() {
59 // The field is 18.4m diagonally.
60 return 18.4;
61}
62constexpr int distance_bits() { return 8; }
63constexpr int distance_offset() { return heading_offset() + heading_bits(); }
64void distance_pack(float distance, gsl::span<char> destination) {
65 const auto integer = aos::FloatToIntLinear<distance_bits()>(
66 distance_min(), distance_max(), distance);
67 aos::PackBits<uint32_t, distance_bits(), distance_offset()>(integer,
68 destination);
69}
70float distance_unpack(gsl::span<const char> source) {
71 const auto integer =
72 aos::UnpackBits<uint32_t, distance_bits(), distance_offset()>(source);
73 return aos::IntToFloatLinear<distance_bits()>(distance_min(), distance_max(),
74 integer);
75}
76
77constexpr float skew_min() { return -3; }
78constexpr float skew_max() { return 3; }
79constexpr int skew_bits() { return 6; }
80constexpr int skew_offset() { return distance_offset() + distance_bits(); }
81void skew_pack(float skew, gsl::span<char> destination) {
82 const auto integer =
83 aos::FloatToIntLinear<skew_bits()>(skew_min(), skew_max(), skew);
84 aos::PackBits<uint32_t, skew_bits(), skew_offset()>(integer, destination);
85}
86float skew_unpack(gsl::span<const char> source) {
87 const auto integer =
88 aos::UnpackBits<uint32_t, skew_bits(), skew_offset()>(source);
89 return aos::IntToFloatLinear<skew_bits()>(skew_min(), skew_max(), integer);
90}
91
92constexpr float height_min() { return 0; }
93constexpr float height_max() { return 1.5; }
94constexpr int height_bits() { return 6; }
95constexpr int height_offset() { return skew_offset() + skew_bits(); }
96void height_pack(float height, gsl::span<char> destination) {
97 const auto integer =
98 aos::FloatToIntLinear<height_bits()>(height_min(), height_max(), height);
99 aos::PackBits<uint32_t, height_bits(), height_offset()>(integer, destination);
100}
101float height_unpack(gsl::span<const char> source) {
102 const auto integer =
103 aos::UnpackBits<uint32_t, height_bits(), height_offset()>(source);
104 return aos::IntToFloatLinear<height_bits()>(height_min(), height_max(),
105 integer);
106}
107
108constexpr int valid_bits() { return 1; }
109constexpr int valid_offset() { return height_offset() + height_bits(); }
110void valid_pack(bool valid, gsl::span<char> destination) {
111 aos::PackBits<uint32_t, valid_bits(), valid_offset()>(valid, destination);
112}
113bool valid_unpack(gsl::span<const char> source) {
114 return aos::UnpackBits<uint32_t, valid_bits(), valid_offset()>(source);
115}
116
117constexpr int present_bits() { return 1; }
118constexpr int present_offset() { return valid_offset() + valid_bits(); }
119void present_pack(bool present, gsl::span<char> destination) {
120 aos::PackBits<uint32_t, present_bits(), present_offset()>(present,
121 destination);
122}
123bool present_unpack(gsl::span<const char> source) {
124 return aos::UnpackBits<uint32_t, present_bits(), present_offset()>(source);
125}
126
127constexpr int next_offset() { return present_offset() + present_bits(); }
128static_assert(next_offset() <= 32, "Target is too big");
129
130} // namespace
131
132SpiTransfer SpiPackToRoborio(const TeensyToRoborio &message) {
133 SpiTransfer transfer;
134 gsl::span<char> remaining_space = transfer;
135 for (int frame = 0; frame < 3; ++frame) {
136 for (int target = 0; target < 3; ++target) {
137 remaining_space[0] = 0;
138 remaining_space[1] = 0;
139 remaining_space[2] = 0;
140 remaining_space[3] = 0;
141
142 if (static_cast<int>(message.frames.size()) > frame) {
143 valid_pack(true, remaining_space);
144 if (static_cast<int>(message.frames[frame].targets.size()) > target) {
145 heading_pack(message.frames[frame].targets[target].heading,
146 remaining_space);
147 distance_pack(message.frames[frame].targets[target].distance,
148 remaining_space);
149 skew_pack(message.frames[frame].targets[target].skew,
150 remaining_space);
151 height_pack(message.frames[frame].targets[target].height,
152 remaining_space);
153 present_pack(true, remaining_space);
154 } else {
155 present_pack(false, remaining_space);
156 }
157 } else {
158 valid_pack(false, remaining_space);
159 }
160
161 remaining_space = remaining_space.subspan(4);
162 }
163 if (static_cast<int>(message.frames.size()) > frame) {
164 const uint8_t age_count = message.frames[frame].age.count();
165 memcpy(&remaining_space[0], &age_count, 1);
166 } else {
167 remaining_space[0] = 0;
168 }
169 remaining_space = remaining_space.subspan(1);
170 }
171 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800172 uint16_t crc = jevois_crc_init();
173 crc = jevois_crc_update(crc, transfer.data(),
174 transfer.size() - remaining_space.size());
175 crc = jevois_crc_finalize(crc);
Brian Silverman2eb89762019-02-17 15:16:37 -0800176 CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800177 memcpy(&remaining_space[0], &crc, sizeof(crc));
178 remaining_space = remaining_space.subspan(sizeof(crc));
179 }
Brian Silverman2eb89762019-02-17 15:16:37 -0800180 CHECK(remaining_space.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800181 return transfer;
182}
183
Brian Silvermana10b87e2019-02-24 15:20:07 -0800184tl::optional<TeensyToRoborio> SpiUnpackToRoborio(
185 gsl::span<const char, spi_transfer_size()> transfer) {
Brian Silverman246cb222019-02-02 16:38:18 -0800186 TeensyToRoborio message;
187 gsl::span<const char> remaining_input = transfer;
188 for (int frame = 0; frame < 3; ++frame) {
189 const bool have_frame = valid_unpack(remaining_input);
190 if (have_frame) {
191 message.frames.push_back({});
192 }
193 for (int target = 0; target < 3; ++target) {
194 if (present_unpack(remaining_input)) {
195 if (have_frame) {
196 message.frames.back().targets.push_back({});
197 message.frames.back().targets.back().heading =
198 heading_unpack(remaining_input);
199 message.frames.back().targets.back().distance =
200 distance_unpack(remaining_input);
201 message.frames.back().targets.back().skew =
202 skew_unpack(remaining_input);
203 message.frames.back().targets.back().height =
204 height_unpack(remaining_input);
205 }
206 }
207
208 remaining_input = remaining_input.subspan(4);
209 }
210 if (have_frame) {
211 uint8_t age_count;
212 memcpy(&age_count, &remaining_input[0], 1);
213 message.frames.back().age = camera_duration(age_count);
214 }
215 remaining_input = remaining_input.subspan(1);
216 }
217 {
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800218 uint16_t calculated_crc = jevois_crc_init();
Brian Silverman246cb222019-02-02 16:38:18 -0800219 calculated_crc =
Brian Silverman0a5e7db2019-02-17 13:45:27 -0800220 jevois_crc_update(calculated_crc, transfer.data(),
221 transfer.size() - remaining_input.size());
222 calculated_crc = jevois_crc_finalize(calculated_crc);
Brian Silverman246cb222019-02-02 16:38:18 -0800223 uint16_t received_crc;
Brian Silverman2eb89762019-02-17 15:16:37 -0800224 CHECK_GE(static_cast<size_t>(remaining_input.size()), sizeof(received_crc));
Brian Silverman246cb222019-02-02 16:38:18 -0800225 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
226 remaining_input = remaining_input.subspan(sizeof(received_crc));
Brian Silverman2eb89762019-02-17 15:16:37 -0800227 CHECK(remaining_input.empty());
Brian Silverman246cb222019-02-02 16:38:18 -0800228 if (calculated_crc != received_crc) {
229 return tl::nullopt;
230 }
231 }
Brian Silverman246cb222019-02-02 16:38:18 -0800232 return message;
233}
234
Brian Silvermana10b87e2019-02-24 15:20:07 -0800235SpiTransfer SpiPackToTeensy(const RoborioToTeensy &message) {
236 SpiTransfer transfer;
237 gsl::span<char> remaining_space = transfer;
238 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
239 remaining_space[0] = message.beacon_brightness[i];
240 remaining_space = remaining_space.subspan(1);
241 }
242 remaining_space[0] = message.light_rings.to_ulong() & 0xFF;
243 remaining_space = remaining_space.subspan(1);
244 {
245 const int64_t realtime_now =
246 message.realtime_now.time_since_epoch().count();
247 memcpy(remaining_space.data(), &realtime_now, sizeof(realtime_now));
248 remaining_space = remaining_space.subspan(sizeof(realtime_now));
249 }
250 {
251 uint16_t crc = jevois_crc_init();
252 crc = jevois_crc_update(crc, transfer.data(),
253 transfer.size() - remaining_space.size());
254 crc = jevois_crc_finalize(crc);
255 CHECK_GE(static_cast<size_t>(remaining_space.size()), sizeof(crc));
256 memcpy(&remaining_space[0], &crc, sizeof(crc));
257 remaining_space = remaining_space.subspan(sizeof(crc));
258 }
259 return transfer;
260}
261
262tl::optional<RoborioToTeensy> SpiUnpackToTeensy(
263 gsl::span<const char, spi_transfer_size()> transfer) {
264 RoborioToTeensy message;
265 gsl::span<const char> remaining_input = transfer;
266 for (size_t i = 0; i < message.beacon_brightness.size(); ++i) {
267 message.beacon_brightness[i] = remaining_input[0];
268 remaining_input = remaining_input.subspan(1);
269 }
270 message.light_rings = remaining_input[0];
271 remaining_input = remaining_input.subspan(1);
272 {
273 int64_t realtime_now;
274 memcpy(&realtime_now, remaining_input.data(), sizeof(realtime_now));
275 message.realtime_now = aos::realtime_clock::time_point(
276 aos::realtime_clock::duration(realtime_now));
277 remaining_input = remaining_input.subspan(sizeof(realtime_now));
278 }
279 {
280 uint16_t calculated_crc = jevois_crc_init();
281 calculated_crc =
282 jevois_crc_update(calculated_crc, transfer.data(),
283 transfer.size() - remaining_input.size());
284 calculated_crc = jevois_crc_finalize(calculated_crc);
285 uint16_t received_crc;
286 CHECK_GE(static_cast<size_t>(remaining_input.size()), sizeof(received_crc));
287 memcpy(&received_crc, &remaining_input[0], sizeof(received_crc));
288 remaining_input = remaining_input.subspan(sizeof(received_crc));
289 if (calculated_crc != received_crc) {
290 return tl::nullopt;
291 }
292 }
293 return message;
294}
295
Brian Silverman246cb222019-02-02 16:38:18 -0800296} // namespace jevois
297} // namespace frc971