ReduxLib C++ 2025.0.0
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 inline void memcpyLE(void* dst, void* src, size_t len) {
106
107 uint8_t* dst8 = reinterpret_cast<uint8_t*>(dst);
108 uint8_t* src8 = reinterpret_cast<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 return std::bit_cast<float>((static_cast<uint32_t>(data >> offset) & 0xffffff) << 8);
246 }
247
248
249 /**
250 * Extracts a 32-bit single-precision float.
251 *
252 * @param data bitfield to extract from
253 * @param offset bit offset of the float to extract
254 * @return extracted float
255 */
256 constexpr float extractF32(uint64_t data, uint8_t offset) {
257 return std::bit_cast<float>(static_cast<uint32_t>(data >> offset));
258 }
259
260 /**
261 * Extracts a 64-bit double-precision float.
262 *
263 * @param data bitfield to extract from
264 * @return extracted float
265 */
266 constexpr double extractF64(uint64_t data) {
267 return std::bit_cast<double>(data);
268 }
269
270 /**
271 * Extracts a boolean from a bitfield.
272 * @param data bitfield to extract from
273 * @param offset the offset of the boolean bit in the bitfield
274 * @return extracted boolean value.
275 */
276 constexpr bool extractBool(uint64_t data, uint8_t offset) {
277 return ((data >> offset) & 1) != 0;
278 }
279
280 /**
281 * Packs an unsigned integer of variable length into a bitfield.
282 * @param data the unsigned integer to pack
283 * @param width the width of the unsigned integer
284 * @param offset the 0-indexed offset of the integer in the bitfield
285 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
286 */
287 constexpr uint64_t packUInt(uint64_t data, uint8_t width, uint8_t offset) {
288 uint64_t mask = ((1ull) << width) - 1;
289 return (data & mask) << offset;
290 }
291
292 /**
293 * Packs a signed integer of variable length into a bitfield as twos complement.
294 * @param data the signed integer to pack
295 * @param width the width of the signed integer
296 * @param offset the 0-indexed offset of the integer in the bitfield
297 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
298 */
299 constexpr uint64_t packInt(int64_t data, uint8_t width, uint8_t offset) {
300 uint64_t mask = ((1ull) << width) - 1;
301 return (static_cast<uint64_t>(data) & mask) << offset;
302 }
303
304 /**
305 * Packs a float into a 24-bit field.
306 * This is accomplished by ignoring the least significant 8 bits of the mantissa.
307 * @param data the float to pack
308 * @param offset the 0-indexed offset of the float in the bitfield
309 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
310 */
311 constexpr uint64_t packF24(float data, uint8_t offset) {
312 return static_cast<uint64_t>(std::bit_cast<uint32_t>(data) >> 8) << offset;
313 }
314
315 /**
316 * Packs a float into a 32-bit field.
317 * @param data the float to pack
318 * @param offset the 0-indexed offset of the float in the bitfield
319 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
320 */
321 constexpr uint64_t packF32(float data, uint8_t offset) {
322 return static_cast<uint64_t>(std::bit_cast<uint32_t>(data)) << offset;
323 }
324
325 /**
326 * Packs a double into a 64-bit field.
327 * @param data the double to pack
328 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
329 */
330 constexpr uint64_t packF64(double data) {
331 return std::bit_cast<uint64_t>(data);
332 }
333
334 /**
335 * Packs a boolean as a single bit into a 64-bit field.
336 * @param data the boolean value
337 * @param offset the 0-indexed offset of the bit in the bitfield
338 * @return packed bitfield that can be ORed with other fields to construct a full bitfield
339 */
340 constexpr uint64_t packBool(bool data, uint8_t offset) {
341 return static_cast<uint64_t>(data) << offset;
342 }
343
344 /**
345 * Converts an enum to an underlying type
346 * @param e value to convert
347 * @return underlying type
348 */
349 template <typename E>
350 constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
351 return static_cast<typename std::underlying_type<E>::type>(e);
352 }
353
354}
Definition: CanandUtils.h:15
constexpr uint64_t packF64(double data)
Definition: CanandUtils.h:330
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:299
constexpr float extractF32(uint64_t data, uint8_t offset)
Definition: CanandUtils.h:256
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:266
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:276
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:321
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:350
constexpr uint64_t packUInt(uint64_t data, uint8_t width, uint8_t offset)
Definition: CanandUtils.h:287
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
void memcpyLE(void *dst, void *src, size_t len)
Definition: CanandUtils.h:105
constexpr uint64_t packBool(bool data, uint8_t offset)
Definition: CanandUtils.h:340
constexpr uint64_t packF24(float data, uint8_t offset)
Definition: CanandUtils.h:311