FERS 0.1.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
serial Namespace Reference

Namespaces

namespace  fmcw_validation
 
namespace  kml_generator_utils
 
namespace  rotation_angle_utils
 
namespace  rotation_warning_utils
 
namespace  vita49
 
namespace  xml_parser_utils
 
namespace  xml_serializer_utils
 

Classes

class  Hdf5OutputSink
 
class  KmlGenerator
 Generates KML files from FERS simulation scenarios for geographical visualization. More...
 
class  Response
 Manages radar signal responses from a transmitter. More...
 

Functions

std::vector< std::vector< RealType > > readPattern (const std::string &name, const std::string &datasetName)
 Reads a 2D antenna gain pattern from the named dataset.
 
void writeOutputFileMetadataAttributes (HighFive::File &file, const core::OutputFileMetadata &metadata)
 Writes additive FERS output metadata attributes to an open HDF5 file.
 
void readPulseData (const std::string &name, std::vector< ComplexType > &data)
 Reads pulse data from an HDF5 file.
 
void addChunkToFile (HighFive::File &file, const std::vector< ComplexType > &data, RealType time, RealType fullscale, unsigned count, const core::PulseChunkMetadata *metadata=nullptr)
 Adds a chunk of data to an HDF5 file.
 
std::unique_ptr< core::ReceiverOutputSinkmakeHdf5OutputSink (std::string output_dir, std::shared_ptr< core::OutputMetadataCollector > metadata_collector)
 
std::unique_ptr< antenna::Antennaparse_antenna_from_json (const nlohmann::json &j)
 Parses an Antenna from JSON.
 
std::unique_ptr< fers_signal::RadarSignalparse_waveform_from_json (const nlohmann::json &j)
 Parses a Waveform from JSON.
 
std::unique_ptr< timing::PrototypeTimingparse_timing_from_json (const nlohmann::json &j, SimId id)
 Parses a timing prototype from JSON.
 
void update_parameters_from_json (const nlohmann::json &j, std::mt19937 &masterSeeder)
 Updates global simulation parameters from JSON.
 
std::unique_ptr< antenna::Antennaparse_required_update_antenna (const nlohmann::json &j)
 
bool antenna_pattern_requires_replacement (const std::string_view pattern, const antenna::Antenna *ant) noexcept
 
void update_existing_antenna_pattern_fields (const nlohmann::json &j, antenna::Antenna *ant, core::World &world)
 
void update_antenna_from_json (const nlohmann::json &j, antenna::Antenna *ant, core::World &world)
 Updates an antenna from JSON without full context recreation.
 
void update_platform_paths_from_json (const nlohmann::json &j, radar::Platform *plat)
 Updates a platform's motion and rotation paths from JSON.
 
void update_transmitter_mode_from_json (const nlohmann::json &j, radar::Transmitter &tx)
 
void update_transmitter_waveform_from_json (const nlohmann::json &j, radar::Transmitter &tx, core::World &world)
 
void update_transmitter_antenna_from_json (const nlohmann::json &j, radar::Transmitter &tx, core::World &world)
 
void update_transmitter_timing_from_json (const nlohmann::json &j, radar::Transmitter &tx, core::World &world)
 
void validate_transmitter_signal_state (const radar::Transmitter &tx, const std::string &owner)
 
void update_transmitter_schedule_from_json (const nlohmann::json &j, radar::Transmitter &tx, const std::string &owner)
 
void update_transmitter_from_json (const nlohmann::json &j, radar::Transmitter *tx, core::World &world, std::mt19937 &masterSeeder)
 Updates a transmitter from JSON without full context recreation.
 
void update_receiver_mode_from_json (const nlohmann::json &j, radar::Receiver &rx)
 
void update_receiver_noise_and_flags_from_json (const nlohmann::json &j, radar::Receiver &rx)
 
void update_receiver_antenna_from_json (const nlohmann::json &j, radar::Receiver &rx, core::World &world)
 
void update_receiver_timing_from_json (const nlohmann::json &j, radar::Receiver &rx, core::World &world)
 
void update_receiver_schedule_from_json (const nlohmann::json &j, radar::Receiver &rx)
 
void update_receiver_from_json (const nlohmann::json &j, radar::Receiver *rx, core::World &world, std::mt19937 &masterSeeder)
 Updates a receiver from JSON without full context recreation.
 
nlohmann::json monostatic_transmitter_json (const nlohmann::json &j)
 
void update_monostatic_receiver_basics (const nlohmann::json &j, const radar::Transmitter &tx, radar::Receiver &rx, core::World &world)
 
void update_monostatic_timing_from_json (const nlohmann::json &j, radar::Transmitter &tx, radar::Receiver &rx, core::World &world)
 
void update_monostatic_schedule_from_json (const nlohmann::json &j, radar::Transmitter &tx, radar::Receiver &rx)
 
void update_monostatic_from_json (const nlohmann::json &j, radar::Transmitter *tx, radar::Receiver *rx, core::World &world, std::mt19937 &masterSeeder)
 Updates a monostatic radar from JSON without full context recreation.
 
void update_target_from_json (const nlohmann::json &j, radar::Target *tgt, core::World &world, std::mt19937 &masterSeeder)
 Updates a target from JSON without full context recreation.
 
void update_timing_from_json (const nlohmann::json &j, core::World &world, SimId id)
 Updates a timing source from JSON without full context recreation.
 
nlohmann::json world_to_json (const core::World &world)
 Serializes the entire simulation world into a nlohmann::json object.
 
void json_to_world (const nlohmann::json &j, core::World &world, std::mt19937 &masterSeeder)
 Deserializes a nlohmann::json object and reconstructs the simulation world.
 
std::unique_ptr< RadarSignalloadWaveformFromFile (const std::string &name, const std::string &filename, RealType power, RealType carrierFreq, const SimId id=0)
 Loads a radar waveform from a file and returns a RadarSignal object.
 
void parseSimulation (const std::string &filename, core::World *world, bool validate, std::mt19937 &masterSeeder)
 Parses a simulation configuration from an XML file.
 
void parseSimulationFromString (const std::string &xmlContent, core::World *world, bool validate, std::mt19937 &masterSeeder)
 Parses a simulation configuration directly from an XML string in memory.
 
std::string world_to_xml_string (const core::World &world)
 Serializes the entire simulation world into an XML formatted string.
 

Variables

std::mutex hdf5_global_mutex
 Global mutex to protect all HDF5 C-library calls, which are not thread-safe.
 

Function Documentation

◆ addChunkToFile()

void serial::addChunkToFile ( HighFive::File &  file,
const std::vector< ComplexType > &  data,
RealType  time,
RealType  fullscale,
unsigned  count,
const core::PulseChunkMetadata metadata = nullptr 
)

Adds a chunk of data to an HDF5 file.

Parameters
fileThe HDF5 file where the chunk is written.
dataA vector of complex data to be written.
timeThe time attribute associated with the chunk.
fullscaleThe fullscale attribute for the chunk.
countThe sequential count number for chunk naming.
Exceptions
std::runtime_errorIf there is an error writing data or setting attributes.

Definition at line 271 of file hdf5_handler.cpp.

273 {
274 std::scoped_lock const lock(hdf5_global_mutex);
275
276 const std::size_t size = data.size();
277
278 const std::string base_chunk_name = "chunk_" + std::format("{:06}", count);
279 const std::string i_chunk_name = base_chunk_name + "_I";
280 const std::string q_chunk_name = base_chunk_name + "_Q";
281
282 std::vector<RealType> i(size), q(size);
283 std::ranges::transform(data, i.begin(), [](const ComplexType& c) { return c.real(); });
284 std::ranges::transform(data, q.begin(), [](const ComplexType& c) { return c.imag(); });
285
286 auto write_chunk = [&](const std::string& chunkName, const std::vector<RealType>& chunkData)
287 {
288 try
289 {
290 HighFive::DataSet dataset =
291 file.createDataSet<RealType>(chunkName, HighFive::DataSpace::From(chunkData));
292 dataset.write(chunkData);
293 }
294 catch (const HighFive::Exception& err)
295 {
296 LOG(Level::FATAL, "Error while writing data to HDF5 file: {}", err.what());
297 throw std::runtime_error("Error while writing data to HDF5 file: " + chunkName + " - " + err.what());
298 }
299 };
300
301 auto set_chunk_attributes = [&](const std::string& chunkName)
302 {
303 try
304 {
305 HighFive::DataSet dataset = file.getDataSet(chunkName);
306 dataset.createAttribute("time", time);
307 dataset.createAttribute("rate", params::rate());
308 dataset.createAttribute("fullscale", fullscale);
309 if (metadata != nullptr)
310 {
311 dataset.createAttribute("chunk_index", metadata->chunk_index);
312 dataset.createAttribute("sample_count", static_cast<unsigned long long>(metadata->sample_count));
313 dataset.createAttribute("sample_start", static_cast<unsigned long long>(metadata->sample_start));
314 dataset.createAttribute("sample_end_exclusive",
315 static_cast<unsigned long long>(metadata->sample_end_exclusive));
316 }
317 }
318 catch (const HighFive::Exception& err)
319 {
320 LOG(Level::FATAL, "Error while setting attributes on chunk: {}", err.what());
321 throw std::runtime_error("Error while setting attributes on chunk: " + chunkName + " - " + err.what());
322 }
323 };
324
327
330 }
double RealType
Type for real numbers.
Definition config.h:27
std::complex< RealType > ComplexType
Type for complex numbers.
Definition config.h:35
#define LOG(level,...)
Definition logging.h:19
RealType rate() noexcept
Get the rendering sample rate.
Definition parameters.h:121
math::Vec3 max
RealType c
std::uint64_t sample_end_exclusive
Exclusive global sample index for the chunk end.
std::uint64_t sample_count
Number of samples in the chunk.
std::uint64_t sample_start
Inclusive global sample index for the chunk start.
unsigned chunk_index
Zero-based chunk index in the receiver output sequence.

References c, core::PulseChunkMetadata::chunk_index, hdf5_global_mutex, LOG, max, params::rate(), core::PulseChunkMetadata::sample_count, core::PulseChunkMetadata::sample_end_exclusive, and core::PulseChunkMetadata::sample_start.

Referenced by serial::Hdf5OutputSink::Impl::writePulsedBlock().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ antenna_pattern_requires_replacement()

bool serial::antenna_pattern_requires_replacement ( const std::string_view  pattern,
const antenna::Antenna ant 
)
noexcept

Definition at line 1732 of file json_serializer.cpp.

1733 {
1734 if (pattern == "isotropic")
1735 {
1736 return dynamic_cast<const antenna::Isotropic*>(ant) == nullptr;
1737 }
1738 if (pattern == "sinc")
1739 {
1740 return dynamic_cast<const antenna::Sinc*>(ant) == nullptr;
1741 }
1742 if (pattern == "gaussian")
1743 {
1744 return dynamic_cast<const antenna::Gaussian*>(ant) == nullptr;
1745 }
1746 if (pattern == "squarehorn")
1747 {
1748 return dynamic_cast<const antenna::SquareHorn*>(ant) == nullptr;
1749 }
1750 if (pattern == "parabolic")
1751 {
1752 return dynamic_cast<const antenna::Parabolic*>(ant) == nullptr;
1753 }
1754 if (pattern == "xml")
1755 {
1756 return dynamic_cast<const antenna::XmlAntenna*>(ant) == nullptr;
1757 }
1758 if (pattern == "file")
1759 {
1760 return dynamic_cast<const antenna::H5Antenna*>(ant) == nullptr;
1761 }
1762 return false;
1763 }
Represents a Gaussian-shaped antenna gain pattern.
Represents an antenna whose gain pattern is loaded from a HDF5 file.
Represents an isotropic antenna with uniform gain in all directions.
Represents a parabolic reflector antenna.
Represents a sinc function-based antenna gain pattern.
Represents a square horn antenna.
Represents an antenna whose gain pattern is defined by an XML file.

References max.

Referenced by update_antenna_from_json().

+ Here is the caller graph for this function:

◆ json_to_world()

void serial::json_to_world ( const nlohmann::json &  j,
core::World world,
std::mt19937 &  masterSeeder 
)

Deserializes a nlohmann::json object and reconstructs the simulation world.

This function is the counterpart to world_to_json. It performs a full state replacement by clearing the existing world and rebuilding it from the provided JSON. This "replace" strategy simplifies state management, guaranteeing that the C++ core is always perfectly synchronized with the state provided by the UI without requiring complex diffing or patching logic. It also handles re-seeding the master random number generator to ensure that loading a state also restores its deterministic behavior.

Parameters
jThe json object to deserialize.
worldThe world object to populate.
masterSeederA reference to the master random number generator, which will be re-seeded.

Definition at line 2293 of file json_serializer.cpp.

2294 {
2297 world.swap(parsed_world);
2298 }
The World class manages the simulator environment.
Definition world.h:39
void swap(World &other) noexcept
Exchanges all owned world state with another world.
Definition world.cpp:422

References max, and core::World::swap().

Referenced by fers_update_scenario_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ loadWaveformFromFile()

std::unique_ptr< fers_signal::RadarSignal > serial::loadWaveformFromFile ( const std::string &  name,
const std::string &  filename,
RealType  power,
RealType  carrierFreq,
const SimId  id = 0 
)

Loads a radar waveform from a file and returns a RadarSignal object.

Parameters
nameThe name of the radar signal.
filenameThe path to the file containing the waveform data.
powerThe power of the radar signal in the waveform.
carrierFreqThe carrier frequency of the radar signal.
Returns
A unique pointer to a RadarSignal object loaded with the waveform data.
Exceptions
std::runtime_errorIf the file cannot be opened or the file format is unrecognized.

Definition at line 157 of file waveform_factory.cpp.

159 {
160 const std::filesystem::path filepath = filename;
161 const auto extension = filepath.extension().string();
162
163 if (hasExtension(extension, ".csv"))
164 {
165 auto wave = loadWaveformFromCsvFile(name, filepath, power, carrierFreq, id);
166 wave->setFilename(filename);
167 return wave;
168 }
169 if (hasExtension(extension, ".h5"))
170 {
171 auto wave = loadWaveformFromHdf5File(name, filepath, power, carrierFreq, id);
172 wave->setFilename(filename);
173 return wave;
174 }
175
176 LOG(logging::Level::FATAL, "Unrecognized file extension '{}' for file: '{}'", extension, filename);
177 throw std::runtime_error("Unrecognized file extension '" + extension + "' for file: " + filename);
178 }
@ FATAL
Fatal level for severe error events.

References logging::FATAL, LOG, and max.

Referenced by serial::xml_parser_utils::createDefaultAssetLoaders(), and fers_signal::from_json().

+ Here is the caller graph for this function:

◆ makeHdf5OutputSink()

std::unique_ptr< core::ReceiverOutputSink > serial::makeHdf5OutputSink ( std::string  output_dir,
std::shared_ptr< core::OutputMetadataCollector metadata_collector 
)

Definition at line 373 of file hdf5_output_sink.cpp.

374 {
375 return std::make_unique<Hdf5OutputSink>(std::move(output_dir), std::move(metadata_collector));
376 }

Referenced by core::runEventDrivenSim().

+ Here is the caller graph for this function:

◆ monostatic_transmitter_json()

nlohmann::json serial::monostatic_transmitter_json ( const nlohmann::json &  j)

Definition at line 2109 of file json_serializer.cpp.

2110 {
2111 auto transmitter_json = j;
2112 if (transmitter_json.contains("fmcw_mode") && transmitter_json.at("fmcw_mode").is_object())
2113 {
2114 transmitter_json["fmcw_mode"].erase("dechirp_mode");
2115 transmitter_json["fmcw_mode"].erase("dechirp_reference");
2116 transmitter_json["fmcw_mode"].erase("if_sample_rate");
2117 transmitter_json["fmcw_mode"].erase("if_filter_bandwidth");
2118 transmitter_json["fmcw_mode"].erase("if_filter_transition_width");
2119 }
2120 return transmitter_json;
2121 }

References max.

Referenced by update_monostatic_from_json().

+ Here is the caller graph for this function:

◆ parse_antenna_from_json()

std::unique_ptr< antenna::Antenna > serial::parse_antenna_from_json ( const nlohmann::json &  j)

Parses an Antenna from JSON.

Definition at line 1691 of file json_serializer.cpp.

1692 {
1693 std::unique_ptr<antenna::Antenna> ant;
1695 return ant;
1696 }
void from_json(const nlohmann::json &j, std::unique_ptr< Antenna > &ant)

References antenna::from_json(), and max.

Referenced by parse_required_update_antenna().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parse_required_update_antenna()

std::unique_ptr< antenna::Antenna > serial::parse_required_update_antenna ( const nlohmann::json &  j)

Definition at line 1719 of file json_serializer.cpp.

1720 {
1722 if (parsed == nullptr)
1723 {
1724 const auto name = j.value("name", std::string{});
1725 const auto pattern = j.value("pattern", "isotropic");
1726 throw std::runtime_error("Cannot update antenna '" + name + "' to pattern '" + pattern +
1727 "' without a filename.");
1728 }
1729 return parsed;
1730 }
std::unique_ptr< antenna::Antenna > parse_antenna_from_json(const nlohmann::json &j)
Parses an Antenna from JSON.

References max, and parse_antenna_from_json().

Referenced by update_antenna_from_json(), and update_existing_antenna_pattern_fields().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parse_timing_from_json()

std::unique_ptr< timing::PrototypeTiming > serial::parse_timing_from_json ( const nlohmann::json &  j,
const SimId  id 
)

Parses a timing prototype from JSON.

Definition at line 1705 of file json_serializer.cpp.

1706 {
1707 auto timing = std::make_unique<timing::PrototypeTiming>(j.at("name").get<std::string>(), id);
1708 j.get_to(*timing);
1709 return timing;
1710 }

References max.

Referenced by update_timing_from_json().

+ Here is the caller graph for this function:

◆ parse_waveform_from_json()

std::unique_ptr< fers_signal::RadarSignal > serial::parse_waveform_from_json ( const nlohmann::json &  j)

Parses a Waveform from JSON.

Definition at line 1698 of file json_serializer.cpp.

1699 {
1700 std::unique_ptr<fers_signal::RadarSignal> wf;
1702 return wf;
1703 }
void from_json(const nlohmann::json &j, std::unique_ptr< RadarSignal > &rs)

References fers_signal::from_json(), and max.

Referenced by fers_update_waveform_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parseSimulation()

void serial::parseSimulation ( const std::string &  filename,
core::World world,
bool  validate,
std::mt19937 &  masterSeeder 
)

Parses a simulation configuration from an XML file.

This function acts as the primary facade for the simulator's XML loading pipeline. It performs the following steps:

  1. Resets the target World and global parameters (params::params).
  2. Loads the main XML file.
  3. Recursively finds and merges any <include> files into the main document.
  4. Optionally validates the combined document against the built-in DTD and XSD schemas.
  5. Uses the internal parser utilities to instantiate simulation objects.
  6. Updates the global params::params with the parsed context parameters.
Parameters
filenameThe filesystem path to the main XML simulation script.
worldA pointer to the World object to be populated with parsed components.
validateA boolean indicating whether to perform strict XML schema validation.
masterSeederA reference to the master random number generator used for assigning independent seeds to components.
Exceptions
XmlExceptionif the XML is malformed, fails schema validation, or contains invalid scenario logic.
std::runtime_errorfor file I/O errors or other critical setup issues.

Definition at line 25 of file xml_parser.cpp.

27 {
28 world->clear();
30
32 if (!main_doc.loadFile(filename))
33 {
34 throw XmlException("Failed to load main XML file: " + filename);
35 }
36
37 const std::filesystem::path main_dir = std::filesystem::path(filename).parent_path();
38 const bool did_combine = xml_parser_utils::addIncludeFilesToMainDocument(main_doc, main_dir);
39
40 if (validate)
41 {
42 xml_parser_utils::validateXml(did_combine, main_doc);
43 }
44 else
45 {
46 LOG(logging::Level::DEBUG, "Skipping XML validation.");
47 }
48
49 xml_parser_utils::ParserContext ctx;
50 ctx.world = world;
51 ctx.master_seeder = &masterSeeder;
52 ctx.base_dir = main_dir;
53 ctx.loaders = xml_parser_utils::createDefaultAssetLoaders();
54
55 xml_parser_utils::processParsedDocument(main_doc, ctx);
56
57 // Push the isolated context parameters into global application parameters
58 params::params = ctx.parameters;
59 }
Class for managing XML documents.
Exception class for handling XML-related errors.
void clear() noexcept
Clears all objects and assets from the simulation world.
Definition world.cpp:407
@ DEBUG
Debug level for general debugging information.
Parameters params
Global simulation parameter state.
Definition parameters.h:85
void reset() noexcept
Resets the parameters to their default-constructed state.
Definition parameters.h:82

References serial::xml_parser_utils::addIncludeFilesToMainDocument(), core::World::clear(), serial::xml_parser_utils::createDefaultAssetLoaders(), logging::DEBUG, LOG, max, params::params, serial::xml_parser_utils::processParsedDocument(), params::Parameters::reset(), and serial::xml_parser_utils::validateXml().

Referenced by fers_load_scenario_from_xml_file().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parseSimulationFromString()

void serial::parseSimulationFromString ( const std::string &  xmlContent,
core::World world,
bool  validate,
std::mt19937 &  masterSeeder 
)

Parses a simulation configuration directly from an XML string in memory.

Similar to parseSimulation, but operates on a raw string instead of a file. Because it does not load from the filesystem, <include> tags are ignored, and any relative paths for file-backed assets (like waveforms or antennas) will be resolved against the current working directory (.).

Parameters
xmlContentThe raw XML string containing the scenario definition.
worldA pointer to the World object to be populated with parsed components.
validateA boolean indicating whether to perform strict XML schema validation.
masterSeederA reference to the master random number generator used for assigning independent seeds to components.
Exceptions
XmlExceptionif the XML string is malformed, fails schema validation, or contains invalid scenario logic.

Definition at line 61 of file xml_parser.cpp.

63 {
64 world->clear();
66
68 if (!doc.loadString(xmlContent))
69 {
70 throw XmlException("Failed to parse XML from memory string.");
71 }
72
73 if (validate)
74 {
75 // Note: <include> tags are not processed when loading from a string.
76 xml_parser_utils::validateXml(false, doc);
77 }
78 else
79 {
80 LOG(logging::Level::DEBUG, "Skipping XML validation.");
81 }
82
83 // When loading from a string, there's no base directory for relative asset paths.
84 // The UI/caller is responsible for ensuring any paths in the XML are absolute or resolvable.
85 const std::filesystem::path base_dir = ".";
86
87 xml_parser_utils::ParserContext ctx;
88 ctx.world = world;
89 ctx.master_seeder = &masterSeeder;
90 ctx.base_dir = base_dir;
91 ctx.loaders = xml_parser_utils::createDefaultAssetLoaders();
92
93 xml_parser_utils::processParsedDocument(doc, ctx);
94
95 // Push the isolated context parameters into global application parameters
96 params::params = ctx.parameters;
97 }

References core::World::clear(), serial::xml_parser_utils::createDefaultAssetLoaders(), logging::DEBUG, LOG, max, params::params, serial::xml_parser_utils::processParsedDocument(), params::Parameters::reset(), and serial::xml_parser_utils::validateXml().

Referenced by fers_load_scenario_from_xml_string().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ readPattern()

std::vector< std::vector< RealType > > serial::readPattern ( const std::string &  name,
const std::string &  datasetName 
)

Reads a 2D antenna gain pattern from the named dataset.

Reads a 2D pattern dataset from an HDF5 file.

Parameters
nameThe name of the HDF5 file.
datasetNameThe name of the dataset to be read.
Returns
A 2D vector containing the pattern data.
Exceptions
std::runtime_errorIf there is an error handling the file or if the dataset dimensions are invalid.

Definition at line 332 of file hdf5_handler.cpp.

333 {
334 std::scoped_lock const lock(hdf5_global_mutex);
335 try
336 {
337 LOG(Level::TRACE, "Reading dataset '{}' from file '{}'", datasetName, name);
338 const HighFive::File file(name, HighFive::File::ReadOnly);
339
340 const auto dataset = file.getDataSet(datasetName);
341
342 const auto dataspace = dataset.getSpace();
343 const auto dims = dataspace.getDimensions();
344
345 if (dims.size() != 2)
346 {
347 LOG(Level::FATAL, "Invalid dataset dimensions for '{}' in file '{}'", datasetName, name);
348 throw std::runtime_error(
349 std::format(R"(Invalid dataset dimensions for "{}" in file "{}")", datasetName, name));
350 }
351
352 LOG(Level::TRACE, "Reading dataset with dimensions {}x{}", dims[0], dims[1]);
353
354 std::vector data(dims[0], std::vector<RealType>(dims[1]));
355 dataset.read(data);
356
357 LOG(Level::TRACE, "Read dataset successfully");
358
359 return data;
360 }
361 catch (const HighFive::Exception& err)
362 {
363 LOG(Level::FATAL, "Error handling HDF5 file: {}", err.what());
364 throw std::runtime_error("Error handling HDF5 file: " + std::string(err.what()));
365 }
366 }

References hdf5_global_mutex, LOG, and max.

◆ readPulseData()

void serial::readPulseData ( const std::string &  name,
std::vector< ComplexType > &  data 
)

Reads pulse data from an HDF5 file.

Parameters
nameThe name of the HDF5 file.
dataA reference to a vector where the complex data will be stored.
Exceptions
std::runtime_errorIf the file does not exist or the datasets "I" and "Q" have mismatched sizes.

Definition at line 222 of file hdf5_handler.cpp.

223 {
224 std::scoped_lock const lock(hdf5_global_mutex);
225
226 if (!std::filesystem::exists(name))
227 {
228 LOG(Level::FATAL, "File '{}' not found", name);
229 throw std::runtime_error("File " + name + " not found.");
230 }
231
232 LOG(Level::TRACE, "Opening file '{}'", name);
233 const HighFive::File file(name, HighFive::File::ReadOnly);
234
235 // Helper lambda to open group and read dataset
236 auto read_dataset = [&file](const std::string& groupName, std::vector<double>& buffer) -> size_t
237 {
238 const auto group = file.getGroup("/" + groupName);
239
240 const auto dataset = group.getDataSet("value");
241
242 const auto dimensions = dataset.getSpace().getDimensions();
243 const auto size = dimensions[0];
244
245 buffer.resize(size);
246 dataset.read(buffer);
247
248 return size;
249 };
250
251 LOG(Level::TRACE, "Reading dataset 'I' from file '{}'", name);
252 std::vector<double> buffer_i;
253 const auto size = read_dataset("I", buffer_i);
254
255 std::vector<double> buffer_q;
256 LOG(Level::TRACE, "Reading dataset 'Q' from file '{}'", name);
257 if (read_dataset("Q", buffer_q) != size)
258 {
259 LOG(Level::FATAL, "Dataset 'Q' is not the same size as dataset 'I' in file '{}'", name);
260 throw std::runtime_error(R"(Dataset "Q" is not the same size as dataset "I" in file )" + name);
261 }
262
263 data.resize(size);
264 for (size_t i = 0; i < size; ++i)
265 {
266 data[i] = ComplexType(buffer_i[i], buffer_q[i]);
267 }
268 LOG(Level::TRACE, "Read dataset successfully");
269 }

References hdf5_global_mutex, LOG, and max.

◆ update_antenna_from_json()

void serial::update_antenna_from_json ( const nlohmann::json &  j,
antenna::Antenna ant,
core::World world 
)

Updates an antenna from JSON without full context recreation.

Definition at line 1802 of file json_serializer.cpp.

1803 {
1804 const auto new_pattern = j.value("pattern", "isotropic");
1806 {
1808 return;
1809 }
1810
1811 ant->setName(j.at("name").get<std::string>());
1812 ant->setEfficiencyFactor(j.value("efficiency", 1.0));
1813 update_existing_antenna_pattern_fields(j, ant, world);
1814 }
void replace(std::unique_ptr< radar::Target > target)
Replaces an existing target, updating internal pointers.
Definition world.cpp:281
std::unique_ptr< antenna::Antenna > parse_required_update_antenna(const nlohmann::json &j)
bool antenna_pattern_requires_replacement(const std::string_view pattern, const antenna::Antenna *ant) noexcept

References antenna_pattern_requires_replacement(), max, parse_required_update_antenna(), core::World::replace(), and update_existing_antenna_pattern_fields().

Referenced by fers_update_antenna_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_existing_antenna_pattern_fields()

void serial::update_existing_antenna_pattern_fields ( const nlohmann::json &  j,
antenna::Antenna ant,
core::World world 
)

Definition at line 1765 of file json_serializer.cpp.

1766 {
1767 if (auto* sinc = dynamic_cast<antenna::Sinc*>(ant))
1768 {
1769 sinc->setAlpha(j.value("alpha", 1.0));
1770 sinc->setBeta(j.value("beta", 1.0));
1771 sinc->setGamma(j.value("gamma", 2.0));
1772 }
1773 else if (auto* gauss = dynamic_cast<antenna::Gaussian*>(ant))
1774 {
1775 gauss->setAzimuthScale(j.value("azscale", 1.0));
1776 gauss->setElevationScale(j.value("elscale", 1.0));
1777 }
1778 else if (auto* horn = dynamic_cast<antenna::SquareHorn*>(ant))
1779 {
1780 horn->setDimension(j.value("diameter", 0.5));
1781 }
1782 else if (auto* para = dynamic_cast<antenna::Parabolic*>(ant))
1783 {
1784 para->setDiameter(j.value("diameter", 0.5));
1785 }
1786 else if (auto* xml = dynamic_cast<antenna::XmlAntenna*>(ant))
1787 {
1788 if (xml->getFilename() != j.value("filename", ""))
1789 {
1791 }
1792 }
1793 else if (auto* h5 = dynamic_cast<antenna::H5Antenna*>(ant))
1794 {
1795 if (h5->getFilename() != j.value("filename", ""))
1796 {
1798 }
1799 }
1800 }

References max, parse_required_update_antenna(), and core::World::replace().

Referenced by update_antenna_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_monostatic_from_json()

void serial::update_monostatic_from_json ( const nlohmann::json &  j,
radar::Transmitter tx,
radar::Receiver rx,
core::World world,
std::mt19937 &  masterSeeder 
)

Updates a monostatic radar from JSON without full context recreation.

Definition at line 2180 of file json_serializer.cpp.

2182 {
2185
2189 validate_transmitter_signal_state(*tx, "Monostatic '" + tx->getName() + "'");
2190 if (j.contains("fmcw_mode"))
2191 {
2192 parse_receiver_dechirp_config(j, *rx, "Monostatic '" + tx->getName() + "'");
2193 }
2195 }
void resolveReceiverDechirpReferences()
Resolves and validates receiver FMCW dechirp references after all components are loaded.
Definition world.cpp:552
void update_monostatic_receiver_basics(const nlohmann::json &j, const radar::Transmitter &tx, radar::Receiver &rx, core::World &world)
void update_monostatic_schedule_from_json(const nlohmann::json &j, radar::Transmitter &tx, radar::Receiver &rx)
void update_transmitter_from_json(const nlohmann::json &j, radar::Transmitter *tx, core::World &world, std::mt19937 &)
Updates a transmitter from JSON without full context recreation.
void update_monostatic_timing_from_json(const nlohmann::json &j, radar::Transmitter &tx, radar::Receiver &rx, core::World &world)
void validate_transmitter_signal_state(const radar::Transmitter &tx, const std::string &owner)
nlohmann::json monostatic_transmitter_json(const nlohmann::json &j)

References max, monostatic_transmitter_json(), core::World::resolveReceiverDechirpReferences(), update_monostatic_receiver_basics(), update_monostatic_schedule_from_json(), update_monostatic_timing_from_json(), update_transmitter_from_json(), and validate_transmitter_signal_state().

Referenced by fers_update_monostatic_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_monostatic_receiver_basics()

void serial::update_monostatic_receiver_basics ( const nlohmann::json &  j,
const radar::Transmitter tx,
radar::Receiver rx,
core::World world 
)

Definition at line 2123 of file json_serializer.cpp.

2125 {
2126 if (j.contains("name"))
2127 rx.setName(j.at("name").get<std::string>());
2128 rx.setMode(tx.getMode());
2129 if (rx.getMode() == radar::OperationMode::PULSED_MODE && j.contains("pulsed_mode"))
2130 {
2131 const auto& mode_json = j.at("pulsed_mode");
2132 rx.setWindowProperties(mode_json.value("window_length", 0.0), tx.getPrf(),
2133 mode_json.value("window_skip", 0.0));
2134 }
2135 update_receiver_noise_and_flags_from_json(j, rx);
2136 if (j.contains("antenna"))
2137 {
2138 rx.setAntenna(world.findAntenna(parse_json_id(j, "antenna", "Monostatic")));
2139 }
2140 }
antenna::Antenna * findAntenna(const SimId id)
Finds an antenna by ID.
Definition world.cpp:159
@ PULSED_MODE
The component operates in a pulsed mode.

References core::World::findAntenna(), max, radar::PULSED_MODE, and update_receiver_noise_and_flags_from_json().

Referenced by update_monostatic_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_monostatic_schedule_from_json()

void serial::update_monostatic_schedule_from_json ( const nlohmann::json &  j,
radar::Transmitter tx,
radar::Receiver rx 
)

Definition at line 2162 of file json_serializer.cpp.

2163 {
2164 if (!j.contains("schedule"))
2165 {
2166 return;
2167 }
2168 auto raw = j.at("schedule").get<std::vector<radar::SchedulePeriod>>();
2169 const bool pulsed = tx.getMode() == radar::OperationMode::PULSED_MODE;
2170 const RealType pri = pulsed ? 1.0 / tx.getPrf() : 0.0;
2171 auto processed_schedule = radar::processRawSchedule(raw, tx.getName(), pulsed, pri);
2172 if (tx.getSignal() != nullptr && tx.getSignal()->isFmcwFamily())
2173 {
2174 validate_fmcw_schedule(processed_schedule, *tx.getSignal(), "Monostatic '" + tx.getName() + "'");
2175 }
2176 tx.setSchedule(processed_schedule);
2177 rx.setSchedule(processed_schedule);
2178 }
std::vector< SchedulePeriod > processRawSchedule(const std::vector< SchedulePeriod > &periods, const std::string &ownerName, const bool isPulsed, const RealType pri)
Processes a raw list of schedule periods.

References max, radar::processRawSchedule(), and radar::PULSED_MODE.

Referenced by update_monostatic_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_monostatic_timing_from_json()

void serial::update_monostatic_timing_from_json ( const nlohmann::json &  j,
radar::Transmitter tx,
radar::Receiver rx,
core::World world 
)

Definition at line 2142 of file json_serializer.cpp.

2144 {
2145 if (!j.contains("timing"))
2146 {
2147 return;
2148 }
2149 auto timing_id = parse_json_id(j, "timing", "Monostatic");
2150 auto* const timing_proto = world.findTiming(timing_id);
2151 if (timing_proto == nullptr)
2152 {
2153 throw std::runtime_error("Timing ID " + std::to_string(timing_id) + " not found.");
2154 }
2155 unsigned const seed = rx.getTiming() ? rx.getTiming()->getSeed() : 0;
2156 auto shared_timing = std::make_shared<timing::Timing>(timing_proto->getName(), seed, timing_proto->getId());
2157 shared_timing->initializeModel(timing_proto);
2158 tx.setTiming(shared_timing);
2159 rx.setTiming(shared_timing);
2160 }
timing::PrototypeTiming * findTiming(const SimId id)
Finds a timing source by ID.
Definition world.cpp:165

References core::World::findTiming(), and max.

Referenced by update_monostatic_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_parameters_from_json()

void serial::update_parameters_from_json ( const nlohmann::json &  j,
std::mt19937 &  masterSeeder 
)

Updates global simulation parameters from JSON.

Definition at line 1712 of file json_serializer.cpp.

1713 {
1714 nlohmann::json sim;
1715 sim["parameters"] = j;
1717 }

References max.

Referenced by fers_update_parameters_from_json().

+ Here is the caller graph for this function:

◆ update_platform_paths_from_json()

void serial::update_platform_paths_from_json ( const nlohmann::json &  j,
radar::Platform plat 
)

Updates a platform's motion and rotation paths from JSON.

Definition at line 1816 of file json_serializer.cpp.

1817 {
1818 if (j.contains("motionpath"))
1819 {
1820 auto path = std::make_unique<math::Path>();
1821 j.at("motionpath").get_to(*path);
1822 plat->setMotionPath(std::move(path));
1823 }
1824 if (j.contains("rotationpath"))
1825 {
1826 auto rot_path = std::make_unique<math::RotationPath>();
1827 const auto& rotation_json = j.at("rotationpath");
1828 rot_path->setInterp(rotation_json.at("interpolation").get<math::RotationPath::InterpType>());
1829 unsigned waypoint_index = 0;
1830 for (const auto& waypoint_json : rotation_json.at("rotationwaypoints"))
1831 {
1832 const RealType azimuth = waypoint_json.at("azimuth").get<RealType>();
1833 const RealType elevation = waypoint_json.at("elevation").get<RealType>();
1834 const RealType time = waypoint_json.at("time").get<RealType>();
1835 const std::string owner =
1836 std::format("platform '{}' rotation waypoint {}", plat->getName(), waypoint_index);
1837
1838 rotation_warning_utils::maybe_warn_about_rotation_value(azimuth, params::rotationAngleUnit(),
1839 rotation_warning_utils::ValueKind::Angle,
1840 "JSON", owner, "azimuth");
1841 rotation_warning_utils::maybe_warn_about_rotation_value(elevation, params::rotationAngleUnit(),
1842 rotation_warning_utils::ValueKind::Angle,
1843 "JSON", owner, "elevation");
1844
1845 rot_path->addCoord(rotation_angle_utils::external_rotation_to_internal(azimuth, elevation, time,
1848 }
1849 rot_path->finalize();
1850 plat->setRotationPath(std::move(rot_path));
1851 }
1852 else if (j.contains("fixedrotation"))
1853 {
1854 auto rot_path = std::make_unique<math::RotationPath>();
1855 const auto& fixed_json = j.at("fixedrotation");
1856 const RealType start_az_deg = fixed_json.at("startazimuth").get<RealType>();
1857 const RealType start_el_deg = fixed_json.at("startelevation").get<RealType>();
1858 const RealType rate_az_deg_s = fixed_json.at("azimuthrate").get<RealType>();
1859 const RealType rate_el_deg_s = fixed_json.at("elevationrate").get<RealType>();
1860 const std::string owner = std::format("platform '{}' fixedrotation", plat->getName());
1861
1862 rotation_warning_utils::maybe_warn_about_rotation_value(start_az_deg, params::rotationAngleUnit(),
1863 rotation_warning_utils::ValueKind::Angle, "JSON",
1864 owner, "startazimuth");
1865 rotation_warning_utils::maybe_warn_about_rotation_value(start_el_deg, params::rotationAngleUnit(),
1866 rotation_warning_utils::ValueKind::Angle, "JSON",
1867 owner, "startelevation");
1868 rotation_warning_utils::maybe_warn_about_rotation_value(rate_az_deg_s, params::rotationAngleUnit(),
1869 rotation_warning_utils::ValueKind::Rate, "JSON",
1870 owner, "azimuthrate");
1871 rotation_warning_utils::maybe_warn_about_rotation_value(rate_el_deg_s, params::rotationAngleUnit(),
1872 rotation_warning_utils::ValueKind::Rate, "JSON",
1873 owner, "elevationrate");
1874
1879 rot_path->setConstantRate(start, rate);
1880 rot_path->finalize();
1881 plat->setRotationPath(std::move(rot_path));
1882 }
1883 }
InterpType
Enumeration for types of interpolation.
RotationAngleUnit rotationAngleUnit() noexcept
Gets the external rotation angle unit.
Definition parameters.h:327
math::RotationCoord external_rotation_to_internal(const RealType azimuth, const RealType elevation, const RealType time, const params::RotationAngleUnit unit) noexcept
Converts external compass azimuth/elevation into internal rotation coordinates.
math::RotationCoord external_rotation_rate_to_internal(const RealType azimuth_rate, const RealType elevation_rate, const RealType time, const params::RotationAngleUnit unit) noexcept
Converts external compass azimuth/elevation rates into internal rotation rates.

References serial::rotation_warning_utils::Angle, serial::rotation_angle_utils::external_rotation_rate_to_internal(), serial::rotation_angle_utils::external_rotation_to_internal(), max, serial::rotation_warning_utils::maybe_warn_about_rotation_value(), serial::rotation_warning_utils::Rate, and params::rotationAngleUnit().

Referenced by fers_update_platform_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_receiver_antenna_from_json()

void serial::update_receiver_antenna_from_json ( const nlohmann::json &  j,
radar::Receiver rx,
core::World world 
)

Definition at line 2046 of file json_serializer.cpp.

2047 {
2048 if (!j.contains("antenna"))
2049 {
2050 return;
2051 }
2052 auto id = parse_json_id(j, "antenna", "Receiver");
2053 auto* ant = world.findAntenna(id);
2054 if (ant == nullptr)
2055 {
2056 throw std::runtime_error("Antenna ID " + std::to_string(id) + " not found.");
2057 }
2058 rx.setAntenna(ant);
2059 }

References core::World::findAntenna(), and max.

Referenced by update_receiver_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_receiver_from_json()

void serial::update_receiver_from_json ( const nlohmann::json &  j,
radar::Receiver rx,
core::World world,
std::mt19937 &   
)

Updates a receiver from JSON without full context recreation.

Definition at line 2091 of file json_serializer.cpp.

2093 {
2094 if (j.contains("name"))
2095 rx->setName(j.at("name").get<std::string>());
2096
2102 if (j.contains("fmcw_mode"))
2103 {
2104 parse_receiver_dechirp_config(j, *rx, "Receiver '" + rx->getName() + "'");
2105 }
2107 }
void update_receiver_mode_from_json(const nlohmann::json &j, radar::Receiver &rx)
void update_receiver_schedule_from_json(const nlohmann::json &j, radar::Receiver &rx)
void update_receiver_noise_and_flags_from_json(const nlohmann::json &j, radar::Receiver &rx)
void update_receiver_timing_from_json(const nlohmann::json &j, radar::Receiver &rx, core::World &world)
void update_receiver_antenna_from_json(const nlohmann::json &j, radar::Receiver &rx, core::World &world)

References max, core::World::resolveReceiverDechirpReferences(), update_receiver_antenna_from_json(), update_receiver_mode_from_json(), update_receiver_noise_and_flags_from_json(), update_receiver_schedule_from_json(), and update_receiver_timing_from_json().

Referenced by fers_update_receiver_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_receiver_mode_from_json()

void serial::update_receiver_mode_from_json ( const nlohmann::json &  j,
radar::Receiver rx 
)

Definition at line 2005 of file json_serializer.cpp.

2006 {
2007 reject_conflicting_mode_blocks(j, "Receiver '" + rx.getName() + "'");
2008 if (j.contains("pulsed_mode"))
2009 {
2011 const auto& mode_json = j.at("pulsed_mode");
2012 rx.setWindowProperties(mode_json.value("window_length", 0.0), mode_json.value("prf", 0.0),
2013 mode_json.value("window_skip", 0.0));
2014 }
2015 else if (j.contains("fmcw_mode"))
2016 {
2018 }
2019 else if (j.contains("cw_mode"))
2020 {
2022 }
2023 }
@ CW_MODE
The component operates in a continuous-wave mode.
@ FMCW_MODE
The component operates in an FMCW streaming mode.

References radar::CW_MODE, radar::FMCW_MODE, max, and radar::PULSED_MODE.

Referenced by update_receiver_from_json().

+ Here is the caller graph for this function:

◆ update_receiver_noise_and_flags_from_json()

void serial::update_receiver_noise_and_flags_from_json ( const nlohmann::json &  j,
radar::Receiver rx 
)

Definition at line 2025 of file json_serializer.cpp.

2026 {
2027 if (j.contains("noise_temp"))
2028 rx.setNoiseTemperature(j.value("noise_temp", 0.0));
2029
2030 if (j.contains("nodirect"))
2031 {
2032 if (j.value("nodirect", false))
2034 else
2036 }
2037 if (j.contains("nopropagationloss"))
2038 {
2039 if (j.value("nopropagationloss", false))
2041 else
2043 }
2044 }
@ FLAG_NODIRECT
Disable direct-path reception.
@ FLAG_NOPROPLOSS
Disable propagation-loss scaling.

References radar::Receiver::FLAG_NODIRECT, radar::Receiver::FLAG_NOPROPLOSS, and max.

Referenced by update_monostatic_receiver_basics(), and update_receiver_from_json().

+ Here is the caller graph for this function:

◆ update_receiver_schedule_from_json()

void serial::update_receiver_schedule_from_json ( const nlohmann::json &  j,
radar::Receiver rx 
)

Definition at line 2079 of file json_serializer.cpp.

2080 {
2081 if (!j.contains("schedule"))
2082 {
2083 return;
2084 }
2085 auto raw = j.at("schedule").get<std::vector<radar::SchedulePeriod>>();
2086 const bool pulsed = rx.getMode() == radar::OperationMode::PULSED_MODE;
2087 const RealType pri = pulsed ? 1.0 / rx.getWindowPrf() : 0.0;
2088 rx.setSchedule(radar::processRawSchedule(raw, rx.getName(), pulsed, pri));
2089 }

References max, radar::processRawSchedule(), and radar::PULSED_MODE.

Referenced by update_receiver_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_receiver_timing_from_json()

void serial::update_receiver_timing_from_json ( const nlohmann::json &  j,
radar::Receiver rx,
core::World world 
)

Definition at line 2061 of file json_serializer.cpp.

2062 {
2063 if (!j.contains("timing"))
2064 {
2065 return;
2066 }
2067 auto timing_id = parse_json_id(j, "timing", "Receiver");
2068 auto* const timing_proto = world.findTiming(timing_id);
2069 if (timing_proto == nullptr)
2070 {
2071 throw std::runtime_error("Timing ID " + std::to_string(timing_id) + " not found.");
2072 }
2073 unsigned const seed = rx.getTiming() ? rx.getTiming()->getSeed() : 0;
2074 auto timing = std::make_shared<timing::Timing>(timing_proto->getName(), seed, timing_proto->getId());
2075 timing->initializeModel(timing_proto);
2076 rx.setTiming(timing);
2077 }

References core::World::findTiming(), and max.

Referenced by update_receiver_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_target_from_json()

void serial::update_target_from_json ( const nlohmann::json &  j,
radar::Target existing_tgt,
core::World world,
std::mt19937 &   
)

Updates a target from JSON without full context recreation.

Definition at line 2197 of file json_serializer.cpp.

2199 {
2200 auto* plat = existing_tgt->getPlatform();
2201 const auto& rcs_json = j.at("rcs");
2202 const auto rcs_type = rcs_json.at("type").get<std::string>();
2203 std::unique_ptr<radar::Target> target_obj;
2204
2205 const auto target_id = existing_tgt->getId();
2206 const auto name = j.value("name", existing_tgt->getName());
2207 unsigned const seed = existing_tgt->getSeed();
2208
2209 if (rcs_type == "isotropic")
2210 {
2211 target_obj = radar::createIsoTarget(plat, name, rcs_json.value("value", 1.0), seed, target_id);
2212 }
2213 else if (rcs_type == "file")
2214 {
2215 const auto filename = rcs_json.value("filename", "");
2217 }
2218 else
2219 {
2220 throw std::runtime_error("Unsupported target RCS type: " + rcs_type);
2221 }
2222
2223 if (j.contains("model"))
2224 {
2225 const auto& model_json = j.at("model");
2226 const auto model_type = model_json.at("type").get<std::string>();
2227 if (model_type == "chisquare" || model_type == "gamma")
2228 {
2229 auto model =
2230 std::make_unique<radar::RcsChiSquare>(target_obj->getRngEngine(), model_json.value("k", 1.0));
2231 target_obj->setFluctuationModel(std::move(model));
2232 }
2233 else if (model_type == "constant")
2234 {
2235 target_obj->setFluctuationModel(std::make_unique<radar::RcsConst>());
2236 }
2237 }
2238
2239 world.replace(std::move(target_obj));
2240 }
std::unique_ptr< Target > createIsoTarget(Platform *platform, std::string name, RealType rcs, unsigned seed, const SimId id=0)
Creates an isotropic target.
Definition target.h:282
std::unique_ptr< Target > createFileTarget(Platform *platform, std::string name, const std::string &filename, unsigned seed, const SimId id=0)
Creates a file-based target.
Definition target.h:297

References radar::createFileTarget(), radar::createIsoTarget(), max, and core::World::replace().

Referenced by fers_update_target_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_timing_from_json()

void serial::update_timing_from_json ( const nlohmann::json &  j,
core::World world,
const SimId  id 
)

Updates a timing source from JSON without full context recreation.

Definition at line 2242 of file json_serializer.cpp.

2243 {
2244 auto* existing = world.findTiming(id);
2245 if (existing == nullptr)
2246 {
2247 throw std::runtime_error("Timing ID " + std::to_string(id) + " not found.");
2248 }
2249
2250 auto patched = j;
2251 if (!patched.contains("name"))
2252 {
2253 patched["name"] = existing->getName();
2254 }
2255
2257 }
std::unique_ptr< timing::PrototypeTiming > parse_timing_from_json(const nlohmann::json &j, const SimId id)
Parses a timing prototype from JSON.

References core::World::findTiming(), max, parse_timing_from_json(), and core::World::replace().

Referenced by fers_update_timing_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_transmitter_antenna_from_json()

void serial::update_transmitter_antenna_from_json ( const nlohmann::json &  j,
radar::Transmitter tx,
core::World world 
)

Definition at line 1925 of file json_serializer.cpp.

1926 {
1927 if (!j.contains("antenna"))
1928 {
1929 return;
1930 }
1931 auto id = parse_json_id(j, "antenna", "Transmitter");
1932 auto* ant = world.findAntenna(id);
1933 if (ant == nullptr)
1934 {
1935 throw std::runtime_error("Antenna ID " + std::to_string(id) + " not found.");
1936 }
1937 tx.setAntenna(ant);
1938 }

References core::World::findAntenna(), and max.

Referenced by update_transmitter_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_transmitter_from_json()

void serial::update_transmitter_from_json ( const nlohmann::json &  j,
radar::Transmitter tx,
core::World world,
std::mt19937 &   
)

Updates a transmitter from JSON without full context recreation.

Definition at line 1990 of file json_serializer.cpp.

1992 {
1993 if (j.contains("name"))
1994 tx->setName(j.at("name").get<std::string>());
1995
1996 const std::string owner = "Transmitter '" + tx->getName() + "'";
2003 }
void update_transmitter_waveform_from_json(const nlohmann::json &j, radar::Transmitter &tx, core::World &world)
void update_transmitter_schedule_from_json(const nlohmann::json &j, radar::Transmitter &tx, const std::string &owner)
void update_transmitter_timing_from_json(const nlohmann::json &j, radar::Transmitter &tx, core::World &world)
void update_transmitter_mode_from_json(const nlohmann::json &j, radar::Transmitter &tx)
void update_transmitter_antenna_from_json(const nlohmann::json &j, radar::Transmitter &tx, core::World &world)

References max, update_transmitter_antenna_from_json(), update_transmitter_mode_from_json(), update_transmitter_schedule_from_json(), update_transmitter_timing_from_json(), update_transmitter_waveform_from_json(), and validate_transmitter_signal_state().

Referenced by fers_update_transmitter_from_json(), and update_monostatic_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_transmitter_mode_from_json()

void serial::update_transmitter_mode_from_json ( const nlohmann::json &  j,
radar::Transmitter tx 
)

Definition at line 1885 of file json_serializer.cpp.

1886 {
1887 reject_conflicting_mode_blocks(j, "Transmitter '" + tx.getName() + "'");
1888 if (j.contains("pulsed_mode"))
1889 {
1891 tx.setPrf(j.at("pulsed_mode").value("prf", 0.0));
1892 }
1893 else if (j.contains("fmcw_mode"))
1894 {
1895 if (has_dechirp_fields(j.at("fmcw_mode")))
1896 {
1897 throw std::runtime_error("Transmitter '" + tx.getName() +
1898 "' fmcw_mode must not contain dechirp configuration.");
1899 }
1901 }
1902 else if (j.contains("cw_mode"))
1903 {
1905 }
1906 }

References radar::CW_MODE, radar::FMCW_MODE, max, and radar::PULSED_MODE.

Referenced by update_transmitter_from_json().

+ Here is the caller graph for this function:

◆ update_transmitter_schedule_from_json()

void serial::update_transmitter_schedule_from_json ( const nlohmann::json &  j,
radar::Transmitter tx,
const std::string &  owner 
)

Definition at line 1972 of file json_serializer.cpp.

1974 {
1975 if (!j.contains("schedule"))
1976 {
1977 return;
1978 }
1979 auto raw = j.at("schedule").get<std::vector<radar::SchedulePeriod>>();
1980 const bool pulsed = tx.getMode() == radar::OperationMode::PULSED_MODE;
1981 const RealType pri = pulsed ? 1.0 / tx.getPrf() : 0.0;
1982 auto schedule = radar::processRawSchedule(raw, tx.getName(), pulsed, pri);
1983 if (tx.getSignal() != nullptr && tx.getSignal()->isFmcwFamily())
1984 {
1985 validate_fmcw_schedule(schedule, *tx.getSignal(), owner);
1986 }
1987 tx.setSchedule(std::move(schedule));
1988 }

References max, radar::processRawSchedule(), and radar::PULSED_MODE.

Referenced by update_transmitter_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_transmitter_timing_from_json()

void serial::update_transmitter_timing_from_json ( const nlohmann::json &  j,
radar::Transmitter tx,
core::World world 
)

Definition at line 1940 of file json_serializer.cpp.

1941 {
1942 if (!j.contains("timing"))
1943 {
1944 return;
1945 }
1946 auto timing_id = parse_json_id(j, "timing", "Transmitter");
1947 auto* const timing_proto = world.findTiming(timing_id);
1948 if (timing_proto == nullptr)
1949 {
1950 throw std::runtime_error("Timing ID " + std::to_string(timing_id) + " not found.");
1951 }
1952 unsigned const seed = tx.getTiming() ? tx.getTiming()->getSeed() : 0;
1953 auto timing = std::make_shared<timing::Timing>(timing_proto->getName(), seed, timing_proto->getId());
1954 timing->initializeModel(timing_proto);
1955 tx.setTiming(timing);
1956 }

References core::World::findTiming(), and max.

Referenced by update_transmitter_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_transmitter_waveform_from_json()

void serial::update_transmitter_waveform_from_json ( const nlohmann::json &  j,
radar::Transmitter tx,
core::World world 
)

Definition at line 1908 of file json_serializer.cpp.

1909 {
1910 if (!j.contains("waveform"))
1911 {
1912 return;
1913 }
1914 auto id = parse_json_id(j, "waveform", "Transmitter");
1915 auto* wf = world.findWaveform(id);
1916 if (wf == nullptr)
1917 {
1918 throw std::runtime_error("Waveform ID " + std::to_string(id) + " not found.");
1919 }
1920 validate_fmcw_waveform(*wf, "Waveform '" + wf->getName() + "'");
1921 validate_waveform_mode_match(*wf, tx.getMode(), "Transmitter '" + tx.getName() + "'");
1922 tx.setWave(wf);
1923 }
fers_signal::RadarSignal * findWaveform(const SimId id)
Finds a radar signal by ID.
Definition world.cpp:153

References core::World::findWaveform(), and max.

Referenced by update_transmitter_from_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ validate_transmitter_signal_state()

void serial::validate_transmitter_signal_state ( const radar::Transmitter tx,
const std::string &  owner 
)

Definition at line 1958 of file json_serializer.cpp.

1959 {
1960 if (tx.getSignal() == nullptr)
1961 {
1962 return;
1963 }
1964 validate_fmcw_waveform(*tx.getSignal(), "Waveform '" + tx.getSignal()->getName() + "'");
1965 validate_waveform_mode_match(*tx.getSignal(), tx.getMode(), owner);
1966 if (tx.getSignal()->isFmcwFamily())
1967 {
1968 validate_fmcw_schedule(tx.getSchedule(), *tx.getSignal(), owner);
1969 }
1970 }

References max.

Referenced by update_monostatic_from_json(), and update_transmitter_from_json().

+ Here is the caller graph for this function:

◆ world_to_json()

nlohmann::json serial::world_to_json ( const core::World world)

Serializes the entire simulation world into a nlohmann::json object.

This function traverses the core::World object model and constructs a JSON representation. It is designed to produce a format that is convenient for the frontend to consume. This involves translating internal data formats (e.g., angles in radians) to a more UI-friendly format (e.g., compass degrees) and restructuring complex object relationships (like monostatic radars) into simpler representations.

Parameters
worldThe world object to serialize.
Returns
A nlohmann::json object representing the world.

Definition at line 2259 of file json_serializer.cpp.

2260 {
2261 nlohmann::json sim_json;
2262
2264 sim_json["parameters"] = params::params;
2265
2266 sim_json["waveforms"] = nlohmann::json::array();
2267 for (const auto& waveform : world.getWaveforms() | std::views::values)
2268 {
2269 sim_json["waveforms"].push_back(*waveform);
2270 }
2271
2272 sim_json["antennas"] = nlohmann::json::array();
2273 for (const auto& antenna : world.getAntennas() | std::views::values)
2274 {
2275 sim_json["antennas"].push_back(*antenna);
2276 }
2277
2278 sim_json["timings"] = nlohmann::json::array();
2279 for (const auto& timing : world.getTimings() | std::views::values)
2280 {
2281 sim_json["timings"].push_back(*timing);
2282 }
2283
2284 sim_json["platforms"] = nlohmann::json::array();
2285 for (const auto& p : world.getPlatforms())
2286 {
2287 sim_json["platforms"].push_back(serialize_platform(p.get(), world));
2288 }
2289
2290 return {{"simulation", sim_json}};
2291 }
std::string simulation_name
The name of the simulation, from the XML.
Definition parameters.h:74

References core::World::getAntennas(), core::World::getPlatforms(), core::World::getTimings(), core::World::getWaveforms(), max, params::params, and params::Parameters::simulation_name.

Referenced by fers_get_scenario_as_json().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ world_to_xml_string()

std::string serial::world_to_xml_string ( const core::World world)

Serializes the entire simulation world into an XML formatted string.

This function serves as the reverse of the XML parser. It is essential for allowing users to modify a scenario in a UI and then export their changes back into a valid FERS XML file that can be used by the CLI or shared. It iterates through the in-memory core::World object and reconstructs the corresponding XML structure.

Parameters
worldThe world object to serialize.
Returns
A string containing the XML representation of the world.

Definition at line 26 of file xml_serializer.cpp.

27 {
28 XmlDocument const doc;
29 XmlElement const root = XmlElement::create("simulation");
30 doc.setRootElement(root);
31
32 const auto& p = params::params;
33
34 if (!p.simulation_name.empty())
35 {
36 root.setAttribute("name", p.simulation_name);
37 }
38 else
39 {
40 root.setAttribute("name", "FERS Scenario");
41 }
42
43 const XmlElement params_elem = root.addChild("parameters");
44 xml_serializer_utils::serializeParameters(params_elem, p);
45
46 for (const auto& waveform : world.getWaveforms() | std::views::values)
47 {
48 XmlElement const waveform_elem = root.addChild("waveform");
49 xml_serializer_utils::serializeWaveform(*waveform, waveform_elem);
50 }
51 for (const auto& timing : world.getTimings() | std::views::values)
52 {
53 XmlElement const timing_elem = root.addChild("timing");
54 xml_serializer_utils::serializeTiming(*timing, timing_elem);
55 }
56 for (const auto& antenna : world.getAntennas() | std::views::values)
57 {
58 XmlElement const antenna_elem = root.addChild("antenna");
59 xml_serializer_utils::serializeAntenna(*antenna, antenna_elem);
60 }
61 for (const auto& platform : world.getPlatforms())
62 {
63 XmlElement const plat_elem = root.addChild("platform");
64 xml_serializer_utils::serializePlatform(*platform, world, plat_elem);
65 }
66
67 return doc.dumpToString();
68 }
Class representing a node in an XML document.
static XmlElement create(const std::string_view name)
Create a new XML element by name.

References XmlElement::create(), core::World::getAntennas(), core::World::getPlatforms(), core::World::getTimings(), core::World::getWaveforms(), max, params::params, serial::xml_serializer_utils::serializeAntenna(), serial::xml_serializer_utils::serializeParameters(), serial::xml_serializer_utils::serializePlatform(), serial::xml_serializer_utils::serializeTiming(), and serial::xml_serializer_utils::serializeWaveform().

Referenced by fers_get_scenario_as_xml().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ writeOutputFileMetadataAttributes()

void serial::writeOutputFileMetadataAttributes ( HighFive::File &  file,
const core::OutputFileMetadata metadata 
)

Writes additive FERS output metadata attributes to an open HDF5 file.

The caller must hold hdf5_global_mutex.

Definition at line 214 of file hdf5_handler.cpp.

215 {
217 writeFmcwIfAttributes(file, metadata);
219 writeFmcwAttributes(file, metadata);
220 }

References max.

Referenced by serial::Hdf5OutputSink::Impl::closePulsedStream(), and processing::pipeline::exportStreamingToHdf5().

+ Here is the caller graph for this function:

Variable Documentation

◆ hdf5_global_mutex

std::mutex serial::hdf5_global_mutex

Global mutex to protect all HDF5 C-library calls, which are not thread-safe.

Definition at line 31 of file hdf5_handler.cpp.

Referenced by addChunkToFile(), serial::Hdf5OutputSink::Impl::closePulsedStream(), processing::pipeline::exportStreamingToHdf5(), serial::Hdf5OutputSink::Impl::openStream(), readPattern(), and readPulseData().