FERS 0.1.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 <algorithm>
16#include <iomanip>
17#include <limits>
18#include <optional>
19#include <sstream>
20#include <stdexcept>
21#include <unordered_map>
22#include <utility>
23
25#include "core/sim_events.h"
26#include "core/sim_id.h"
27#include "parameters.h"
28#include "radar/radar_obj.h"
29#include "signal/radar_signal.h"
31#include "timing/timing.h"
32
35using radar::Platform;
36using radar::Receiver;
37using radar::Target;
40
41namespace core
42{
43 namespace
44 {
45 std::vector<ActiveStreamingSource> transmitterDechirpSources(const Transmitter* const tx)
46 {
47 std::vector<ActiveStreamingSource> sources;
48 if (tx->getSchedule().empty())
49 {
51 if (source.segment_start < source.segment_end)
52 {
53 sources.push_back(source);
54 }
55 return sources;
56 }
57
58 for (const auto& period : tx->getSchedule())
59 {
60 auto source = makeActiveSource(tx, period.start, std::min(params::endTime(), period.end));
61 if (source.segment_start < source.segment_end && source.segment_end > params::startTime())
62 {
63 sources.push_back(source);
64 }
65 }
66 return sources;
67 }
68
69 std::vector<ActiveStreamingSource> waveformDechirpSources(const RadarSignal* const waveform,
70 const Receiver* const rx)
71 {
72 std::vector<ActiveStreamingSource> sources;
73 if (rx->getSchedule().empty())
74 {
76 if (source.segment_start < source.segment_end)
77 {
78 sources.push_back(source);
79 }
80 return sources;
81 }
82
83 for (const auto& period : rx->getSchedule())
84 {
85 auto source =
86 makeActiveSourceFromWaveform(waveform, period.start, std::min(params::endTime(), period.end));
87 if (source.segment_start < source.segment_end && source.segment_end > params::startTime())
88 {
89 sources.push_back(source);
90 }
91 }
92 return sources;
93 }
94
95 void validateDechirpTransmitter(const Transmitter* const tx, const std::string& owner)
96 {
97 if (tx == nullptr)
98 {
99 throw std::runtime_error(owner + " references a missing dechirp transmitter.");
100 }
101 if (tx->getMode() != radar::OperationMode::FMCW_MODE || tx->getSignal() == nullptr ||
102 !tx->getSignal()->isFmcwFamily())
103 {
104 throw std::runtime_error(owner + " dechirp reference transmitter '" + tx->getName() +
105 "' must be an FMCW transmitter with an FMCW waveform.");
106 }
107 }
108 }
109
110 void World::add(std::unique_ptr<Platform> plat) noexcept { _platforms.push_back(std::move(plat)); }
111
112 void World::add(std::unique_ptr<Transmitter> trans) noexcept
113 {
114 _transmitters_by_name[trans->getName()] = trans.get();
115 _transmitters.push_back(std::move(trans));
116 }
117
118 void World::add(std::unique_ptr<Receiver> recv) noexcept { _receivers.push_back(std::move(recv)); }
119
120 void World::add(std::unique_ptr<Target> target) noexcept { _targets.push_back(std::move(target)); }
121
122 void World::add(std::unique_ptr<RadarSignal> waveform)
123 {
124 const SimId id = waveform->getId();
125 if (_waveforms.contains(id))
126 {
127 throw std::runtime_error("A waveform with the ID " + std::to_string(id) + " already exists.");
128 }
129 _waveform_ids_by_name[waveform->getName()] = id;
130 _waveforms[id] = std::move(waveform);
131 }
132
133 void World::add(std::unique_ptr<Antenna> antenna)
134 {
135 const SimId id = antenna->getId();
136 if (_antennas.contains(id))
137 {
138 throw std::runtime_error("An antenna with the ID " + std::to_string(id) + " already exists.");
139 }
140 _antennas[id] = std::move(antenna);
141 }
142
143 void World::add(std::unique_ptr<PrototypeTiming> timing)
144 {
145 const SimId id = timing->getId();
146 if (_timings.contains(id))
147 {
148 throw std::runtime_error("A timing source with the ID " + std::to_string(id) + " already exists.");
149 }
150 _timings[id] = std::move(timing);
151 }
152
154 {
155 const auto it = _waveforms.find(id);
156 return it != _waveforms.end() ? it->second.get() : nullptr;
157 }
158
160 {
161 const auto it = _antennas.find(id);
162 return it != _antennas.end() ? it->second.get() : nullptr;
163 }
164
166 {
167 const auto it = _timings.find(id);
168 return it != _timings.end() ? it->second.get() : nullptr;
169 }
170
172 {
173 for (auto& p : _platforms)
174 {
175 if (p->getId() == id)
176 return p.get();
177 }
178 return nullptr;
179 }
180
182 {
183 for (auto& tx : _transmitters)
184 if (tx->getId() == id)
185 return tx.get();
186 return nullptr;
187 }
188
190 {
191 const auto it = _transmitters_by_name.find(name);
192 return it != _transmitters_by_name.end() ? it->second : nullptr;
193 }
194
196 {
197 for (auto& rx : _receivers)
198 if (rx->getId() == id)
199 return rx.get();
200 return nullptr;
201 }
202
203 RadarSignal* World::findWaveformByName(const std::string& name)
204 {
205 const auto it = _waveform_ids_by_name.find(name);
206 return it != _waveform_ids_by_name.end() ? findWaveform(it->second) : nullptr;
207 }
208
210 {
211 const auto include_streaming_interval_start = [](std::optional<RealType>& earliest,
212 const RealType segment_start, const RealType segment_end,
213 const bool allow_pre_start)
214 {
218 {
219 return;
220 }
221
223 allow_pre_start && segment_start < sim_start ? segment_start : std::max(sim_start, segment_start);
224 earliest = earliest.has_value() ? std::min(*earliest, required_start) : required_start;
225 };
226
227 std::optional<RealType> earliest;
228 for (const auto& transmitter_ptr : _transmitters)
229 {
230 if (transmitter_ptr == nullptr || !transmitter_ptr->isStreamingMode())
231 {
232 continue;
233 }
234
235 const auto& schedule = transmitter_ptr->getSchedule();
236 if (schedule.empty())
237 {
239 continue;
240 }
241
242 for (const auto& period : schedule)
243 {
245 }
246 }
247
248 for (const auto& receiver_ptr : _receivers)
249 {
250 if (receiver_ptr == nullptr ||
253 {
254 continue;
255 }
256
257 const auto& schedule = receiver_ptr->getSchedule();
258 if (schedule.empty())
259 {
261 continue;
262 }
263
264 for (const auto& period : schedule)
265 {
267 }
268 }
269
270 return earliest.value_or(params::startTime());
271 }
272
274 {
275 for (auto& tgt : _targets)
276 if (tgt->getId() == id)
277 return tgt.get();
278 return nullptr;
279 }
280
281 void World::replace(std::unique_ptr<Target> target)
282 {
283 const SimId id = target->getId();
284 for (auto& t : _targets)
285 {
286 if (t->getId() == id)
287 {
288 t = std::move(target);
289 return;
290 }
291 }
292 _targets.push_back(std::move(target));
293 }
294
295 void World::replace(std::unique_ptr<Antenna> antenna)
296 {
297 const SimId id = antenna->getId();
298 const Antenna* new_ptr = antenna.get();
299
300 std::unique_ptr<Antenna> old_owned;
301 const Antenna* old_ptr = nullptr;
302
303 if (auto it = _antennas.find(id); it != _antennas.end())
304 {
305 old_owned = std::move(it->second);
306 old_ptr = old_owned.get();
307 it->second = std::move(antenna);
308 }
309 else
310 {
311 _antennas[id] = std::move(antenna);
312 }
313
314 if ((old_ptr != nullptr) && old_ptr != new_ptr)
315 {
316 for (auto& tx : _transmitters)
317 if (tx->getAntenna() == old_ptr)
318 tx->setAntenna(new_ptr);
319
320 for (auto& rx : _receivers)
321 if (rx->getAntenna() == old_ptr)
322 rx->setAntenna(new_ptr);
323 }
324 }
325
326 void World::replace(std::unique_ptr<RadarSignal> waveform)
327 {
328 const SimId id = waveform->getId();
329 RadarSignal* new_ptr = waveform.get();
330 const std::string new_name = waveform->getName();
331
332 std::unique_ptr<RadarSignal> old_owned;
333 const RadarSignal* old_ptr = nullptr;
334
335 if (auto it = _waveforms.find(id); it != _waveforms.end())
336 {
337 old_owned = std::move(it->second);
338 old_ptr = old_owned.get();
339 _waveform_ids_by_name.erase(old_owned->getName());
340 _waveform_ids_by_name[new_name] = id;
341 it->second = std::move(waveform);
342 }
343 else
344 {
345 _waveform_ids_by_name[new_name] = id;
346 _waveforms[id] = std::move(waveform);
347 }
348
349 if ((old_ptr != nullptr) && old_ptr != new_ptr)
350 {
351 for (auto& tx : _transmitters)
352 if (tx->getSignal() == old_ptr)
353 tx->setSignal(new_ptr);
354 }
355 }
356
357 void World::replace(std::unique_ptr<PrototypeTiming> timing)
358 {
359 const SimId id = timing->getId();
360 const PrototypeTiming* new_ptr = timing.get();
361
362 std::unique_ptr<PrototypeTiming> old_owned;
363 const PrototypeTiming* old_ptr = nullptr;
364
365 if (auto it = _timings.find(id); it != _timings.end())
366 {
367 old_owned = std::move(it->second);
368 old_ptr = old_owned.get();
369 it->second = std::move(timing);
370 }
371 else
372 {
373 _timings[id] = std::move(timing);
374 }
375
376 std::unordered_map<const timing::Timing*, std::shared_ptr<timing::Timing>> refreshed_instances;
378 {
379 const auto current_timing = radar_obj->getTiming();
380 if (!current_timing || (current_timing->getId() != id))
381 {
382 return;
383 }
384
385 const timing::Timing* const timing_key = current_timing.get();
386 const auto [it, inserted] = refreshed_instances.try_emplace(timing_key);
387 if (inserted)
388 {
389 auto refreshed =
390 std::make_shared<timing::Timing>(new_ptr->getName(), current_timing->getSeed(), new_ptr->getId());
391 refreshed->initializeModel(new_ptr);
392 it->second = std::move(refreshed);
393 }
394 radar_obj->setTiming(it->second);
395 };
396
397 if ((old_ptr != nullptr) && old_ptr != new_ptr)
398 {
399 for (auto& tx : _transmitters)
401
402 for (auto& rx : _receivers)
404 }
405 }
406
408 {
409 _platforms.clear();
410 _transmitters.clear();
411 _transmitters_by_name.clear();
412 _receivers.clear();
413 _targets.clear();
414 _waveforms.clear();
415 _waveform_ids_by_name.clear();
416 _antennas.clear();
417 _timings.clear();
418 _event_queue = {};
419 _simulation_state = {};
420 }
421
422 void World::swap(World& other) noexcept
423 {
424 using std::swap;
425
426 _platforms.swap(other._platforms);
427 _transmitters.swap(other._transmitters);
428 _receivers.swap(other._receivers);
429 _targets.swap(other._targets);
430 _waveforms.swap(other._waveforms);
431 _waveform_ids_by_name.swap(other._waveform_ids_by_name);
432 _transmitters_by_name.swap(other._transmitters_by_name);
433 _antennas.swap(other._antennas);
434 _timings.swap(other._timings);
435 _event_queue.swap(other._event_queue);
436 swap(_simulation_state, other._simulation_state);
437 }
438
440 {
443
444 for (const auto& transmitter : _transmitters)
445 {
446 scheduleInitialTransmitterEvents(transmitter.get(), sim_start, sim_end);
447 }
448
449 for (const auto& receiver : _receivers)
450 {
451 scheduleInitialReceiverEvents(receiver.get(), sim_start, sim_end);
452 }
453 }
454
455 void World::scheduleInitialTransmitterEvents(Transmitter* const transmitter, const RealType sim_start,
456 const RealType sim_end)
457 {
459 {
460 scheduleInitialPulsedTransmitterEvent(transmitter, sim_start, sim_end);
461 return;
462 }
463 scheduleInitialStreamingTransmitterEvents(transmitter, sim_start, sim_end);
464 }
465
466 void World::scheduleInitialPulsedTransmitterEvent(Transmitter* const transmitter, const RealType sim_start,
467 const RealType sim_end)
468 {
469 // Find the first valid pulse time starting from the simulation start time.
470 if (auto start_time = transmitter->getNextPulseTime(sim_start); start_time && *start_time <= sim_end)
471 {
472 _event_queue.push({*start_time, EventType::TX_PULSED_START, transmitter});
473 }
474 }
475
476 void World::scheduleInitialStreamingTransmitterEvents(Transmitter* const transmitter, const RealType sim_start,
477 const RealType sim_end)
478 {
479 const auto& schedule = transmitter->getSchedule();
480 if (schedule.empty())
481 {
483 pushStreamingTransmitterEvents(transmitter, sim_start, end);
484 return;
485 }
486
487 for (const auto& period : schedule)
488 {
489 const RealType start = std::max(sim_start, period.start);
490 const RealType end = makeActiveSource(transmitter, period.start, std::min(sim_end, period.end)).segment_end;
491 pushStreamingTransmitterEvents(transmitter, start, end);
492 }
493 }
494
495 void World::pushStreamingTransmitterEvents(Transmitter* const transmitter, const RealType start, const RealType end)
496 {
497 if (start < end)
498 {
499 _event_queue.push({start, EventType::TX_STREAMING_START, transmitter});
500 _event_queue.push({end, EventType::TX_STREAMING_END, transmitter});
501 }
502 }
503
504 void World::scheduleInitialReceiverEvents(Receiver* const receiver, const RealType sim_start,
505 const RealType sim_end)
506 {
508 {
509 scheduleInitialPulsedReceiverEvent(receiver, sim_end);
510 return;
511 }
512 scheduleInitialStreamingReceiverEvents(receiver, sim_start, sim_end);
513 }
514
515 void World::scheduleInitialPulsedReceiverEvent(Receiver* const receiver, const RealType sim_end)
516 {
518 if (auto start = receiver->getNextWindowTime(nominal_start); start && *start < sim_end)
519 {
520 _event_queue.push({*start, EventType::RX_PULSED_WINDOW_START, receiver});
521 }
522 }
523
524 void World::scheduleInitialStreamingReceiverEvents(Receiver* const receiver, const RealType sim_start,
525 const RealType sim_end)
526 {
527 const auto& schedule = receiver->getSchedule();
528 if (schedule.empty())
529 {
530 _event_queue.push({sim_start, EventType::RX_STREAMING_START, receiver});
531 _event_queue.push({sim_end, EventType::RX_STREAMING_END, receiver});
532 return;
533 }
534
535 for (const auto& period : schedule)
536 {
537 const RealType start = std::max(sim_start, period.start);
538 const RealType end = std::min(sim_end, period.end);
539 pushStreamingReceiverEvents(receiver, start, end);
540 }
541 }
542
543 void World::pushStreamingReceiverEvents(Receiver* const receiver, const RealType start, const RealType end)
544 {
545 if (start < end)
546 {
547 _event_queue.push({start, EventType::RX_STREAMING_START, receiver});
548 _event_queue.push({end, EventType::RX_STREAMING_END, receiver});
549 }
550 }
551
553 {
554 for (const auto& rx_ptr : _receivers)
555 {
556 auto& rx = *rx_ptr;
557 rx.clearResolvedDechirpSources();
558 if (!rx.isDechirpEnabled())
559 {
560 continue;
561 }
562 if (rx.getMode() != radar::OperationMode::FMCW_MODE)
563 {
564 throw std::runtime_error("Receiver '" + rx.getName() + "' enables dechirping outside FMCW mode.");
565 }
566
567 auto reference = rx.getDechirpReference();
568 std::vector<ActiveStreamingSource> sources;
569 const std::string owner = "Receiver '" + rx.getName() + "'";
570 switch (reference.source)
571 {
572 case Receiver::DechirpReferenceSource::Attached:
573 {
574 const auto* const tx = dynamic_cast<const Transmitter*>(rx.getAttached());
576 reference.transmitter_id = tx->getId();
577 reference.transmitter_name = tx->getName();
579 break;
580 }
581 case Receiver::DechirpReferenceSource::Transmitter:
582 {
583 auto* const tx = findTransmitterByName(reference.name);
585 reference.transmitter_id = tx->getId();
586 reference.transmitter_name = tx->getName();
588 break;
589 }
590 case Receiver::DechirpReferenceSource::Custom:
591 {
592 auto* const waveform = findWaveformByName(reference.name);
593 if (waveform == nullptr || !waveform->isFmcwFamily())
594 {
595 throw std::runtime_error(owner + " custom dechirp reference waveform '" + reference.name +
596 "' must be a top-level FMCW waveform.");
597 }
598 reference.waveform_id = waveform->getId();
599 reference.waveform_name = waveform->getName();
600 sources = waveformDechirpSources(waveform, &rx);
601 break;
602 }
603 case Receiver::DechirpReferenceSource::None:
604 throw std::runtime_error(owner + " enables dechirping without a dechirp reference.");
605 }
606
607 if (sources.empty())
608 {
609 throw std::runtime_error(owner + " dechirp reference has no active LO segments in the simulation.");
610 }
611 std::ranges::sort(sources, [](const ActiveStreamingSource& lhs, const ActiveStreamingSource& rhs)
612 { return lhs.segment_start < rhs.segment_start; });
613 rx.setDechirpReference(std::move(reference));
614 rx.setResolvedDechirpSources(std::move(sources));
615 }
616 }
617
618 std::string World::dumpEventQueue() const
619 {
620 if (_event_queue.empty())
621 {
622 return "Event Queue is empty.\n";
623 }
624
625 std::stringstream ss;
626 ss << std::fixed << std::setprecision(6);
627
628 const std::string separator = "--------------------------------------------------------------------";
629 const std::string title = "| Event Queue Contents (" + std::to_string(_event_queue.size()) + " events)";
630 if (separator.size() > static_cast<std::size_t>(std::numeric_limits<int>::max()))
631 {
632 throw std::runtime_error("Separator width exceeds stream formatting limits.");
633 }
634 const int title_width = static_cast<int>(separator.size()) - 1;
635
636 ss << separator << "\n"
637 << std::left << std::setw(title_width) << title << "|\n"
638 << separator << "\n"
639 << "| " << std::left << std::setw(12) << "Timestamp" << " | " << std::setw(21) << "Event Type" << " | "
640 << std::setw(25) << "Source Object" << " |\n"
641 << separator << "\n";
642
643 auto queue_copy = _event_queue;
644
645 while (!queue_copy.empty())
646 {
647 const auto [timestamp, event_type, source_object] = queue_copy.top();
648 queue_copy.pop();
649
650 ss << "| " << std::right << std::setw(12) << timestamp << " | " << std::left << std::setw(21)
651 << toString(event_type) << " | " << std::left << std::setw(25) << source_object->getName() << " |\n";
652 }
653 ss << separator << "\n";
654
655 return ss.str();
656 }
657}
Header file defining various types of antennas and their gain patterns.
const Transmitter & transmitter
const Receiver & receiver
Abstract base class representing an antenna.
The World class manages the simulator environment.
Definition world.h:39
void scheduleInitialEvents()
Populates the event queue with the initial events for the simulation.
Definition world.cpp:439
void add(std::unique_ptr< radar::Platform > plat) noexcept
Adds a radar platform to the simulation world.
Definition world.cpp:110
void replace(std::unique_ptr< radar::Target > target)
Replaces an existing target, updating internal pointers.
Definition world.cpp:281
fers_signal::RadarSignal * findWaveform(const SimId id)
Finds a radar signal by ID.
Definition world.cpp:153
radar::Target * findTarget(const SimId id)
Finds a target by ID.
Definition world.cpp:273
fers_signal::RadarSignal * findWaveformByName(const std::string &name)
Finds a waveform by name.
Definition world.cpp:203
radar::Receiver * findReceiver(const SimId id)
Finds a receiver by ID.
Definition world.cpp:195
radar::Transmitter * findTransmitter(const SimId id)
Finds a transmitter by ID.
Definition world.cpp:181
void clear() noexcept
Clears all objects and assets from the simulation world.
Definition world.cpp:407
void resolveReceiverDechirpReferences()
Resolves and validates receiver FMCW dechirp references after all components are loaded.
Definition world.cpp:552
timing::PrototypeTiming * findTiming(const SimId id)
Finds a timing source by ID.
Definition world.cpp:165
antenna::Antenna * findAntenna(const SimId id)
Finds an antenna by ID.
Definition world.cpp:159
void swap(World &other) noexcept
Exchanges all owned world state with another world.
Definition world.cpp:422
std::string dumpEventQueue() const
Dumps the current state of the event queue to a string for debugging.
Definition world.cpp:618
radar::Platform * findPlatform(const SimId id)
Finds a platform by ID.
Definition world.cpp:171
radar::Transmitter * findTransmitterByName(const std::string &name)
Finds a transmitter by name.
Definition world.cpp:189
RealType earliestPhaseNoiseLookupStart() const
Finds the earliest simulation time that can require CW phase-noise samples.
Definition world.cpp:209
Class representing a radar signal with associated properties.
SimId getId() const noexcept
Gets the unique ID of the radar signal.
const std::string & getName() const noexcept
Gets the name of the radar signal.
bool isFmcwFamily() const noexcept
Returns true when this signal belongs to the FMCW waveform family.
Represents a simulation platform with motion and rotation paths.
Definition platform.h:32
Manages radar signal reception and response processing.
Definition receiver.h:47
const std::vector< SchedulePeriod > & getSchedule() const noexcept
Retrieves the list of active reception periods.
Definition receiver.h:382
RealType getWindowStart(unsigned window) const
Retrieves the start time of a specific radar window.
Definition receiver.cpp:371
std::optional< RealType > getNextWindowTime(RealType time) const
Determines the next valid window start time at or after the given time.
Definition receiver.cpp:384
OperationMode getMode() const noexcept
Gets the operational mode of the receiver.
Definition receiver.h:209
Base class for radar targets.
Definition target.h:118
Represents a radar transmitter system.
Definition transmitter.h:34
std::optional< RealType > getNextPulseTime(RealType time) const
Determines the valid simulation time for a pulse at or after the given time.
const std::vector< SchedulePeriod > & getSchedule() const noexcept
Retrieves the list of active transmission periods.
OperationMode getMode() const noexcept
Gets the operational mode of the transmitter.
Definition transmitter.h:95
Manages timing properties such as frequency, offsets, and synchronization.
Represents a timing source for simulation.
Definition timing.h:36
double RealType
Type for real numbers.
Definition config.h:27
ActiveStreamingSource makeActiveSource(const radar::Transmitter *const tx, const RealType segment_start, const RealType segment_end)
Builds an active-source cache from a streaming transmitter and segment bounds.
std::string toString(const EventType type)
Converts an EventType enum to its string representation.
Definition sim_events.h:74
@ RX_PULSED_WINDOW_START
A pulsed receiver opens its listening window.
@ TX_STREAMING_END
A streaming transmitter stops transmitting.
@ RX_STREAMING_END
A streaming receiver stops listening.
@ TX_STREAMING_START
A streaming transmitter starts transmitting.
@ TX_PULSED_START
A pulsed transmitter begins emitting a pulse.
@ RX_STREAMING_START
A streaming receiver starts listening.
ActiveStreamingSource makeActiveSourceFromWaveform(const fers_signal::RadarSignal *const signal, const RealType segment_start, const RealType segment_end)
Builds an active-source cache directly from a waveform for receiver-local LO references.
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.
@ CW_MODE
The component operates in a continuous-wave mode.
@ FMCW_MODE
The component operates in an FMCW streaming 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
math::Vec3 max
Cached description of an active streaming transmitter segment.
RealType segment_end
Segment end time in seconds.
Timing source for simulation objects.
Header file for the World class in the simulator.