ReduxLib C++ 2025.0.0-beta2
Loading...
Searching...
No Matches
CanandUtils.h
1// Copyright (c) Redux Robotics and other contributors.
2// This is open source and can be modified and shared under the 3-clause BSD license.
3
4#pragma once
5#include <cstdint>
6#include <chrono>
7#include <units/time.h>
8#include <bit>
9
10/**
11 * Series of utility functions for CAN messaging and bit manipulation.
12 *
13 * For more information, see https://docs.wpilib.org/en/stable/docs/software/can-devices/can-addressing.html
14 */
16 // This is the Redux CAN id.
17 static constexpr uint8_t REDUX_CAN_ID = 14;
18
19 /**
20 * Extracts 5-bit device type code from a full message id
21 * @param fullId the full 29-bit message id
22 * @return the device type code
23 */
24 constexpr uint8_t getDeviceType(uint32_t fullId) {
25 return (fullId >> 24) & 0x1f;
26 }
27
28 /**
29 * Extracts 2-bit product id/API class from a full message id.
30 * Instead of doing a 6bit/4bit split for api class/api index, we use 2bit/8bit.
31 *
32 * @param fullId the full 29-bit message id
33 * @return the product id code
34 */
35 constexpr uint8_t getApiPage(uint32_t fullId) {
36 return (fullId >> 14) & 0x3;
37 }
38
39 /**
40 * Extracts the 8-bit API index from a full message id.
41 * Instead of doing a 6bit/4bit split for api class/api index, we use 2bit/8bit.
42 *
43 * @param fullId the full 29-bit message id
44 * @return the product id code
45 */
46 constexpr uint8_t getApiIndex(uint32_t fullId) {
47 return (fullId >> 6) & 0xff;
48 }
49
50 /**
51 * Extracts 6-bit device id from a full message id
52 * This is the "CAN id" that end users will see and care about.
53 *
54 * @param fullId the full 29-bit message id
55 * @return the device CAN id
56 */
57 constexpr uint8_t getDeviceId(uint32_t fullId) {
58 return fullId & 0x3f;
59 }
60
61 /**
62 * Checks if a full CAN id will match against device type, product id, and device id
63 * We use this to determine if a message is intended for a specific device.
64 *
65 * @param idToCompare full 29-bit id
66 * @param deviceType device id code
67 * @param devId device id
68 * @return whether the parameters matches the message id
69 */
70 constexpr bool idMatches(uint32_t idToCompare, uint8_t deviceType, uint8_t devId) {
71 return (idToCompare & 0x1f00003fu) == (((uint32_t) deviceType << 24u) | devId);
72 }
73
74 /**
75 * Construct a CAN message id to send to a Redux device.
76 *
77 * @param deviceType the device id code
78 * @param devId CAN device id
79 * @param msgId API message id
80 * @return a 29-bit full CAN message id
81 */
82 constexpr uint32_t constructMessageId(uint8_t deviceType, uint16_t devId, uint8_t msgId) {
83 return (deviceType << 24) | (REDUX_CAN_ID << 16) | (msgId << 6) | (devId);
84 }
85
86 /**
87 * Converts seconds from the units library to seconds in std::chrono::duration.
88 *
89 * Useful for condition variables.
90 *
91 * @param seconds in units::second_t terms
92 * @return seconds in std::chrono::duration terms
93 */
94 constexpr std::chrono::duration<double, std::ratio<1LL, 1LL>> toChronoSeconds(units::second_t seconds) {
95 return seconds.to<double>() * std::chrono::seconds(1);
96 }
97
98 /**
99 * Converts a byte buffer to a little endian buffer.
100 *
101 * @param dst destination ptr
102 * @param src source ptr
103 * @param len the length of the data in bytes. If less than 8, the unused bit-space will be zeros.
104 */
105 constexpr void memcpyLE(void* dst, void* src, size_t len) {
106
107 uint8_t* dst8 = (uint8_t*) dst;
108 uint8_t* src8 = (uint8_t*) src;
109 // we are NOT supporting big-endian. if the MRC is big-endian i will start questioning everything
110 if constexpr (std::endian::native == std::endian::little) {
111#ifdef __linux__
112 memcpy(dst, src, len);
113#else
114 for (size_t i = 0; i < len; i++) {
115 dst8[i] = src8[i];
116 }
117
118#endif
119 } else {
120 for (size_t i = 0; i < len; i++) {
121 dst8[len - i - 1] = src8[i];
122 }
123 }
124 }
125
126 /**
127 * Extracts an unsigned integer up to 8 bits wide.
128 * @param data bitfield to extract from
129 * @param width width of integer in bits. Values larger than 8 are undefined behavior.
130 * @param offset bit offset of the integer to extract
131 * @return extracted integer
132 */
133 constexpr uint8_t extractU8(uint64_t data, uint8_t width, uint8_t offset) {
134 uint64_t mask = ((1ull) << width) - 1;
135 return static_cast<uint8_t>((data >> offset) & mask);
136 }
137
138 /**
139 * Extracts an unsigned integer up to 16 bits wide.
140 * @param data bitfield to extract from
141 * @param width width of integer in bits. Values larger than 16 are undefined behavior.
142 * @param offset bit offset of the integer to extract
143 * @return extracted integer
144 */
145 constexpr uint16_t extractU16(uint64_t data, uint8_t width, uint8_t offset) {
146 uint64_t mask = ((1ull) << width) - 1;
147 return static_cast<uint16_t>((data >> offset) & mask);
148 }
149
150 /**
151 * Extracts an unsigned integer up to 32 bits wide.
152 * @param data bitfield to extract from
153 * @param width width of integer in bits. Values larger than 32 are undefined behavior.
154 * @param offset bit offset of the integer to extract
155 * @return extracted integer
156 */
157 constexpr uint32_t extractU32(uint64_t data, uint8_t width, uint8_t offset) {
158 uint64_t mask = ((1ull) << width) - 1;
159 return static_cast<uint32_t>((data >> offset) & mask);
160 }
161
162 /**
163 * Extracts an unsigned integer up to 64 bits wide.
164 * @param data bitfield to extract from
165 * @param width width of integer in bits. Values larger than 64 are undefined behavior.
166 * @param offset bit offset of the integer to extract
167 * @return extracted integer
168 */
169 constexpr uint64_t extractU64(uint64_t data, uint8_t width, uint8_t offset) {
170 uint64_t mask = ((1ull) << width) - 1;
171 return static_cast<uint64_t>((data >> offset) & mask);
172 }
173
174
175 /**
176 * Extracts a signed integer up to 8 bits wide, performing a sign extension if necessary.
177 * @param data bitfield to extract from
178 * @param width width of integer in bits. Values larger than 8 are undefined behavior.
179 * @param offset bit offset of the integer to extract
180 * @return extracted integer
181 */
182 constexpr int8_t extractI8(uint64_t data, uint8_t width, uint8_t offset) {
183 const size_t BIT_WIDTH = (sizeof(int8_t) << 3);
184 int8_t result = static_cast<int8_t>(extractU8(data, width, offset));
185 if (width >= BIT_WIDTH) { return result; }
186 uint8_t shift = (BIT_WIDTH - width);
187 return (result << shift) >> shift;
188 }
189
190 /**
191 * Extracts a signed integer up to 16 bits wide, performing a sign extension if necessary.
192 * @param data bitfield to extract from
193 * @param width width of integer in bits. Values larger than 16 are undefined behavior.
194 * @param offset bit offset of the integer to extract
195 * @return extracted integer
196 */
197 constexpr int16_t extractI16(uint64_t data, uint8_t width, uint8_t offset) {
198 const size_t BIT_WIDTH = (sizeof(int16_t) << 3);
199 int16_t result = static_cast<int16_t>(extractU16(data, width, offset));
200 if (width >= BIT_WIDTH) { return result; }
201 uint8_t shift = (BIT_WIDTH - width);
202 return (result << shift) >> shift;
203 }
204
205 /**
206 * Extracts a signed integer up to 32 bits wide, performing a sign extension if necessary.
207 * @param data bitfield to extract from
208 * @param width width of integer in bits. Values larger than 32 are undefined behavior.
209 * @param offset bit offset of the integer to extract
210 * @return extracted integer
211 */
212 constexpr int32_t extractI32(uint64_t data, uint8_t width, uint8_t offset) {
213 const size_t BIT_WIDTH = (sizeof(int32_t) << 3);
214 int32_t result = static_cast<int32_t>(extractU32(data, width, offset));
215 if (width >= BIT_WIDTH) { return result; }
216 uint8_t shift = (BIT_WIDTH - width);
217 return (result << shift) >> shift;
218 }
219
220 /**
221 * Extracts a signed integer up to 64 bits wide, performing a sign extension if necessary.
222 * @param data bitfield to extract from
223 * @param width width of integer in bits. Values larger than 64 are undefined behavior.
224 * @param offset bit offset of the integer to extract
225 * @return extracted integer
226 */
227 constexpr int64_t extractI64(uint64_t data, uint8_t width, uint8_t offset) {
228 const size_t BIT_WIDTH = (sizeof(int64_t) << 3);
229 int64_t result = static_cast<int64_t>(extractU64(data, width, offset));
230 if (width >= BIT_WIDTH) { return result; }
231 uint8_t shift = (BIT_WIDTH - width);
232 return (result << shift) >> shift;
233 }
234
235 /**
236 * Extracts a 24-bit float.
237 *
238 * <p>24-bit floats have 1 sign bit, 8 exponent bits, and 15 mantissa bits. </p>
239 *
240 * @param data bitfield to extract from
241 * @param offset bit offset of the float to extract
242 * @return extracted float
243 */
244 constexpr float extractF24(uint64_t data, uint8_t offset) {
245 float fvalue = 0;
246 uint32_t uvalue = static_cast<uint32_t>((data >> offset) & 0xffffff) << 8;
247 memcpyLE(&fvalue, &uvalue, 4);
248 return fvalue;
249 }
250
251
252 /**
253 * Extracts a 32-bit single-precision float.
254 *
255 * @param data bitfield to extract from
256 * @param offset bit offset of the float to extract
257 * @return extracted float
258 */
259 constexpr float extractF32(uint64_t data, uint8_t offset) {
260 float fvalue = 0;
261 uint32_t uvalue = static_cast<uint32_t>(data >> offset);
262 memcpyLE(&fvalue, &uvalue, sizeof(float));
263 return fvalue;
264 }
265
266 /**
267 * Extracts a 64-bit double-precision float.
268 *
269 * @param data bitfield to extract from
270 * @return extracted float
271 */
272 constexpr double extractF64(uint64_t data) {
273 double dvalue = 0;
274 memcpyLE(&dvalue, &data, sizeof(double));
275 return dvalue;
276 }
277
278 /**
279 * Extracts a boolean from a bitfield.
280 * @param data bitfield to extract from
281 * @param offset the offset of the boolean bit in the bitfield
282 * @return extracted boolean value.
283 */
284 constexpr bool extractBool(uint64_t data, uint8_t offset) {
285 return ((data >> offset) & 1) != 0;
286 }
287
288 /**
289 * Packs an unsigned integer of variable length into a bitfield.
290 * @param data the unsigned integer to pack
291 * @param width the width of the unsigned integer
292 * @param offset the 0-indexed offset of the integer in the bitfield
293 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
294 */
295 constexpr uint64_t packUInt(uint64_t data, uint8_t width, uint8_t offset) {
296 uint64_t mask = ((1ull) << width) - 1;
297 return (data & mask) << offset;
298 }
299
300 /**
301 * Packs a signed integer of variable length into a bitfield as twos complement.
302 * @param data the signed integer to pack
303 * @param width the width of the signed integer
304 * @param offset the 0-indexed offset of the integer in the bitfield
305 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
306 */
307 constexpr uint64_t packInt(int64_t data, uint8_t width, uint8_t offset) {
308 uint64_t mask = ((1ull) << width) - 1;
309 return (static_cast<uint64_t>(data) & mask) << offset;
310 }
311
312 /**
313 * Packs a float into a 24-bit field.
314 * This is accomplished by ignoring the least significant 8 bits of the mantissa.
315 * @param data the float to pack
316 * @param offset the 0-indexed offset of the float in the bitfield
317 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
318 */
319 constexpr uint64_t packF24(float data, uint8_t offset) {
320 uint32_t udata = 0;
321 memcpyLE(&udata, &data, sizeof(float));
322 udata >>= 8;
323 return static_cast<uint64_t>(udata) << offset;
324 }
325
326 /**
327 * Packs a float into a 32-bit field.
328 * @param data the float to pack
329 * @param offset the 0-indexed offset of the float in the bitfield
330 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
331 */
332 constexpr uint64_t packF32(float data, uint8_t offset) {
333 uint32_t udata = 0;
334 memcpyLE(&udata, &data, sizeof(float));
335 return static_cast<uint64_t>(udata) << offset;
336 }
337
338 /**
339 * Packs a double into a 64-bit field.
340 * @param data the double to pack
341 * @param offset the 0-indexed offset of the double in the bitfield
342 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
343 */
344 constexpr uint64_t packF64(double data, uint8_t offset) {
345 uint64_t udata = 0;
346 memcpyLE(&udata, &data, sizeof(double));
347 return udata << offset;
348 }
349
350 /**
351 * Packs a boolean as a single bit into a 64-bit field.
352 * @param data the boolean value
353 * @param offset the 0-indexed offset of the bit in the bitfield
354 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
355 */
356 constexpr uint64_t packBool(bool data, uint8_t offset) {
357 return static_cast<uint64_t>(data) << offset;
358 }
359
360 /**
361 * Converts an enum to an underlying type
362 * @param e value to convert
363 * @return underlying type
364 */
365 template <typename E>
366 constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
367 return static_cast<typename std::underlying_type<E>::type>(e);
368 }
369
370}
Definition: CanandUtils.h:15
constexpr uint8_t getDeviceType(uint32_t fullId)
Definition: CanandUtils.h:24
constexpr uint64_t packInt(int64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:307
constexpr float extractF32(uint64_t data, uint8_t offset)
Definition: CanandUtils.h:259
constexpr uint8_t extractU8(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:133
constexpr uint8_t getDeviceId(uint32_t fullId)
Definition: CanandUtils.h:57
constexpr int32_t extractI32(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:212
constexpr double extractF64(uint64_t data)
Definition: CanandUtils.h:272
constexpr bool idMatches(uint32_t idToCompare, uint8_t deviceType, uint8_t devId)
Definition: CanandUtils.h:70
constexpr bool extractBool(uint64_t data, uint8_t offset)
Definition: CanandUtils.h:284
constexpr uint64_t extractU64(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:169
constexpr int16_t extractI16(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:197
constexpr std::chrono::duration< double, std::ratio< 1LL, 1LL > > toChronoSeconds(units::second_t seconds)
Definition: CanandUtils.h:94
constexpr uint32_t constructMessageId(uint8_t deviceType, uint16_t devId, uint8_t msgId)
Definition: CanandUtils.h:82
constexpr uint64_t packF32(float data, uint8_t offset)
Definition: CanandUtils.h:332
constexpr uint64_t packF64(double data, uint8_t offset)
Definition: CanandUtils.h:344
constexpr void memcpyLE(void *dst, void *src, size_t len)
Definition: CanandUtils.h:105
constexpr uint8_t getApiIndex(uint32_t fullId)
Definition: CanandUtils.h:46
constexpr uint8_t getApiPage(uint32_t fullId)
Definition: CanandUtils.h:35
constexpr uint16_t extractU16(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:145
constexpr std::underlying_type< E >::type to_underlying(E e) noexcept
Definition: CanandUtils.h:366
constexpr uint64_t packUInt(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:295
constexpr int8_t extractI8(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:182
constexpr uint32_t extractU32(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:157
constexpr float extractF24(uint64_t data, uint8_t offset)
Definition: CanandUtils.h:244
constexpr int64_t extractI64(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:227
constexpr uint64_t packBool(bool data, uint8_t offset)
Definition: CanandUtils.h:356
constexpr uint64_t packF24(float data, uint8_t offset)
Definition: CanandUtils.h:319