FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
world.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright (c) 2006-2008 Marc Brooker and Michael Inggs
4// Copyright (c) 2008-present FERS Contributors (see AUTHORS.md).
5//
6// See the GNU GPLv2 LICENSE file in the FERS project root for more information.
7
8/**
9 * @file world.cpp
10 * @brief Implementation of the World class for the radar simulation environment.
11 */
12
13#include "world.h"
14
15#include <iomanip>
16#include <limits>
17#include <sstream>
18
20#include "core/sim_events.h"
21#include "core/sim_id.h"
22#include "parameters.h"
23#include "radar/radar_obj.h"
24#include "signal/radar_signal.h"
26#include "timing/timing.h"
27
30using radar::Platform;
31using radar::Receiver;
32using radar::Target;
35
36namespace core
37{
38 void World::add(std::unique_ptr<Platform> plat) noexcept { _platforms.push_back(std::move(plat)); }
39
40 void World::add(std::unique_ptr<Transmitter> trans) noexcept { _transmitters.push_back(std::move(trans)); }
41
42 void World::add(std::unique_ptr<Receiver> recv) noexcept { _receivers.push_back(std::move(recv)); }
43
44 void World::add(std::unique_ptr<Target> target) noexcept { _targets.push_back(std::move(target)); }
45
46 void World::add(std::unique_ptr<RadarSignal> waveform)
47 {
48 const SimId id = waveform->getId();
49 if (_waveforms.contains(id))
50 {
51 throw std::runtime_error("A waveform with the ID " + std::to_string(id) + " already exists.");
52 }
53 _waveforms[id] = std::move(waveform);
54 }
55
56 void World::add(std::unique_ptr<Antenna> antenna)
57 {
58 const SimId id = antenna->getId();
59 if (_antennas.contains(id))
60 {
61 throw std::runtime_error("An antenna with the ID " + std::to_string(id) + " already exists.");
62 }
63 _antennas[id] = std::move(antenna);
64 }
65
66 void World::add(std::unique_ptr<PrototypeTiming> timing)
67 {
68 const SimId id = timing->getId();
69 if (_timings.contains(id))
70 {
71 throw std::runtime_error("A timing source with the ID " + std::to_string(id) + " already exists.");
72 }
73 _timings[id] = std::move(timing);
74 }
75
77 {
78 const auto it = _waveforms.find(id);
79 return it != _waveforms.end() ? it->second.get() : nullptr;
80 }
81
83 {
84 const auto it = _antennas.find(id);
85 return it != _antennas.end() ? it->second.get() : nullptr;
86 }
87
89 {
90 const auto it = _timings.find(id);
91 return it != _timings.end() ? it->second.get() : nullptr;
92 }
93
95 {
96 for (auto& p : _platforms)
97 {
98 if (p->getId() == id)
99 return p.get();
100 }
101 return nullptr;
102 }
103
105 {
106 for (auto& tx : _transmitters)
107 if (tx->getId() == id)
108 return tx.get();
109 return nullptr;
110 }
111
113 {
114 for (auto& rx : _receivers)
115 if (rx->getId() == id)
116 return rx.get();
117 return nullptr;
118 }
119
121 {
122 for (auto& tgt : _targets)
123 if (tgt->getId() == id)
124 return tgt.get();
125 return nullptr;
126 }
127
128 void World::replace(std::unique_ptr<Target> target)
129 {
130 const SimId id = target->getId();
131 for (auto& t : _targets)
132 {
133 if (t->getId() == id)
134 {
135 t = std::move(target);
136 return;
137 }
138 }
139 _targets.push_back(std::move(target));
140 }
141
142 void World::replace(std::unique_ptr<Antenna> antenna)
143 {
144 const SimId id = antenna->getId();
145 const Antenna* new_ptr = antenna.get();
146
147 std::unique_ptr<Antenna> old_owned;
148 const Antenna* old_ptr = nullptr;
149
150 if (auto it = _antennas.find(id); it != _antennas.end())
151 {
152 old_owned = std::move(it->second);
153 old_ptr = old_owned.get();
154 it->second = std::move(antenna);
155 }
156 else
157 {
158 _antennas[id] = std::move(antenna);
159 }
160
161 if ((old_ptr != nullptr) && old_ptr != new_ptr)
162 {
163 for (auto& tx : _transmitters)
164 if (tx->getAntenna() == old_ptr)
165 tx->setAntenna(new_ptr);
166
167 for (auto& rx : _receivers)
168 if (rx->getAntenna() == old_ptr)
169 rx->setAntenna(new_ptr);
170 }
171 }
172
173 void World::replace(std::unique_ptr<RadarSignal> waveform)
174 {
175 const SimId id = waveform->getId();
176 RadarSignal* new_ptr = waveform.get();
177
178 std::unique_ptr<RadarSignal> old_owned;
179 const RadarSignal* old_ptr = nullptr;
180
181 if (auto it = _waveforms.find(id); it != _waveforms.end())
182 {
183 old_owned = std::move(it->second);
184 old_ptr = old_owned.get();
185 it->second = std::move(waveform);
186 }
187 else
188 {
189 _waveforms[id] = std::move(waveform);
190 }
191
192 if ((old_ptr != nullptr) && old_ptr != new_ptr)
193 {
194 for (auto& tx : _transmitters)
195 if (tx->getSignal() == old_ptr)
196 tx->setSignal(new_ptr);
197 }
198 }
199
200 void World::replace(std::unique_ptr<PrototypeTiming> timing)
201 {
202 const SimId id = timing->getId();
203 const PrototypeTiming* new_ptr = timing.get();
204
205 std::unique_ptr<PrototypeTiming> old_owned;
206 const PrototypeTiming* old_ptr = nullptr;
207
208 if (auto it = _timings.find(id); it != _timings.end())
209 {
210 old_owned = std::move(it->second);
211 old_ptr = old_owned.get();
212 it->second = std::move(timing);
213 }
214 else
215 {
216 _timings[id] = std::move(timing);
217 }
218
219 auto refresh_timing = [id, new_ptr](auto& radar_obj)
220 {
221 const auto current_timing = radar_obj->getTiming();
222 if (!current_timing || (current_timing->getId() != id))
223 {
224 return;
225 }
226
227 auto refreshed =
228 std::make_shared<timing::Timing>(new_ptr->getName(), current_timing->getSeed(), new_ptr->getId());
229 refreshed->initializeModel(new_ptr);
230 radar_obj->setTiming(refreshed);
231 };
232
233 if ((old_ptr != nullptr) && old_ptr != new_ptr)
234 {
235 for (auto& tx : _transmitters)
236 refresh_timing(tx);
237
238 for (auto& rx : _receivers)
239 refresh_timing(rx);
240 }
241 }
242
243 void World::clear() noexcept
244 {
245 _platforms.clear();
246 _transmitters.clear();
247 _receivers.clear();
248 _targets.clear();
249 _waveforms.clear();
250 _antennas.clear();
251 _timings.clear();
252 _event_queue = {};
253 _simulation_state = {};
254 }
255
257 {
258 const RealType sim_start = params::startTime();
259 const RealType sim_end = params::endTime();
260
261 for (const auto& transmitter : _transmitters)
262 {
263 if (transmitter->getMode() == radar::OperationMode::PULSED_MODE)
264 {
265 // Find the first valid pulse time starting from the simulation start time.
266 if (auto start_time = transmitter->getNextPulseTime(sim_start); start_time)
267 {
268 if (*start_time <= sim_end)
269 {
270 _event_queue.push({*start_time, EventType::TX_PULSED_START, transmitter.get()});
271 }
272 }
273 }
274 else // CW_MODE
275 {
276 const auto& schedule = transmitter->getSchedule();
277 if (schedule.empty())
278 {
279 // Legacy behavior: Always on for simulation duration
280 _event_queue.push({sim_start, EventType::TX_CW_START, transmitter.get()});
281 _event_queue.push({sim_end, EventType::TX_CW_END, transmitter.get()});
282 }
283 else
284 {
285 for (const auto& period : schedule)
286 {
287 // Clip periods to simulation bounds
288 const RealType start = std::max(sim_start, period.start);
289 const RealType end = std::min(sim_end, period.end);
290
291 if (start < end)
292 {
293 _event_queue.push({start, EventType::TX_CW_START, transmitter.get()});
294 _event_queue.push({end, EventType::TX_CW_END, transmitter.get()});
295 }
296 }
297 }
298 }
299 }
300
301 for (const auto& receiver : _receivers)
302 {
303 if (receiver->getMode() == radar::OperationMode::PULSED_MODE)
304 {
305 // Schedule the first receive window checking against schedule
306 const RealType nominal_start = receiver->getWindowStart(0);
307 if (auto start = receiver->getNextWindowTime(nominal_start); start && *start < params::endTime())
308 {
309 _event_queue.push({*start, EventType::RX_PULSED_WINDOW_START, receiver.get()});
310 }
311 }
312 else // CW_MODE
313 {
314 const auto& schedule = receiver->getSchedule();
315 if (schedule.empty())
316 {
317 // Legacy behavior: Always on for simulation duration
318 _event_queue.push({params::startTime(), EventType::RX_CW_START, receiver.get()});
319 _event_queue.push({params::endTime(), EventType::RX_CW_END, receiver.get()});
320 }
321 else
322 {
323 for (const auto& period : schedule)
324 {
325 const RealType start = std::max(params::startTime(), period.start);
326 const RealType end = std::min(params::endTime(), period.end);
327 if (start < end)
328 {
329 _event_queue.push({start, EventType::RX_CW_START, receiver.get()});
330 _event_queue.push({end, EventType::RX_CW_END, receiver.get()});
331 }
332 }
333 }
334 }
335 }
336 }
337
338 std::string World::dumpEventQueue() const
339 {
340 if (_event_queue.empty())
341 {
342 return "Event Queue is empty.\n";
343 }
344
345 std::stringstream ss;
346 ss << std::fixed << std::setprecision(6);
347
348 const std::string separator = "--------------------------------------------------------------------";
349 const std::string title = "| Event Queue Contents (" + std::to_string(_event_queue.size()) + " events)";
350 if (separator.size() > static_cast<std::size_t>(std::numeric_limits<int>::max()))
351 {
352 throw std::runtime_error("Separator width exceeds stream formatting limits.");
353 }
354 const int title_width = static_cast<int>(separator.size()) - 1;
355
356 ss << separator << "\n"
357 << std::left << std::setw(title_width) << title << "|\n"
358 << separator << "\n"
359 << "| " << std::left << std::setw(12) << "Timestamp" << " | " << std::setw(21) << "Event Type" << " | "
360 << std::setw(25) << "Source Object" << " |\n"
361 << separator << "\n";
362
363 auto queue_copy = _event_queue;
364
365 while (!queue_copy.empty())
366 {
367 const auto [timestamp, event_type, source_object] = queue_copy.top();
368 queue_copy.pop();
369
370 ss << "| " << std::right << std::setw(12) << timestamp << " | " << std::left << std::setw(21)
371 << toString(event_type) << " | " << std::left << std::setw(25) << source_object->getName() << " |\n";
372 }
373 ss << separator << "\n";
374
375 return ss.str();
376 }
377}
Header file defining various types of antennas and their gain patterns.
Abstract base class representing an antenna.
void scheduleInitialEvents()
Populates the event queue with the initial events for the simulation.
Definition world.cpp:256
void add(std::unique_ptr< radar::Platform > plat) noexcept
Adds a radar platform to the simulation world.
Definition world.cpp:38
void replace(std::unique_ptr< radar::Target > target)
Replaces an existing target, updating internal pointers.
Definition world.cpp:128
fers_signal::RadarSignal * findWaveform(const SimId id)
Finds a radar signal by ID.
Definition world.cpp:76
radar::Target * findTarget(const SimId id)
Finds a target by ID.
Definition world.cpp:120
radar::Receiver * findReceiver(const SimId id)
Finds a receiver by ID.
Definition world.cpp:112
radar::Transmitter * findTransmitter(const SimId id)
Finds a transmitter by ID.
Definition world.cpp:104
void clear() noexcept
Clears all objects and assets from the simulation world.
Definition world.cpp:243
timing::PrototypeTiming * findTiming(const SimId id)
Finds a timing source by ID.
Definition world.cpp:88
antenna::Antenna * findAntenna(const SimId id)
Finds an antenna by ID.
Definition world.cpp:82
std::string dumpEventQueue() const
Dumps the current state of the event queue to a string for debugging.
Definition world.cpp:338
radar::Platform * findPlatform(const SimId id)
Finds a platform by ID.
Definition world.cpp:94
Class representing a radar signal with associated properties.
Represents a simulation platform with motion and rotation paths.
Definition platform.h:32
Manages radar signal reception and response processing.
Definition receiver.h:37
Base class for radar targets.
Definition target.h:118
SimId getId() const noexcept
Gets the unique ID of the target.
Definition target.h:153
Represents a radar transmitter system.
Definition transmitter.h:33
Manages timing properties such as frequency, offsets, and synchronization.
std::string getName() const
Gets the name of the timing source.
SimId getId() const noexcept
Gets the unique ID of the timing source.
double RealType
Type for real numbers.
Definition config.h:27
std::string toString(const EventType type)
Converts an EventType enum to its string representation.
Definition sim_events.h:72
@ RX_PULSED_WINDOW_START
A pulsed receiver opens its listening window.
@ TX_CW_START
A continuous-wave transmitter starts transmitting.
@ TX_CW_END
A continuous-wave transmitter stops transmitting.
@ TX_PULSED_START
A pulsed transmitter begins emitting a pulse.
@ RX_CW_END
A continuous-wave receiver stops listening.
@ RX_CW_START
A continuous-wave receiver starts listening.
RealType endTime() noexcept
Get the end time for the simulation.
Definition parameters.h:109
RealType startTime() noexcept
Get the start time for the simulation.
Definition parameters.h:103
@ PULSED_MODE
The component operates in a pulsed mode.
Defines the Parameters struct and provides methods for managing simulation parameters.
Header file for the PrototypeTiming class.
Defines the Radar class and associated functionality.
Classes for handling radar waveforms and signals.
Defines the core structures for the event-driven simulation engine.
uint64_t SimId
64-bit Unique Simulation ID.
Definition sim_id.h:18
Timing source for simulation objects.
Header file for the World class in the simulator.