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, const RealType power, const RealType carrierFreq, const SimId id, const FileWaveformKind kind)
 
std::unique_ptr< fers_signal::RadarSignalloadWaveformFromFile (const std::string &name, const std::string &filename, RealType power, RealType carrierFreq, const SimId id=0, fers_signal::FileWaveformKind kind=fers_signal::FileWaveformKind::Pulsed)
 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 368 of file hdf5_handler.cpp.

370 {
371 std::scoped_lock const lock(hdf5_global_mutex);
372
373 const std::size_t size = data.size();
374
375 const std::string base_chunk_name = "chunk_" + std::format("{:06}", count);
376 const std::string i_chunk_name = base_chunk_name + "_I";
377 const std::string q_chunk_name = base_chunk_name + "_Q";
378
379 std::vector<RealType> i(size), q(size);
380 std::ranges::transform(data, i.begin(), [](const ComplexType& c) { return c.real(); });
381 std::ranges::transform(data, q.begin(), [](const ComplexType& c) { return c.imag(); });
382
383 auto write_chunk = [&](const std::string& chunkName, const std::vector<RealType>& chunkData)
384 {
385 try
386 {
387 HighFive::DataSet dataset =
388 file.createDataSet<RealType>(chunkName, HighFive::DataSpace::From(chunkData));
389 dataset.write(chunkData);
390 }
391 catch (const HighFive::Exception& err)
392 {
393 LOG(Level::FATAL, "Error while writing data to HDF5 file: {}", err.what());
394 throw std::runtime_error("Error while writing data to HDF5 file: " + chunkName + " - " + err.what());
395 }
396 };
397
398 auto set_chunk_attributes = [&](const std::string& chunkName)
399 {
400 try
401 {
402 HighFive::DataSet dataset = file.getDataSet(chunkName);
403 dataset.createAttribute("time", time);
404 dataset.createAttribute("rate", params::rate());
405 dataset.createAttribute("fullscale", fullscale);
406 if (metadata != nullptr)
407 {
408 dataset.createAttribute("chunk_index", metadata->chunk_index);
409 dataset.createAttribute("sample_count", static_cast<unsigned long long>(metadata->sample_count));
410 dataset.createAttribute("sample_start", static_cast<unsigned long long>(metadata->sample_start));
411 dataset.createAttribute("sample_end_exclusive",
412 static_cast<unsigned long long>(metadata->sample_end_exclusive));
413 }
414 }
415 catch (const HighFive::Exception& err)
416 {
417 LOG(Level::FATAL, "Error while setting attributes on chunk: {}", err.what());
418 throw std::runtime_error("Error while setting attributes on chunk: " + chunkName + " - " + err.what());
419 }
420 };
421
424
427 }
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 1843 of file json_serializer.cpp.

1844 {
1845 if (pattern == "isotropic")
1846 {
1847 return dynamic_cast<const antenna::Isotropic*>(ant) == nullptr;
1848 }
1849 if (pattern == "sinc")
1850 {
1851 return dynamic_cast<const antenna::Sinc*>(ant) == nullptr;
1852 }
1853 if (pattern == "gaussian")
1854 {
1855 return dynamic_cast<const antenna::Gaussian*>(ant) == nullptr;
1856 }
1857 if (pattern == "squarehorn")
1858 {
1859 return dynamic_cast<const antenna::SquareHorn*>(ant) == nullptr;
1860 }
1861 if (pattern == "parabolic")
1862 {
1863 return dynamic_cast<const antenna::Parabolic*>(ant) == nullptr;
1864 }
1865 if (pattern == "xml")
1866 {
1867 return dynamic_cast<const antenna::XmlAntenna*>(ant) == nullptr;
1868 }
1869 if (pattern == "file")
1870 {
1871 return dynamic_cast<const antenna::H5Antenna*>(ant) == nullptr;
1872 }
1873 return false;
1874 }
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 2414 of file json_serializer.cpp.

2415 {
2418 world.swap(parsed_world);
2419 }
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:423

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() [1/2]

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

Definition at line 167 of file waveform_factory.cpp.

170 {
171 const std::filesystem::path filepath = filename;
172 const auto extension = filepath.extension().string();
173 if (kind != FileWaveformKind::Pulsed && !hasExtension(extension, ".h5"))
174 {
175 throw std::runtime_error("File-backed CW and FMCW waveforms require the pulsed-compatible HDF5 (.h5) "
176 "format: " +
177 filename);
178 }
179
180 if (hasExtension(extension, ".csv"))
181 {
182 auto wave = loadWaveformFromCsvFile(name, filepath, power, carrierFreq, id, kind);
183 wave->setFilename(filename);
184 return wave;
185 }
186 if (hasExtension(extension, ".h5"))
187 {
188 auto wave = loadWaveformFromHdf5File(name, filepath, power, carrierFreq, id, kind);
189 wave->setFilename(filename);
190 return wave;
191 }
192
193 LOG(logging::Level::FATAL, "Unrecognized file extension '{}' for file: '{}'", extension, filename);
194 throw std::runtime_error("Unrecognized file extension '" + extension + "' for file: " + filename);
195 }
@ 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:

◆ loadWaveformFromFile() [2/2]

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

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.
idOptional explicit waveform identifier.
kindRadar mode assigned to the loaded samples.
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.

◆ 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 2230 of file json_serializer.cpp.

2231 {
2232 auto transmitter_json = j;
2233 if (transmitter_json.contains("fmcw_mode") && transmitter_json.at("fmcw_mode").is_object())
2234 {
2235 transmitter_json["fmcw_mode"].erase("dechirp_mode");
2236 transmitter_json["fmcw_mode"].erase("dechirp_reference");
2237 transmitter_json["fmcw_mode"].erase("if_sample_rate");
2238 transmitter_json["fmcw_mode"].erase("if_filter_bandwidth");
2239 transmitter_json["fmcw_mode"].erase("if_filter_transition_width");
2240 }
2241 return transmitter_json;
2242 }

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 1802 of file json_serializer.cpp.

1803 {
1804 std::unique_ptr<antenna::Antenna> ant;
1806 return ant;
1807 }
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 1830 of file json_serializer.cpp.

1831 {
1833 if (parsed == nullptr)
1834 {
1835 const auto name = j.value("name", std::string{});
1836 const auto pattern = j.value("pattern", "isotropic");
1837 throw std::runtime_error("Cannot update antenna '" + name + "' to pattern '" + pattern +
1838 "' without a filename.");
1839 }
1840 return parsed;
1841 }
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 1816 of file json_serializer.cpp.

1817 {
1818 auto timing = std::make_unique<timing::PrototypeTiming>(j.at("name").get<std::string>(), id);
1819 j.get_to(*timing);
1820 return timing;
1821 }

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 1809 of file json_serializer.cpp.

1810 {
1811 std::unique_ptr<fers_signal::RadarSignal> wf;
1813 return wf;
1814 }
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:408
@ 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 429 of file hdf5_handler.cpp.

430 {
431 std::scoped_lock const lock(hdf5_global_mutex);
432 try
433 {
434 LOG(Level::TRACE, "Reading dataset '{}' from file '{}'", datasetName, name);
435 const HighFive::File file(name, HighFive::File::ReadOnly);
436
437 const auto dataset = file.getDataSet(datasetName);
438
439 const auto dataspace = dataset.getSpace();
440 const auto dims = dataspace.getDimensions();
441
442 if (dims.size() != 2)
443 {
444 LOG(Level::FATAL, "Invalid dataset dimensions for '{}' in file '{}'", datasetName, name);
445 throw std::runtime_error(
446 std::format(R"(Invalid dataset dimensions for "{}" in file "{}")", datasetName, name));
447 }
448
449 LOG(Level::TRACE, "Reading dataset with dimensions {}x{}", dims[0], dims[1]);
450
451 std::vector data(dims[0], std::vector<RealType>(dims[1]));
452 dataset.read(data);
453
454 LOG(Level::TRACE, "Read dataset successfully");
455
456 return data;
457 }
458 catch (const HighFive::Exception& err)
459 {
460 LOG(Level::FATAL, "Error handling HDF5 file: {}", err.what());
461 throw std::runtime_error("Error handling HDF5 file: " + std::string(err.what()));
462 }
463 }

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 319 of file hdf5_handler.cpp.

320 {
321 std::scoped_lock const lock(hdf5_global_mutex);
322
323 if (!std::filesystem::exists(name))
324 {
325 LOG(Level::FATAL, "File '{}' not found", name);
326 throw std::runtime_error("File " + name + " not found.");
327 }
328
329 LOG(Level::TRACE, "Opening file '{}'", name);
330 const HighFive::File file(name, HighFive::File::ReadOnly);
331
332 // Helper lambda to open group and read dataset
333 auto read_dataset = [&file](const std::string& groupName, std::vector<double>& buffer) -> size_t
334 {
335 const auto group = file.getGroup("/" + groupName);
336
337 const auto dataset = group.getDataSet("value");
338
339 const auto dimensions = dataset.getSpace().getDimensions();
340 const auto size = dimensions[0];
341
342 buffer.resize(size);
343 dataset.read(buffer);
344
345 return size;
346 };
347
348 LOG(Level::TRACE, "Reading dataset 'I' from file '{}'", name);
349 std::vector<double> buffer_i;
350 const auto size = read_dataset("I", buffer_i);
351
352 std::vector<double> buffer_q;
353 LOG(Level::TRACE, "Reading dataset 'Q' from file '{}'", name);
354 if (read_dataset("Q", buffer_q) != size)
355 {
356 LOG(Level::FATAL, "Dataset 'Q' is not the same size as dataset 'I' in file '{}'", name);
357 throw std::runtime_error(R"(Dataset "Q" is not the same size as dataset "I" in file )" + name);
358 }
359
360 data.resize(size);
361 for (size_t i = 0; i < size; ++i)
362 {
363 data[i] = ComplexType(buffer_i[i], buffer_q[i]);
364 }
365 LOG(Level::TRACE, "Read dataset successfully");
366 }

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 1913 of file json_serializer.cpp.

1914 {
1915 const auto new_pattern = j.value("pattern", "isotropic");
1917 {
1919 return;
1920 }
1921
1922 ant->setName(j.at("name").get<std::string>());
1923 ant->setEfficiencyFactor(j.value("efficiency", 1.0));
1924 update_existing_antenna_pattern_fields(j, ant, world);
1925 }
void replace(std::unique_ptr< radar::Target > target)
Replaces an existing target, updating internal pointers.
Definition world.cpp:282
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 1876 of file json_serializer.cpp.

1877 {
1878 if (auto* sinc = dynamic_cast<antenna::Sinc*>(ant))
1879 {
1880 sinc->setAlpha(j.value("alpha", 1.0));
1881 sinc->setBeta(j.value("beta", 1.0));
1882 sinc->setGamma(j.value("gamma", 2.0));
1883 }
1884 else if (auto* gauss = dynamic_cast<antenna::Gaussian*>(ant))
1885 {
1886 gauss->setAzimuthScale(j.value("azscale", 1.0));
1887 gauss->setElevationScale(j.value("elscale", 1.0));
1888 }
1889 else if (auto* horn = dynamic_cast<antenna::SquareHorn*>(ant))
1890 {
1891 horn->setDimension(j.value("diameter", 0.5));
1892 }
1893 else if (auto* para = dynamic_cast<antenna::Parabolic*>(ant))
1894 {
1895 para->setDiameter(j.value("diameter", 0.5));
1896 }
1897 else if (auto* xml = dynamic_cast<antenna::XmlAntenna*>(ant))
1898 {
1899 if (xml->getFilename() != j.value("filename", ""))
1900 {
1902 }
1903 }
1904 else if (auto* h5 = dynamic_cast<antenna::H5Antenna*>(ant))
1905 {
1906 if (h5->getFilename() != j.value("filename", ""))
1907 {
1909 }
1910 }
1911 }

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 2301 of file json_serializer.cpp.

2303 {
2306
2310 validate_transmitter_signal_state(*tx, "Monostatic '" + tx->getName() + "'");
2311 if (j.contains("fmcw_mode"))
2312 {
2313 parse_receiver_dechirp_config(j, *rx, "Monostatic '" + tx->getName() + "'");
2314 }
2316 }
void resolveReceiverDechirpReferences()
Resolves and validates receiver FMCW dechirp references after all components are loaded.
Definition world.cpp:553
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 2244 of file json_serializer.cpp.

2246 {
2247 if (j.contains("name"))
2248 rx.setName(j.at("name").get<std::string>());
2249 rx.setMode(tx.getMode());
2250 if (rx.getMode() == radar::OperationMode::PULSED_MODE && j.contains("pulsed_mode"))
2251 {
2252 const auto& mode_json = j.at("pulsed_mode");
2253 rx.setWindowProperties(mode_json.value("window_length", 0.0), tx.getPrf(),
2254 mode_json.value("window_skip", 0.0));
2255 }
2256 update_receiver_noise_and_flags_from_json(j, rx);
2257 if (j.contains("antenna"))
2258 {
2259 rx.setAntenna(world.findAntenna(parse_json_id(j, "antenna", "Monostatic")));
2260 }
2261 }
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 2283 of file json_serializer.cpp.

2284 {
2285 if (!j.contains("schedule"))
2286 {
2287 return;
2288 }
2289 auto raw = j.at("schedule").get<std::vector<radar::SchedulePeriod>>();
2290 const bool pulsed = tx.getMode() == radar::OperationMode::PULSED_MODE;
2291 const RealType pri = pulsed ? 1.0 / tx.getPrf() : 0.0;
2292 auto processed_schedule = radar::processRawSchedule(raw, tx.getName(), pulsed, pri);
2293 if (tx.getSignal() != nullptr && (tx.getSignal()->isFmcwFamily() || tx.getSignal()->isSteppedFrequency()))
2294 {
2295 validate_fmcw_schedule(processed_schedule, *tx.getSignal(), "Monostatic '" + tx.getName() + "'");
2296 }
2297 tx.setSchedule(processed_schedule);
2298 rx.setSchedule(processed_schedule);
2299 }
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 2263 of file json_serializer.cpp.

2265 {
2266 if (!j.contains("timing"))
2267 {
2268 return;
2269 }
2270 auto timing_id = parse_json_id(j, "timing", "Monostatic");
2271 auto* const timing_proto = world.findTiming(timing_id);
2272 if (timing_proto == nullptr)
2273 {
2274 throw std::runtime_error("Timing ID " + std::to_string(timing_id) + " not found.");
2275 }
2276 unsigned const seed = rx.getTiming() ? rx.getTiming()->getSeed() : 0;
2277 auto shared_timing = std::make_shared<timing::Timing>(timing_proto->getName(), seed, timing_proto->getId());
2278 shared_timing->initializeModel(timing_proto);
2279 tx.setTiming(shared_timing);
2280 rx.setTiming(shared_timing);
2281 }
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 1823 of file json_serializer.cpp.

1824 {
1825 nlohmann::json sim;
1826 sim["parameters"] = j;
1828 }

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 1927 of file json_serializer.cpp.

1928 {
1929 if (j.contains("motionpath"))
1930 {
1931 auto path = std::make_unique<math::Path>();
1932 j.at("motionpath").get_to(*path);
1933 plat->setMotionPath(std::move(path));
1934 }
1935 if (j.contains("rotationpath"))
1936 {
1937 auto rot_path = std::make_unique<math::RotationPath>();
1938 const auto& rotation_json = j.at("rotationpath");
1939 rot_path->setInterp(rotation_json.at("interpolation").get<math::RotationPath::InterpType>());
1940 unsigned waypoint_index = 0;
1941 for (const auto& waypoint_json : rotation_json.at("rotationwaypoints"))
1942 {
1943 const RealType azimuth = waypoint_json.at("azimuth").get<RealType>();
1944 const RealType elevation = waypoint_json.at("elevation").get<RealType>();
1945 const RealType time = waypoint_json.at("time").get<RealType>();
1946 const std::string owner =
1947 std::format("platform '{}' rotation waypoint {}", plat->getName(), waypoint_index);
1948
1949 rotation_warning_utils::maybe_warn_about_rotation_value(azimuth, params::rotationAngleUnit(),
1950 rotation_warning_utils::ValueKind::Angle,
1951 "JSON", owner, "azimuth");
1952 rotation_warning_utils::maybe_warn_about_rotation_value(elevation, params::rotationAngleUnit(),
1953 rotation_warning_utils::ValueKind::Angle,
1954 "JSON", owner, "elevation");
1955
1956 rot_path->addCoord(rotation_angle_utils::external_rotation_to_internal(azimuth, elevation, time,
1959 }
1960 rot_path->finalize();
1961 plat->setRotationPath(std::move(rot_path));
1962 }
1963 else if (j.contains("fixedrotation"))
1964 {
1965 auto rot_path = std::make_unique<math::RotationPath>();
1966 const auto& fixed_json = j.at("fixedrotation");
1967 const RealType start_az_deg = fixed_json.at("startazimuth").get<RealType>();
1968 const RealType start_el_deg = fixed_json.at("startelevation").get<RealType>();
1969 const RealType rate_az_deg_s = fixed_json.at("azimuthrate").get<RealType>();
1970 const RealType rate_el_deg_s = fixed_json.at("elevationrate").get<RealType>();
1971 const std::string owner = std::format("platform '{}' fixedrotation", plat->getName());
1972
1973 rotation_warning_utils::maybe_warn_about_rotation_value(start_az_deg, params::rotationAngleUnit(),
1974 rotation_warning_utils::ValueKind::Angle, "JSON",
1975 owner, "startazimuth");
1976 rotation_warning_utils::maybe_warn_about_rotation_value(start_el_deg, params::rotationAngleUnit(),
1977 rotation_warning_utils::ValueKind::Angle, "JSON",
1978 owner, "startelevation");
1979 rotation_warning_utils::maybe_warn_about_rotation_value(rate_az_deg_s, params::rotationAngleUnit(),
1980 rotation_warning_utils::ValueKind::Rate, "JSON",
1981 owner, "azimuthrate");
1982 rotation_warning_utils::maybe_warn_about_rotation_value(rate_el_deg_s, params::rotationAngleUnit(),
1983 rotation_warning_utils::ValueKind::Rate, "JSON",
1984 owner, "elevationrate");
1985
1990 rot_path->setConstantRate(start, rate);
1991 rot_path->finalize();
1992 plat->setRotationPath(std::move(rot_path));
1993 }
1994 }
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 2167 of file json_serializer.cpp.

2168 {
2169 if (!j.contains("antenna"))
2170 {
2171 return;
2172 }
2173 auto id = parse_json_id(j, "antenna", "Receiver");
2174 auto* ant = world.findAntenna(id);
2175 if (ant == nullptr)
2176 {
2177 throw std::runtime_error("Antenna ID " + std::to_string(id) + " not found.");
2178 }
2179 rx.setAntenna(ant);
2180 }

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 2212 of file json_serializer.cpp.

2214 {
2215 if (j.contains("name"))
2216 rx->setName(j.at("name").get<std::string>());
2217
2223 if (j.contains("fmcw_mode"))
2224 {
2225 parse_receiver_dechirp_config(j, *rx, "Receiver '" + rx->getName() + "'");
2226 }
2228 }
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 2121 of file json_serializer.cpp.

2122 {
2123 reject_conflicting_mode_blocks(j, "Receiver '" + rx.getName() + "'");
2124 if (j.contains("pulsed_mode"))
2125 {
2127 const auto& mode_json = j.at("pulsed_mode");
2128 rx.setWindowProperties(mode_json.value("window_length", 0.0), mode_json.value("prf", 0.0),
2129 mode_json.value("window_skip", 0.0));
2130 }
2131 else if (j.contains("fmcw_mode"))
2132 {
2134 }
2135 else if (j.contains("sfcw_mode"))
2136 {
2137 reject_non_empty_sfcw_mode(j, "Receiver '" + rx.getName() + "'");
2139 }
2140 else if (j.contains("cw_mode"))
2141 {
2143 }
2144 }
@ SFCW_MODE
The component operates in a stepped-frequency CW streaming mode.
@ 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, radar::PULSED_MODE, and radar::SFCW_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 2146 of file json_serializer.cpp.

2147 {
2148 if (j.contains("noise_temp"))
2149 rx.setNoiseTemperature(j.value("noise_temp", 0.0));
2150
2151 if (j.contains("nodirect"))
2152 {
2153 if (j.value("nodirect", false))
2155 else
2157 }
2158 if (j.contains("nopropagationloss"))
2159 {
2160 if (j.value("nopropagationloss", false))
2162 else
2164 }
2165 }
@ 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 2200 of file json_serializer.cpp.

2201 {
2202 if (!j.contains("schedule"))
2203 {
2204 return;
2205 }
2206 auto raw = j.at("schedule").get<std::vector<radar::SchedulePeriod>>();
2207 const bool pulsed = rx.getMode() == radar::OperationMode::PULSED_MODE;
2208 const RealType pri = pulsed ? 1.0 / rx.getWindowPrf() : 0.0;
2209 rx.setSchedule(radar::processRawSchedule(raw, rx.getName(), pulsed, pri));
2210 }

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 2182 of file json_serializer.cpp.

2183 {
2184 if (!j.contains("timing"))
2185 {
2186 return;
2187 }
2188 auto timing_id = parse_json_id(j, "timing", "Receiver");
2189 auto* const timing_proto = world.findTiming(timing_id);
2190 if (timing_proto == nullptr)
2191 {
2192 throw std::runtime_error("Timing ID " + std::to_string(timing_id) + " not found.");
2193 }
2194 unsigned const seed = rx.getTiming() ? rx.getTiming()->getSeed() : 0;
2195 auto timing = std::make_shared<timing::Timing>(timing_proto->getName(), seed, timing_proto->getId());
2196 timing->initializeModel(timing_proto);
2197 rx.setTiming(timing);
2198 }

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 2318 of file json_serializer.cpp.

2320 {
2321 auto* plat = existing_tgt->getPlatform();
2322 const auto& rcs_json = j.at("rcs");
2323 const auto rcs_type = rcs_json.at("type").get<std::string>();
2324 std::unique_ptr<radar::Target> target_obj;
2325
2326 const auto target_id = existing_tgt->getId();
2327 const auto name = j.value("name", existing_tgt->getName());
2328 unsigned const seed = existing_tgt->getSeed();
2329
2330 if (rcs_type == "isotropic")
2331 {
2332 target_obj = radar::createIsoTarget(plat, name, rcs_json.value("value", 1.0), seed, target_id);
2333 }
2334 else if (rcs_type == "file")
2335 {
2336 const auto filename = rcs_json.value("filename", "");
2338 }
2339 else
2340 {
2341 throw std::runtime_error("Unsupported target RCS type: " + rcs_type);
2342 }
2343
2344 if (j.contains("model"))
2345 {
2346 const auto& model_json = j.at("model");
2347 const auto model_type = model_json.at("type").get<std::string>();
2348 if (model_type == "chisquare" || model_type == "gamma")
2349 {
2350 auto model =
2351 std::make_unique<radar::RcsChiSquare>(target_obj->getRngEngine(), model_json.value("k", 1.0));
2352 target_obj->setFluctuationModel(std::move(model));
2353 }
2354 else if (model_type == "constant")
2355 {
2356 target_obj->setFluctuationModel(std::make_unique<radar::RcsConst>());
2357 }
2358 }
2359
2360 world.replace(std::move(target_obj));
2361 }
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 2363 of file json_serializer.cpp.

2364 {
2365 auto* existing = world.findTiming(id);
2366 if (existing == nullptr)
2367 {
2368 throw std::runtime_error("Timing ID " + std::to_string(id) + " not found.");
2369 }
2370
2371 auto patched = j;
2372 if (!patched.contains("name"))
2373 {
2374 patched["name"] = existing->getName();
2375 }
2376
2378 }
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 2041 of file json_serializer.cpp.

2042 {
2043 if (!j.contains("antenna"))
2044 {
2045 return;
2046 }
2047 auto id = parse_json_id(j, "antenna", "Transmitter");
2048 auto* ant = world.findAntenna(id);
2049 if (ant == nullptr)
2050 {
2051 throw std::runtime_error("Antenna ID " + std::to_string(id) + " not found.");
2052 }
2053 tx.setAntenna(ant);
2054 }

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 2106 of file json_serializer.cpp.

2108 {
2109 if (j.contains("name"))
2110 tx->setName(j.at("name").get<std::string>());
2111
2112 const std::string owner = "Transmitter '" + tx->getName() + "'";
2119 }
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 1996 of file json_serializer.cpp.

1997 {
1998 reject_conflicting_mode_blocks(j, "Transmitter '" + tx.getName() + "'");
1999 if (j.contains("pulsed_mode"))
2000 {
2002 tx.setPrf(j.at("pulsed_mode").value("prf", 0.0));
2003 }
2004 else if (j.contains("fmcw_mode"))
2005 {
2006 if (has_dechirp_fields(j.at("fmcw_mode")))
2007 {
2008 throw std::runtime_error("Transmitter '" + tx.getName() +
2009 "' fmcw_mode must not contain dechirp configuration.");
2010 }
2012 }
2013 else if (j.contains("sfcw_mode"))
2014 {
2015 reject_non_empty_sfcw_mode(j, "Transmitter '" + tx.getName() + "'");
2017 }
2018 else if (j.contains("cw_mode"))
2019 {
2021 }
2022 }

References radar::CW_MODE, radar::FMCW_MODE, max, radar::PULSED_MODE, and radar::SFCW_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 2088 of file json_serializer.cpp.

2090 {
2091 if (!j.contains("schedule"))
2092 {
2093 return;
2094 }
2095 auto raw = j.at("schedule").get<std::vector<radar::SchedulePeriod>>();
2096 const bool pulsed = tx.getMode() == radar::OperationMode::PULSED_MODE;
2097 const RealType pri = pulsed ? 1.0 / tx.getPrf() : 0.0;
2098 auto schedule = radar::processRawSchedule(raw, tx.getName(), pulsed, pri);
2099 if (tx.getSignal() != nullptr && (tx.getSignal()->isFmcwFamily() || tx.getSignal()->isSteppedFrequency()))
2100 {
2101 validate_fmcw_schedule(schedule, *tx.getSignal(), owner);
2102 }
2103 tx.setSchedule(std::move(schedule));
2104 }

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 2056 of file json_serializer.cpp.

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

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 2024 of file json_serializer.cpp.

2025 {
2026 if (!j.contains("waveform"))
2027 {
2028 return;
2029 }
2030 auto id = parse_json_id(j, "waveform", "Transmitter");
2031 auto* wf = world.findWaveform(id);
2032 if (wf == nullptr)
2033 {
2034 throw std::runtime_error("Waveform ID " + std::to_string(id) + " not found.");
2035 }
2036 validate_fmcw_waveform(*wf, "Waveform '" + wf->getName() + "'");
2037 validate_waveform_mode_match(*wf, tx.getMode(), "Transmitter '" + tx.getName() + "'");
2038 tx.setWave(wf);
2039 }
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 2074 of file json_serializer.cpp.

2075 {
2076 if (tx.getSignal() == nullptr)
2077 {
2078 return;
2079 }
2080 validate_fmcw_waveform(*tx.getSignal(), "Waveform '" + tx.getSignal()->getName() + "'");
2081 validate_waveform_mode_match(*tx.getSignal(), tx.getMode(), owner);
2082 if (tx.getSignal()->isFmcwFamily() || tx.getSignal()->isSteppedFrequency())
2083 {
2084 validate_fmcw_schedule(tx.getSchedule(), *tx.getSignal(), owner);
2085 }
2086 }

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 2380 of file json_serializer.cpp.

2381 {
2382 nlohmann::json sim_json;
2383
2385 sim_json["parameters"] = params::params;
2386
2387 sim_json["waveforms"] = nlohmann::json::array();
2388 for (const auto& waveform : world.getWaveforms() | std::views::values)
2389 {
2390 sim_json["waveforms"].push_back(*waveform);
2391 }
2392
2393 sim_json["antennas"] = nlohmann::json::array();
2394 for (const auto& antenna : world.getAntennas() | std::views::values)
2395 {
2396 sim_json["antennas"].push_back(*antenna);
2397 }
2398
2399 sim_json["timings"] = nlohmann::json::array();
2400 for (const auto& timing : world.getTimings() | std::views::values)
2401 {
2402 sim_json["timings"].push_back(*timing);
2403 }
2404
2405 sim_json["platforms"] = nlohmann::json::array();
2406 for (const auto& p : world.getPlatforms())
2407 {
2408 sim_json["platforms"].push_back(serialize_platform(p.get(), world));
2409 }
2410
2411 return {{"simulation", sim_json}};
2412 }
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 309 of file hdf5_handler.cpp.

310 {
311 writeBaseMetadataAttributes(file, metadata);
312 writeFmcwIfAttributes(file, metadata);
313 writeDechirpReferenceAttributes(file, metadata);
314 writeFmcwAttributes(file, metadata);
315 writeSfcwAttributes(file, metadata);
316 writeNestedSfcwMetadata(file, metadata);
317 }

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().