CARLA
Loading...
Searching...
No Matches
mirrorPackets.h
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB).
2// This work is licensed under the terms of the MIT license.
3// For a copy, see <https://opensource.org/licenses/MIT>.
4
5#pragma once
6
8
9#include <algorithm>
10#include <array>
11#include <cstdint>
12#include <istream>
13#include <optional>
14#include <ostream>
15#include <sstream>
16#include <string>
17#include <unordered_map>
18#include <utility>
19#include <vector>
20
21namespace carla {
22namespace multigpu {
23
24 /// One record of frame data packet 34: an actor's world pose as the primary
25 /// holds it, in UE units, with its rotation as a quaternion (an Euler round
26 /// trip snaps pitch near +-90 degrees, e.g. for cameras looking down).
28 uint32_t id = 0u;
29 double location[3] = {0.0, 0.0, 0.0};
30 /// x, y, z, w
31 double rotation[4] = {0.0, 0.0, 0.0, 1.0};
32 };
33
34 /// Frame data packet 34: every live actor's pose in this frame.
36
37 static constexpr uint8_t kPacketId = 34u;
38
39 static constexpr uint16_t kRecordSizeV1 = 60u;
40
41 std::vector<MirroredPosition> positions;
42
43 void Write(std::ostream &out) const {
44 const uint32_t count = static_cast<uint32_t>(positions.size());
45 WriteFrameDataHeader(out, kPacketId, sizeof(uint32_t) + sizeof(uint16_t) + count * kRecordSizeV1);
47 for (const MirroredPosition &position : positions) {
48 detail::WritePod(out, position.id);
49 for (double value : position.location) {
50 detail::WritePod(out, value);
51 }
52 for (double value : position.rotation) {
53 detail::WritePod(out, value);
54 }
55 }
56 }
57
58 /// Reads a payload of @a size bytes; records that grew in a newer build
59 /// are read up to the fields known here.
60 bool Read(std::istream &in, uint32_t size) {
61 positions.clear();
62 BoundedReader reader(in, size);
63 uint32_t count = 0u;
64 uint16_t record_size = 0u;
65 if (!reader.ReadFixedListHeader(kRecordSizeV1, count, record_size)) {
66 return false;
67 }
68 positions.reserve(count);
69 for (uint32_t i = 0u; i < count; ++i) {
70 MirroredPosition position;
71 const bool ok = reader.ReadRecord(record_size, [&](BoundedReader &record) {
72 bool read = record.Read(position.id);
73 for (double &value : position.location) {
74 read = read && record.Read(value);
75 }
76 for (double &value : position.rotation) {
77 read = read && record.Read(value);
78 }
79 return read;
80 });
81 if (!ok) {
82 return false;
83 }
84 positions.push_back(position);
85 }
86 return true;
87 }
88 };
89
90 /// A wheel as the primary draws it: degrees and cm.
91 struct WheelPose {
92 float steer = 0.0f;
93 float spin = 0.0f;
94 float suspension_offset = 0.0f;
95 };
96
97 /// An open door, which on the primary is a physics body of its own: its pose
98 /// relative to the vehicle, in UE units, rotation as a quaternion (x, y, z, w).
99 struct DoorPose {
100 uint8_t door = 0u;
101 std::array<double, 3> location = {0.0, 0.0, 0.0};
102 std::array<double, 4> rotation = {0.0, 0.0, 0.0, 1.0};
103 };
104
105 /// A vehicle's moving parts: every wheel, by wheel index, and the doors
106 /// open (any other door is closed), and its drivetrain as the primary's
107 /// simulation has it: what the vehicle's sensors report.
109 uint32_t actor_id = 0u;
110 std::vector<WheelPose> wheels;
111 std::vector<DoorPose> open_doors;
112 std::optional<int32_t> current_gear;
113 bool manual_gear_shift = false;
114 /// Whether the primary's body simulates physics (it reports no angular
115 /// velocity otherwise).
116 bool simulating_physics = false;
117 };
118
119 /// Frame data packet 32, every frame: the moving parts of every vehicle.
121
122 static constexpr uint8_t kPacketId = 32u;
123
124 enum Field : uint8_t {
127 kDrivetrain = 3u
128 };
129
130 static constexpr uint16_t kWheelRecordSizeV1 = 12u;
131
132 static constexpr uint16_t kDoorRecordSizeV1 = 1u + 3u * 8u + 4u * 8u;
133
134 std::vector<VehicleParts> vehicles;
135
136 void Write(std::ostream &out) const {
137 std::ostringstream payload;
138 detail::WritePod(payload, static_cast<uint32_t>(vehicles.size()));
139 for (const VehicleParts &vehicle : vehicles) {
140 std::ostringstream record;
141 detail::WritePod(record, vehicle.actor_id);
142 {
143 std::ostringstream value;
144 WriteFixedListHeader(value, static_cast<uint32_t>(vehicle.wheels.size()), kWheelRecordSizeV1);
145 for (const WheelPose &wheel : vehicle.wheels) {
146 detail::WritePod(value, wheel.steer);
147 detail::WritePod(value, wheel.spin);
148 detail::WritePod(value, wheel.suspension_offset);
149 }
150 WriteField(record, kWheels, value.str());
151 }
152 if (!vehicle.open_doors.empty()) {
153 std::ostringstream value;
154 WriteFixedListHeader(value, static_cast<uint32_t>(vehicle.open_doors.size()), kDoorRecordSizeV1);
155 for (const DoorPose &door : vehicle.open_doors) {
156 detail::WritePod(value, door.door);
157 for (double axis : door.location) {
158 detail::WritePod(value, axis);
159 }
160 for (double axis : door.rotation) {
161 detail::WritePod(value, axis);
162 }
163 }
164 WriteField(record, kOpenDoors, value.str());
165 }
166 if (vehicle.current_gear) {
167 std::ostringstream value;
168 detail::WritePod(value, *vehicle.current_gear);
169 const uint8_t flags =
170 (vehicle.manual_gear_shift ? 1u : 0u) | (vehicle.simulating_physics ? 2u : 0u);
171 detail::WritePod(value, flags);
172 WriteField(record, kDrivetrain, value.str());
173 }
174 WriteSizedBytes(payload, record.str());
175 }
176 WriteFrameDataPacket(out, kPacketId, payload.str());
177 }
178
179 bool Read(std::istream &in, uint32_t size) {
180 vehicles.clear();
181 BoundedReader reader(in, size);
182 uint32_t count = 0u;
183 if (!reader.ReadVariableListCount(count)) {
184 return false;
185 }
186 for (uint32_t i = 0u; i < count; ++i) {
187 VehicleParts vehicle;
188 const bool ok = reader.ReadSizedRecord([&](BoundedReader &record) {
189 return record.Read(vehicle.actor_id) && record.ReadFields([&](uint8_t kind, BoundedReader &value) {
190 switch (kind) {
191 case kWheels:
192 return ReadList(value, kWheelRecordSizeV1, vehicle.wheels, [](BoundedReader &item, WheelPose &wheel) {
193 return item.Read(wheel.steer) && item.Read(wheel.spin) && item.Read(wheel.suspension_offset);
194 });
195 case kOpenDoors:
196 return ReadList(value, kDoorRecordSizeV1, vehicle.open_doors, [](BoundedReader &item, DoorPose &door) {
197 bool read = item.Read(door.door);
198 for (double &axis : door.location) {
199 read = read && item.Read(axis);
200 }
201 for (double &axis : door.rotation) {
202 read = read && item.Read(axis);
203 }
204 return read;
205 });
206 case kDrivetrain: {
207 int32_t gear = 0;
208 uint8_t flags = 0u;
209 if (!value.Read(gear) || !value.Read(flags)) {
210 return false;
211 }
212 vehicle.current_gear = gear;
213 vehicle.manual_gear_shift = (flags & 1u) != 0u;
214 vehicle.simulating_physics = (flags & 2u) != 0u;
215 return true;
216 }
217 default:
218 return true;
219 }
220 });
221 });
222 if (!ok) {
223 return false;
224 }
225 vehicles.push_back(std::move(vehicle));
226 }
227 return true;
228 }
229
230 private:
231
232 template <typename T, typename F>
233 static bool ReadList(BoundedReader &value, uint16_t min_record_size, std::vector<T> &out, F &&read) {
234 uint32_t count = 0u;
235 uint16_t record_size = 0u;
236 if (!value.ReadFixedListHeader(min_record_size, count, record_size)) {
237 return false;
238 }
239 out.clear();
240 out.reserve(count);
241 for (uint32_t i = 0u; i < count; ++i) {
242 T item;
243 if (!value.ReadRecord(record_size, [&](BoundedReader &record) { return read(record, item); })) {
244 return false;
245 }
246 out.push_back(item);
247 }
248 return true;
249 }
250 };
251
252 /// Order in which to place actors so that every parent is placed before its
253 /// children: by attachment depth, keeping the input order within a depth.
254 /// @a parents maps an id to its parent's id (absent or 0 for a root). A
255 /// parent that is not among @a ids counts as a root, and so does every actor
256 /// of a cycle, so the result always holds every id once.
257 inline std::vector<uint32_t> OrderParentsFirst(
258 const std::vector<uint32_t> &ids,
259 const std::unordered_map<uint32_t, uint32_t> &parents) {
260 std::unordered_map<uint32_t, int> in_list;
261 for (uint32_t id : ids) {
262 in_list.emplace(id, 0);
263 }
264 std::unordered_map<uint32_t, size_t> depth;
265 auto depth_of = [&](uint32_t id) {
266 // Walk up the chain; a chain longer than the list can only be a cycle.
267 size_t steps = 0u;
268 uint32_t current = id;
269 while (steps <= ids.size()) {
270 const auto cached = depth.find(current);
271 if (cached != depth.end()) {
272 return cached->second + steps;
273 }
274 const auto parent = parents.find(current);
275 if ((parent == parents.end()) || (parent->second == 0u) ||
276 (in_list.find(parent->second) == in_list.end())) {
277 return steps;
278 }
279 current = parent->second;
280 ++steps;
281 }
282 return size_t{0u};
283 };
284 std::vector<std::pair<size_t, uint32_t>> ordered;
285 ordered.reserve(ids.size());
286 for (uint32_t id : ids) {
287 const size_t d = depth_of(id);
288 depth.emplace(id, d);
289 ordered.emplace_back(d, id);
290 }
291 std::stable_sort(ordered.begin(), ordered.end(), [](const auto &a, const auto &b) {
292 return a.first < b.first;
293 });
294 std::vector<uint32_t> result;
295 result.reserve(ordered.size());
296 for (const auto &item : ordered) {
297 result.push_back(item.second);
298 }
299 return result;
300 }
301
302} // namespace multigpu
303} // namespace carla
Reads the fields of one packet (or one record inside it) without ever consuming more than its declare...
bool ReadFixedListHeader(uint16_t min_record_size, uint32_t &count, uint16_t &record_size)
Header of a list of fixed-size records, checked against the bytes left.
bool ReadRecord(uint32_t bytes, F &&read)
Runs read(record) on the next bytes as a reader of their own, then skips what it left unread (a recor...
bool ReadSizedRecord(F &&read)
Reads one [u32 record_size][record] entry of a variable-size list.
bool ReadVariableListCount(uint32_t &count)
Count of a list of variable-size records, checked against the bytes left.
bool ReadFields(F &&read)
Reads TLV fields until the reader is exhausted.
void WritePod(std::ostream &out, const T &value)
void WriteFixedListHeader(std::ostream &out, uint32_t count, uint16_t record_size)
Header of a list of fixed-size records.
void WriteField(std::ostream &out, uint8_t kind, const std::string &value)
void WriteFrameDataHeader(std::ostream &out, uint8_t id, uint32_t payload_size)
void WriteSizedBytes(std::ostream &out, const std::string &bytes)
One [u32 size][bytes] entry: a variable-size record or, after a kind byte, a TLV value.
void WriteFrameDataPacket(std::ostream &out, uint8_t id, const std::string &payload)
Writes a packet whose payload was built beforehand.
std::vector< uint32_t > OrderParentsFirst(const std::vector< uint32_t > &ids, const std::unordered_map< uint32_t, uint32_t > &parents)
Order in which to place actors so that every parent is placed before its children: by attachment dept...
This file contains definitions of common data structures used in traffic manager.
An open door, which on the primary is a physics body of its own: its pose relative to the vehicle,...
std::array< double, 3 > location
std::array< double, 4 > rotation
One record of frame data packet 34: an actor's world pose as the primary holds it,...
Frame data packet 34: every live actor's pose in this frame.
bool Read(std::istream &in, uint32_t size)
Reads a payload of size bytes; records that grew in a newer build are read up to the fields known her...
void Write(std::ostream &out) const
static constexpr uint16_t kRecordSizeV1
static constexpr uint8_t kPacketId
std::vector< MirroredPosition > positions
Frame data packet 32, every frame: the moving parts of every vehicle.
static constexpr uint8_t kPacketId
std::vector< VehicleParts > vehicles
static constexpr uint16_t kDoorRecordSizeV1
void Write(std::ostream &out) const
static bool ReadList(BoundedReader &value, uint16_t min_record_size, std::vector< T > &out, F &&read)
static constexpr uint16_t kWheelRecordSizeV1
bool Read(std::istream &in, uint32_t size)
A vehicle's moving parts: every wheel, by wheel index, and the doors open (any other door is closed),...
std::vector< DoorPose > open_doors
std::vector< WheelPose > wheels
std::optional< int32_t > current_gear
bool simulating_physics
Whether the primary's body simulates physics (it reports no angular velocity otherwise).
A wheel as the primary draws it: degrees and cm.