20enum class vision_wire_format { legacy_14, compact_10 };
26 std::uint32_t legacy_mode{};
34namespace network_protocol {
35using bytes = std::span<const std::byte>;
36static_assert(
sizeof(float) == 4 && std::numeric_limits<float>::is_iec559);
37static_assert(
sizeof(double) == 8 && std::numeric_limits<double>::is_iec559);
39inline std::uint32_t read_u32(bytes data, std::size_t offset) {
40 std::uint32_t result{};
41 for (std::size_t i = 0; i < 4; ++i) {
42 result |= std::uint32_t(std::to_integer<unsigned char>(data[offset + i])) << (8 * i);
47inline float read_float(bytes data, std::size_t offset) {
48 return std::bit_cast<float>(read_u32(data, offset));
51inline void write_le(std::span<std::byte> data, std::size_t offset,
52 std::uint64_t value, std::size_t count) {
53 for (std::size_t i = 0; i < count; ++i) {
54 data[offset + i] = std::byte((value >> (8 * i)) & 0xff);
58inline void write_float(std::span<std::byte> data, std::size_t offset,
float value) {
59 write_le(data, offset, std::bit_cast<std::uint32_t>(value), 4);
62inline std::optional<aim_target> decode_aim(bytes data, std::uint8_t header,
63 vision_wire_format format) {
64 if (format != vision_wire_format::legacy_14 && format != vision_wire_format::compact_10) {
67 const auto length = format == vision_wire_format::legacy_14 ? 14u : 10u;
68 if (data.size() != length || data[0] != std::byte{header} || header == 0x37 ||
69 std::to_integer<unsigned char>(data[9]) > 1) {
72 aim_target result{read_float(data, 1), read_float(data, 5), data[9] == std::byte{1}, 0};
73 if (!std::isfinite(result.yaw) || !std::isfinite(result.pitch)) {
76 if (format == vision_wire_format::legacy_14) {
77 result.legacy_mode = read_u32(data, 10);
78 if (result.legacy_mode > 5) {
85inline std::optional<navigation_command> decode_navigation(bytes data) {
86 if (data.size() != 9 || data[0] != std::byte{0x37}) {
89 navigation_command result{read_float(data, 1), read_float(data, 5)};
90 if (!std::isfinite(result.vx) || !std::isfinite(result.vy)) {
96inline std::array<std::byte, 10> encode_posture(std::uint8_t header,
97 float yaw,
float pitch,
bool red) {
98 if (header == 0x37 || !std::isfinite(yaw) || !std::isfinite(pitch)) {
99 throw std::invalid_argument(
"invalid vision posture");
101 std::array<std::byte, 10> data{};
102 data[0] = std::byte{header};
103 write_float(data, 1, yaw);
104 write_float(data, 5, pitch);
105 data[9] = std::byte{red};
109inline std::array<std::byte, 10> encode_navigation(
float yaw,
float hp_fraction,
110 bool match_started) {
111 if (!std::isfinite(yaw) || !std::isfinite(hp_fraction) || hp_fraction < 0 ||
113 throw std::invalid_argument(
"invalid navigation telemetry");
115 std::array<std::byte, 10> data{};
116 data[0] = std::byte{0x37};
117 write_float(data, 1, yaw);
118 write_float(data, 5, hp_fraction);
119 data[9] = std::byte{match_started};
127 using clock = std::chrono::steady_clock;
128 void update(T value, clock::time_point now = clock::now()) {
132 std::optional<T>
get(clock::duration timeout, clock::time_point now = clock::now())
const {
133 if (!value_ || timeout <= clock::duration::zero() || now < received_ ||
134 now - received_ >= timeout) {
139 void reset() { value_.reset(); }
141 std::optional<T> value_;
142 clock::time_point received_{};
145inline std::uint32_t log_name_id(std::string_view name) {
146 std::uint32_t value = 2166136261u;
147 for (
const unsigned char c : name) {
148 value = (value ^ c) * 16777619u;
153inline std::vector<std::byte> encode_log_name(std::string_view name) {
154 if (name.empty() || name.size() > 255) {
155 throw std::invalid_argument(
"remote log name must contain 1..255 bytes");
157 std::vector<std::byte> data(8 + name.size());
158 write_le(data, 0, data.size(), 2);
159 data[2] = std::byte{0};
160 write_le(data, 3, log_name_id(name), 4);
161 data[7] = std::byte(name.size());
162 for (std::size_t i = 0; i < name.size(); ++i) data[8 + i] = std::byte(name[i]);
166inline std::array<std::byte, 15> encode_log_value(std::uint32_t
id,
double value) {
167 if (!std::isfinite(value))
throw std::invalid_argument(
"non-finite remote log value");
168 std::array<std::byte, 15> data{};
169 write_le(data, 0, data.size(), 2);
170 data[2] = std::byte{1};
171 write_le(data, 3,
id, 4);
172 write_le(data, 7, std::bit_cast<std::uint64_t>(value), 8);
176inline std::vector<std::byte> encode_log_text(std::string_view text,
bool message_box =
false) {
178 if (text.size() > 65502)
throw std::invalid_argument(
"remote log text too long");
179 std::vector<std::byte> data(5 + text.size());
180 write_le(data, 0, data.size(), 2);
181 data[2] = std::byte(message_box ? 3 : 2);
182 write_le(data, 3, text.size(), 2);
183 for (std::size_t i = 0; i < text.size(); ++i) data[5 + i] = std::byte(text[i]);