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

Classes

struct  AssetLoaders
 Container for functions that load external file-backed assets. More...
 
struct  ParserContext
 Encapsulates the state required during the XML parsing process. More...
 
struct  ReferenceLookup
 Holds maps to resolve string names to internal SimId references during XML parsing. More...
 

Functions

RealType get_child_real_type (const XmlElement &element, const std::string &elementName)
 Extracts a floating-point (RealType) value from a named child element.
 
bool get_attribute_bool (const XmlElement &element, const std::string &attributeName, bool defaultVal)
 Extracts a boolean value from a named attribute.
 
SimId assign_id_from_attribute (const std::string &owner, ObjectType type)
 Generates a unique SimId based on the requested object type.
 
SimId resolve_reference_id (const XmlElement &element, const std::string &attributeName, const std::string &owner, const std::unordered_map< std::string, SimId > &name_map)
 Resolves an XML string reference into an internal SimId.
 
std::vector< radar::SchedulePeriodparseSchedule (const XmlElement &parent, const std::string &parentName, bool isPulsed, RealType pri=0.0)
 Parses a schedule (active periods) for a transmitter or receiver.
 
unsigned parseUnsignedParameter (const std::string_view param_name, const RealType raw_value)
 
template<typename Setter >
void setOptionalRealParameter (const XmlElement &parameters, const std::string &param_name, const RealType default_value, Setter setter)
 
template<typename Setter >
void setOptionalUnsignedParameter (const XmlElement &parameters, const std::string &param_name, const unsigned default_value, Setter setter)
 
void parseOptionalNumericParameters (const XmlElement &parameters, params::Parameters &params_out)
 
void parseRotationAngleUnit (const XmlElement &parameters, params::Parameters &params_out)
 
bool parseOriginParameter (const XmlElement &parameters, params::Parameters &params_out)
 
void parseUtmCoordinateSystem (const XmlElement &cs_element, params::Parameters &params_out)
 
void parseCoordinateSystemParameter (const XmlElement &parameters, params::Parameters &params_out, const bool origin_set)
 
void parseParameters (const XmlElement &parameters, params::Parameters &params_out)
 Parses the <parameters> block into the isolated context parameters.
 
void parseWaveform (const XmlElement &waveform, ParserContext &ctx)
 Parses a <waveform> block and adds it to the World.
 
void parseTiming (const XmlElement &timing, ParserContext &ctx)
 Parses a <timing> block and adds the prototype timing to the World.
 
void parseAntenna (const XmlElement &antenna, ParserContext &ctx)
 Parses an <antenna> block and adds it to the World.
 
void parseMotionPath (const XmlElement &motionPath, radar::Platform *platform)
 Parses a <motionpath> block and attaches it to a Platform.
 
void parseRotationPath (const XmlElement &rotation, radar::Platform *platform, params::RotationAngleUnit unit)
 Parses a <rotationpath> block and attaches it to a Platform.
 
void parseFixedRotation (const XmlElement &rotation, radar::Platform *platform, params::RotationAngleUnit unit)
 Parses a <fixedrotation> block and attaches it to a Platform.
 
static radar::TransmitterparseTransmitterWithMode (const XmlElement &transmitter, radar::Platform *platform, ParserContext &ctx, const ReferenceLookup &refs, const radar::OperationMode mode)
 Parses a transmitter after its operation mode has already been determined.
 
radar::TransmitterparseTransmitter (const XmlElement &transmitter, radar::Platform *platform, ParserContext &ctx, const ReferenceLookup &refs)
 Parses a <transmitter> block, resolves its dependencies, and adds it to the World.
 
static radar::ReceiverparseReceiverWithMode (const XmlElement &receiver, radar::Platform *platform, ParserContext &ctx, const ReferenceLookup &refs, const radar::OperationMode mode)
 Parses a receiver after its operation mode has already been determined.
 
radar::ReceiverparseReceiver (const XmlElement &receiver, radar::Platform *platform, ParserContext &ctx, const ReferenceLookup &refs)
 Parses a <receiver> block, resolves its dependencies, and adds it to the World.
 
void parseMonostatic (const XmlElement &monostatic, radar::Platform *platform, ParserContext &ctx, const ReferenceLookup &refs)
 Parses a <monostatic> block, creating a linked transmitter and receiver pair.
 
void parseTarget (const XmlElement &target, radar::Platform *platform, ParserContext &ctx)
 Parses a <target> block and adds it to the World.
 
void parsePlatformElements (const XmlElement &platform, ParserContext &ctx, radar::Platform *plat, const std::function< void(const XmlElement &, std::string_view)> &register_name, const ReferenceLookup &refs)
 Iterates and parses all children elements (radars, targets) of a platform.
 
void parsePlatform (const XmlElement &platform, ParserContext &ctx, const std::function< void(const XmlElement &, std::string_view)> &register_name, const ReferenceLookup &refs)
 Parses a complete <platform> block, including its motion paths and sub-elements.
 
void collectIncludeElements (const XmlDocument &doc, const fs::path &currentDir, std::vector< fs::path > &includePaths)
 
bool addIncludeFilesToMainDocument (const XmlDocument &mainDoc, const fs::path &currentDir)
 
void validateXml (bool didCombine, const XmlDocument &mainDoc)
 Validates an XML document against the embedded DTD and XSD schemas.
 
void processParsedDocument (const XmlDocument &doc, ParserContext &ctx)
 Coordinates the full parsing of a validated XML document tree.
 
AssetLoaders createDefaultAssetLoaders ()
 Creates an AssetLoaders struct populated with standard file-I/O implementations.
 
void collectIncludeElements (const XmlDocument &doc, const std::filesystem::path &currentDir, std::vector< std::filesystem::path > &includePaths)
 Recursively finds all <include> tags in a document and resolves their absolute paths.
 
bool addIncludeFilesToMainDocument (const XmlDocument &mainDoc, const std::filesystem::path &currentDir)
 Resolves and merges all <include> files directly into the provided main document.
 

Function Documentation

◆ addIncludeFilesToMainDocument() [1/2]

bool serial::xml_parser_utils::addIncludeFilesToMainDocument ( const XmlDocument mainDoc,
const fs::path &  currentDir 
)

Definition at line 1362 of file xml_parser_utils.cpp.

1363 {
1364 std::vector<fs::path> include_paths;
1366 bool did_combine = false;
1367
1368 for (const auto& include_path : include_paths)
1369 {
1371 if (!included_doc.loadFile(include_path.string()))
1372 {
1373 throw XmlException("Failed to load included XML file: " + include_path.string());
1374 }
1375
1377 did_combine = true;
1378 }
1379
1381 return did_combine;
1382 }
Class for managing XML documents.
Exception class for handling XML-related errors.
void mergeXmlDocuments(const XmlDocument &mainDoc, const XmlDocument &includedDoc)
Merge two XML documents.
void removeIncludeElements(const XmlDocument &doc)
Remove "include" elements from the XML document.
void collectIncludeElements(const XmlDocument &doc, const fs::path &currentDir, std::vector< fs::path > &includePaths)
math::Vec3 max

References collectIncludeElements(), max, mergeXmlDocuments(), and removeIncludeElements().

Referenced by serial::parseSimulation().

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

◆ addIncludeFilesToMainDocument() [2/2]

bool serial::xml_parser_utils::addIncludeFilesToMainDocument ( const XmlDocument mainDoc,
const std::filesystem::path &  currentDir 
)

Resolves and merges all <include> files directly into the provided main document.

Parameters
mainDocThe primary XML document that will be mutated.
currentDirThe base directory used to resolve include paths.
Returns
True if at least one file was included and merged, false otherwise.

◆ assign_id_from_attribute()

SimId serial::xml_parser_utils::assign_id_from_attribute ( const std::string &  owner,
ObjectType  type 
)

Generates a unique SimId based on the requested object type.

Parameters
ownerThe name/description of the object requesting the ID (used for logging).
typeThe category/type of the object.
Returns
A newly generated SimId.

Definition at line 349 of file xml_parser_utils.cpp.

350 {
351 const SimId id = SimIdGenerator::instance().generateId(type);
352 LOG(logging::Level::TRACE, "Assigned ID {} to {} (generated)", id, owner);
353 return id;
354 }
SimId generateId(ObjectType type)
Generate a unique SimId for a given object type.
Definition sim_id.h:60
static SimIdGenerator & instance()
Get the singleton instance of SimIdGenerator.
Definition sim_id.h:48
#define LOG(level,...)
Definition logging.h:19
@ TRACE
Trace level for detailed debugging information.
uint64_t SimId
64-bit Unique Simulation ID.
Definition sim_id.h:18

References SimIdGenerator::generateId(), SimIdGenerator::instance(), LOG, max, and logging::TRACE.

Referenced by parseAntenna(), parsePlatform(), parseReceiverWithMode(), parseTarget(), parseTiming(), parseTransmitterWithMode(), and parseWaveform().

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

◆ collectIncludeElements() [1/2]

void serial::xml_parser_utils::collectIncludeElements ( const XmlDocument doc,
const fs::path &  currentDir,
std::vector< fs::path > &  includePaths 
)

Definition at line 1332 of file xml_parser_utils.cpp.

1333 {
1334 unsigned index = 0;
1335 while (true)
1336 {
1337 XmlElement const include_element = doc.getRootElement().childElement("include", index++);
1338 if (!include_element.isValid())
1339 break;
1340
1341 std::string const include_filename = include_element.getText();
1342 if (include_filename.empty())
1343 {
1344 LOG(logging::Level::ERROR, "<include> element is missing the filename!");
1345 continue;
1346 }
1347
1348 fs::path const include_path = currentDir / include_filename;
1349 includePaths.push_back(include_path);
1350
1352 if (!included_doc.loadFile(include_path.string()))
1353 {
1354 LOG(logging::Level::ERROR, "Failed to load included XML file: {}", include_path.string());
1355 continue;
1356 }
1357
1358 collectIncludeElements(included_doc, include_path.parent_path(), includePaths);
1359 }
1360 }
Class representing a node in an XML document.
@ ERROR
Error level for error events.

References collectIncludeElements(), logging::ERROR, LOG, and max.

Referenced by addIncludeFilesToMainDocument(), and collectIncludeElements().

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

◆ collectIncludeElements() [2/2]

void serial::xml_parser_utils::collectIncludeElements ( const XmlDocument doc,
const std::filesystem::path &  currentDir,
std::vector< std::filesystem::path > &  includePaths 
)

Recursively finds all <include> tags in a document and resolves their absolute paths.

Parameters
docThe XML document to search.
currentDirThe base directory for resolving relative paths.
includePathsA vector populated with the absolute paths of included files.

◆ createDefaultAssetLoaders()

AssetLoaders serial::xml_parser_utils::createDefaultAssetLoaders ( )

Creates an AssetLoaders struct populated with standard file-I/O implementations.

Provides the default hooks to load actual HDF5, XML, and binary waveform files from the filesystem into the simulation environment.

Returns
An AssetLoaders instance with standard file handlers attached.

Definition at line 1504 of file xml_parser_utils.cpp.

1505 {
1506 return {.loadWaveform = [](const std::string& name, const fs::path& pulse_path, RealType power,
1508 { return serial::loadWaveformFromFile(name, pulse_path.string(), power, carrierFreq, id); },
1509 .loadXmlAntenna = [](const std::string& name, const std::string& filename, SimId id)
1510 { return std::make_unique<antenna::XmlAntenna>(name, filename, id); },
1511 .loadH5Antenna = [](const std::string& name, const std::string& filename, SimId id)
1512 { return std::make_unique<antenna::H5Antenna>(name, filename, id); },
1513 .loadFileTarget = [](radar::Platform* platform, const std::string& name, const std::string& filename,
1514 unsigned seed, SimId id)
1515 { return radar::createFileTarget(platform, name, filename, seed, id); }};
1516 }
Represents a simulation platform with motion and rotation paths.
Definition platform.h:32
double RealType
Type for real numbers.
Definition config.h:27
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
std::unique_ptr< RadarSignal > loadWaveformFromFile(const std::string &name, const std::string &filename, const RealType power, const RealType carrierFreq, const SimId id)
Loads a radar waveform from a file and returns a RadarSignal object.

References radar::createFileTarget(), serial::xml_parser_utils::AssetLoaders::loadWaveform, serial::loadWaveformFromFile(), and max.

Referenced by serial::parseSimulation(), and serial::parseSimulationFromString().

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

◆ get_attribute_bool()

bool serial::xml_parser_utils::get_attribute_bool ( const XmlElement element,
const std::string &  attributeName,
bool  defaultVal 
)

Extracts a boolean value from a named attribute.

Parameters
elementThe XML element containing the attribute.
attributeNameThe name of the attribute.
defaultValThe value to return if the attribute is missing or invalid.
Returns
The parsed boolean value, or the default if the attribute is missing or invalid.

Definition at line 327 of file xml_parser_utils.cpp.

328 {
330 if (!attr_value.has_value())
331 {
332 LOG(logging::Level::DEBUG, "Attribute '{}' not specified. Defaulting to {}.", attributeName, defaultVal);
333 return defaultVal;
334 }
335 if (*attr_value == "true")
336 {
337 return true;
338 }
339 if (*attr_value == "false")
340 {
341 return false;
342 }
343
344 LOG(logging::Level::WARNING, "Invalid boolean value '{}' for attribute '{}'. Defaulting to {}.", *attr_value,
346 return defaultVal;
347 }
static std::optional< std::string > getOptionalAttribute(const XmlElement &element, const std::string_view name)
Get the value of an optional attribute.
@ WARNING
Warning level for potentially harmful situations.
@ DEBUG
Debug level for general debugging information.

References logging::DEBUG, XmlElement::getOptionalAttribute(), LOG, max, and logging::WARNING.

Referenced by parseReceiverWithMode(), and parseTiming().

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

◆ get_child_real_type()

RealType serial::xml_parser_utils::get_child_real_type ( const XmlElement element,
const std::string &  elementName 
)

Extracts a floating-point (RealType) value from a named child element.

Parameters
elementThe parent XML element.
elementNameThe name of the child element to extract text from.
Returns
The parsed floating-point value.
Exceptions
XmlExceptionif the child element is missing or empty.

Definition at line 317 of file xml_parser_utils.cpp.

318 {
319 const std::string text = element.childElement(elementName, 0).getText();
320 if (text.empty())
321 {
322 throw XmlException("Element " + elementName + " is empty!");
323 }
324 return std::stod(text);
325 }

References max.

Referenced by parseAntenna(), parseFixedRotation(), parseMotionPath(), parseOptionalNumericParameters(), parseParameters(), parseReceiverWithMode(), parseRotationPath(), parseTarget(), parseTiming(), parseTransmitterWithMode(), parseWaveform(), setOptionalRealParameter(), and setOptionalUnsignedParameter().

+ Here is the caller graph for this function:

◆ parseAntenna()

void serial::xml_parser_utils::parseAntenna ( const XmlElement antenna,
ParserContext ctx 
)

Parses an <antenna> block and adds it to the World.

Parameters
antennaThe <antenna> XML element.
ctxThe current parser context.

Definition at line 792 of file xml_parser_utils.cpp.

793 {
794 const std::string name = XmlElement::getSafeAttribute(antenna, "name");
795 const SimId id = assign_id_from_attribute("antenna '" + name + "'", ObjectType::Antenna);
796 const std::string pattern = XmlElement::getSafeAttribute(antenna, "pattern");
797
798 std::unique_ptr<antenna::Antenna> ant;
799
800 LOG(logging::Level::DEBUG, "Adding antenna '{}' with pattern '{}'", name, pattern);
801 if (pattern == "isotropic")
802 {
803 ant = std::make_unique<antenna::Isotropic>(name, id);
804 }
805 else if (pattern == "sinc")
806 {
807 ant = std::make_unique<antenna::Sinc>(name, get_child_real_type(antenna, "alpha"),
808 get_child_real_type(antenna, "beta"),
809 get_child_real_type(antenna, "gamma"), id);
810 }
811 else if (pattern == "gaussian")
812 {
813 ant = std::make_unique<antenna::Gaussian>(name, get_child_real_type(antenna, "azscale"),
814 get_child_real_type(antenna, "elscale"), id);
815 }
816 else if (pattern == "squarehorn")
817 {
818 ant = std::make_unique<antenna::SquareHorn>(name, get_child_real_type(antenna, "diameter"), id);
819 }
820 else if (pattern == "parabolic")
821 {
822 ant = std::make_unique<antenna::Parabolic>(name, get_child_real_type(antenna, "diameter"), id);
823 }
824 else if (pattern == "xml")
825 {
826 ant = ctx.loaders.loadXmlAntenna(name, XmlElement::getSafeAttribute(antenna, "filename"), id);
827 }
828 else if (pattern == "file")
829 {
830 ant = ctx.loaders.loadH5Antenna(name, XmlElement::getSafeAttribute(antenna, "filename"), id);
831 }
832 else
833 {
834 LOG(logging::Level::FATAL, "Unsupported antenna pattern: {}", pattern);
835 throw XmlException("Unsupported antenna pattern: " + pattern);
836 }
837
838 if (!antenna.childElement("efficiency", 0).isValid())
839 {
840 LOG(logging::Level::DEBUG, "Antenna '{}' does not specify efficiency, assuming unity.", name);
841 }
842 else
843 {
844 try
845 {
846 ant->setEfficiencyFactor(get_child_real_type(antenna, "efficiency"));
847 }
848 catch (const XmlException&)
849 {
850 LOG(logging::Level::WARNING, "Antenna '{}' has an empty efficiency, assuming unity.", name);
851 }
852 }
853
854 ctx.world->add(std::move(ant));
855 }
static std::string getSafeAttribute(const XmlElement &element, const std::string_view name)
Get the value of an attribute safely.
@ FATAL
Fatal level for severe error events.
SimId assign_id_from_attribute(const std::string &owner, ObjectType type)
Generates a unique SimId based on the requested object type.
RealType get_child_real_type(const XmlElement &element, const std::string &elementName)
Extracts a floating-point (RealType) value from a named child element.

References Antenna, assign_id_from_attribute(), logging::DEBUG, logging::FATAL, get_child_real_type(), XmlElement::getSafeAttribute(), LOG, max, and logging::WARNING.

Referenced by processParsedDocument().

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

◆ parseCoordinateSystemParameter()

void serial::xml_parser_utils::parseCoordinateSystemParameter ( const XmlElement parameters,
params::Parameters params_out,
const bool  origin_set 
)

Definition at line 569 of file xml_parser_utils.cpp.

571 {
572 if (const XmlElement cs_element = parameters.childElement("coordinatesystem", 0); cs_element.isValid())
573 {
574 try
575 {
576 const std::string frame_str = XmlElement::getSafeAttribute(cs_element, "frame");
577 if (frame_str == "UTM")
578 {
580 }
581 else if (frame_str == "ECEF")
582 {
583 params_out.coordinate_frame = params::CoordinateFrame::ECEF;
584 LOG(logging::Level::INFO, "KML coordinate system set to ECEF.");
585 }
586 else if (frame_str == "ENU")
587 {
588 params_out.coordinate_frame = params::CoordinateFrame::ENU;
589 if (!origin_set)
590 {
592 "ENU KML frame specified but no <origin> tag found. Using default KML origin at UCT.");
593 }
594 LOG(logging::Level::INFO, "KML coordinate system set to ENU local tangent plane.");
595 }
596 else
597 {
598 throw XmlException("Unsupported KML coordinate frame: " + frame_str);
599 }
600 }
601 catch (const std::exception& e)
602 {
604 "Could not parse KML <coordinatesystem> from XML: {}. Defaulting KML export to ENU.", e.what());
605 params_out.coordinate_frame = params::CoordinateFrame::ENU;
606 params_out.utm_zone = 0;
607 params_out.utm_north_hemisphere = true;
608 }
609 }
610 }
XmlElement childElement(const std::string_view name="", const unsigned index=0) const noexcept
Retrieve a child element by name and index.
@ INFO
Info level for informational messages.
@ ENU
East-North-Up local tangent plane (default)
@ ECEF
Earth-Centered, Earth-Fixed.
void parseUtmCoordinateSystem(const XmlElement &cs_element, params::Parameters &params_out)

References XmlElement::childElement(), params::ECEF, params::ENU, XmlElement::getSafeAttribute(), logging::INFO, LOG, max, parseUtmCoordinateSystem(), and logging::WARNING.

Referenced by parseParameters().

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

◆ parseFixedRotation()

void serial::xml_parser_utils::parseFixedRotation ( const XmlElement rotation,
radar::Platform platform,
params::RotationAngleUnit  unit 
)

Parses a <fixedrotation> block and attaches it to a Platform.

Parameters
rotationThe <fixedrotation> XML element.
platformThe platform to modify.

Definition at line 985 of file xml_parser_utils.cpp.

986 {
987 math::RotationPath* path = platform->getRotationPath();
988 try
989 {
990 const RealType start_az_deg = get_child_real_type(rotation, "startazimuth");
991 const RealType start_el_deg = get_child_real_type(rotation, "startelevation");
992 const RealType rate_az_deg_s = get_child_real_type(rotation, "azimuthrate");
993 const RealType rate_el_deg_s = get_child_real_type(rotation, "elevationrate");
994 const std::string owner = std::format("platform '{}' fixedrotation", platform->getName());
995
996 rotation_warning_utils::maybe_warn_about_rotation_value(
997 start_az_deg, unit, rotation_warning_utils::ValueKind::Angle, "XML", owner, "startazimuth");
998 rotation_warning_utils::maybe_warn_about_rotation_value(
999 start_el_deg, unit, rotation_warning_utils::ValueKind::Angle, "XML", owner, "startelevation");
1000 rotation_warning_utils::maybe_warn_about_rotation_value(
1001 rate_az_deg_s, unit, rotation_warning_utils::ValueKind::Rate, "XML", owner, "azimuthrate");
1002 rotation_warning_utils::maybe_warn_about_rotation_value(
1003 rate_el_deg_s, unit, rotation_warning_utils::ValueKind::Rate, "XML", owner, "elevationrate");
1004 const math::RotationCoord start =
1005 rotation_angle_utils::external_rotation_to_internal(start_az_deg, start_el_deg, 0.0, unit);
1006 const math::RotationCoord rate =
1007 rotation_angle_utils::external_rotation_rate_to_internal(rate_az_deg_s, rate_el_deg_s, 0.0, unit);
1008
1009 path->setConstantRate(start, rate);
1010 LOG(logging::Level::DEBUG, "Added fixed rotation to platform {}", platform->getName());
1011 }
1012 catch (XmlException& e)
1013 {
1014 LOG(logging::Level::FATAL, "Failed to set fixed rotation for platform {}. {}", platform->getName(),
1015 e.what());
1016 throw XmlException("Failed to set fixed rotation for platform " + platform->getName());
1017 }
1018 }
Manages rotational paths with different interpolation techniques.
void setConstantRate(const RotationCoord &setstart, const RotationCoord &setrate) noexcept
Sets constant rate interpolation.
Represents a rotation in terms of azimuth, elevation, and time.
Definition coord.h:72

References serial::rotation_warning_utils::Angle, logging::DEBUG, serial::rotation_angle_utils::external_rotation_rate_to_internal(), serial::rotation_angle_utils::external_rotation_to_internal(), logging::FATAL, get_child_real_type(), LOG, max, serial::rotation_warning_utils::maybe_warn_about_rotation_value(), serial::rotation_warning_utils::Rate, and math::RotationPath::setConstantRate().

Referenced by parsePlatform().

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

◆ parseMonostatic()

void serial::xml_parser_utils::parseMonostatic ( const XmlElement monostatic,
radar::Platform platform,
ParserContext ctx,
const ReferenceLookup refs 
)

Parses a <monostatic> block, creating a linked transmitter and receiver pair.

Parameters
monostaticThe <monostatic> XML element.
platformThe platform this radar belongs to.
ctxThe current parser context.
refsLookup tables for resolving references.

Definition at line 1187 of file xml_parser_utils.cpp.

1189 {
1190 const std::string name = XmlElement::getSafeAttribute(monostatic, "name");
1191 const radar::OperationMode monostatic_mode = parse_mode_elements(monostatic, "Monostatic '" + name + "'");
1194 if (trans->getMode() != monostatic_mode || recv->getMode() != monostatic_mode)
1195 {
1196 throw XmlException("Monostatic '" + name + "' parsed inconsistent transmitter/receiver modes.");
1197 }
1198 if (trans->getSignal() != nullptr)
1199 {
1200 validate_waveform_mode_match(*trans->getSignal(), trans->getMode(),
1201 "Monostatic '" + trans->getName() + "'");
1202 }
1203 trans->setAttached(recv);
1204 recv->setAttached(trans);
1205 }
Manages radar signal reception and response processing.
Definition receiver.h:47
Represents a radar transmitter system.
Definition transmitter.h:34
OperationMode
Defines the operational mode of a radar component.
Definition radar_obj.h:39
static radar::Transmitter * parseTransmitterWithMode(const XmlElement &transmitter, radar::Platform *platform, ParserContext &ctx, const ReferenceLookup &refs, const radar::OperationMode mode)
Parses a transmitter after its operation mode has already been determined.
static radar::Receiver * parseReceiverWithMode(const XmlElement &receiver, radar::Platform *platform, ParserContext &ctx, const ReferenceLookup &refs, const radar::OperationMode mode)
Parses a receiver after its operation mode has already been determined.

References XmlElement::getSafeAttribute(), max, parseReceiverWithMode(), and parseTransmitterWithMode().

Referenced by parsePlatformElements().

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

◆ parseMotionPath()

void serial::xml_parser_utils::parseMotionPath ( const XmlElement motionPath,
radar::Platform platform 
)

Parses a <motionpath> block and attaches it to a Platform.

Parameters
motionPathThe <motionpath> XML element.
platformThe platform to modify.

Definition at line 857 of file xml_parser_utils.cpp.

858 {
859 math::Path* path = platform->getMotionPath();
860 if (const auto interp_value = XmlElement::getOptionalAttribute(motionPath, "interpolation"))
861 {
862 if (*interp_value == "linear")
863 {
865 }
866 else if (*interp_value == "cubic")
867 {
869 }
870 else if (*interp_value == "static")
871 {
873 }
874 else
875 {
876 LOG(logging::Level::ERROR, "Unsupported interpolation type: {} for platform {}. Defaulting to static",
877 *interp_value, platform->getName());
879 }
880 }
881 else
882 {
884 "MotionPath interpolation type for platform {} not specified. Defaulting to static.",
885 platform->getName());
887 }
888
889 unsigned waypoint_index = 0;
890 while (true)
891 {
892 XmlElement const waypoint = motionPath.childElement("positionwaypoint", waypoint_index);
893 if (!waypoint.isValid())
894 {
895 break;
896 }
897
898 try
899 {
903 get_child_real_type(waypoint, "altitude"));
904 path->addCoord(coord);
905 LOG(logging::Level::TRACE, "Added waypoint {} to motion path for platform {}.", waypoint_index,
906 platform->getName());
907 }
908 catch (const XmlException& e)
909 {
910 LOG(logging::Level::ERROR, "Failed to add waypoint to motion path. Discarding waypoint. {}", e.what());
911 }
912
914 }
915 path->finalize();
916 }
Represents a path with coordinates and allows for various interpolation methods.
Definition path.h:31
@ INTERP_STATIC
Hold the first coordinate for all query times.
@ INTERP_LINEAR
Linearly interpolate between neighboring coordinates.
@ INTERP_CUBIC
Cubically interpolate between neighboring coordinates.
void setInterp(InterpType settype) noexcept
Changes the interpolation type.
Definition path.cpp:164
void addCoord(const Coord &coord) noexcept
Adds a coordinate to the path.
Definition path.cpp:27
void finalize()
Finalizes the path, preparing it for interpolation.
Definition path.cpp:147
A class representing a vector in rectangular coordinates.
Represents a position in 3D space with an associated time.
Definition coord.h:24

References math::Path::addCoord(), logging::DEBUG, logging::ERROR, math::Path::finalize(), get_child_real_type(), XmlElement::getOptionalAttribute(), math::Path::INTERP_CUBIC, math::Path::INTERP_LINEAR, math::Path::INTERP_STATIC, LOG, max, math::Path::setInterp(), and logging::TRACE.

Referenced by parsePlatform().

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

◆ parseOptionalNumericParameters()

void serial::xml_parser_utils::parseOptionalNumericParameters ( const XmlElement parameters,
params::Parameters params_out 
)

Definition at line 449 of file xml_parser_utils.cpp.

450 {
452 [&](const RealType value)
453 {
454 params_out.c = value;
455 LOG(logging::Level::INFO, "Propagation speed (c) set to: {:.5f}", value);
456 });
457
458 setOptionalRealParameter(parameters, "simSamplingRate", 1000.0,
459 [&](const RealType value)
460 {
461 params_out.sim_sampling_rate = value;
462 LOG(logging::Level::DEBUG, "Simulation sampling rate set to: {:.5f} Hz", value);
463 });
464
465 if (parameters.childElement("randomseed", 0).isValid())
466 {
467 const auto seed = parseUnsignedParameter("randomseed", get_child_real_type(parameters, "randomseed"));
468 params_out.random_seed = seed;
469 LOG(logging::Level::DEBUG, "Random seed set to: {}", seed);
470 }
471
472 setOptionalUnsignedParameter(parameters, "adc_bits", 0,
473 [&](const unsigned value)
474 {
475 params_out.adc_bits = value;
476 LOG(logging::Level::DEBUG, "ADC quantization bits set to: {}", value);
477 });
478
479 setOptionalUnsignedParameter(parameters, "oversample", 1,
480 [&](const unsigned value)
481 {
483 params_out.oversample_ratio = value;
484 LOG(logging::Level::DEBUG, "Oversampling enabled with ratio: {}", value);
485 });
486 }
bool isValid() const noexcept
Check if the XML element is valid.
void validateOversampleRatio(const unsigned ratio)
Validates that an oversampling ratio is supported.
Definition parameters.h:164
void setOptionalUnsignedParameter(const XmlElement &parameters, const std::string &param_name, const unsigned default_value, Setter setter)
void setOptionalRealParameter(const XmlElement &parameters, const std::string &param_name, const RealType default_value, Setter setter)
static constexpr RealType DEFAULT_C
Speed of light (m/s)
Definition parameters.h:53

References XmlElement::childElement(), logging::DEBUG, params::Parameters::DEFAULT_C, get_child_real_type(), logging::INFO, XmlElement::isValid(), LOG, max, parseUnsignedParameter(), setOptionalRealParameter(), setOptionalUnsignedParameter(), and params::validateOversampleRatio().

Referenced by parseParameters().

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

◆ parseOriginParameter()

bool serial::xml_parser_utils::parseOriginParameter ( const XmlElement parameters,
params::Parameters params_out 
)

Definition at line 510 of file xml_parser_utils.cpp.

511 {
512 bool origin_set = false;
513 if (const XmlElement origin_element = parameters.childElement("origin", 0); origin_element.isValid())
514 {
515 try
516 {
517 params_out.origin_latitude = std::stod(XmlElement::getSafeAttribute(origin_element, "latitude"));
518 params_out.origin_longitude = std::stod(XmlElement::getSafeAttribute(origin_element, "longitude"));
519 if (const auto altitude = XmlElement::getOptionalAttribute(origin_element, "altitude"))
520 {
521 params_out.origin_altitude = std::stod(*altitude);
522 }
523 else
524 {
525 params_out.origin_altitude = 0.0;
526 LOG(logging::Level::DEBUG, "KML origin altitude not specified. Defaulting to 0.");
527 }
528 origin_set = true;
529 LOG(logging::Level::INFO, "KML origin set to lat: {}, lon: {}, alt: {}", params_out.origin_latitude,
530 params_out.origin_longitude, params_out.origin_altitude);
531 }
532 catch (const std::exception& e)
533 {
534 LOG(logging::Level::WARNING, "Could not parse KML origin from XML, using defaults. Error: {}",
535 e.what());
536 }
537 }
538 return origin_set;
539 }

References XmlElement::childElement(), logging::DEBUG, XmlElement::getOptionalAttribute(), XmlElement::getSafeAttribute(), logging::INFO, LOG, max, and logging::WARNING.

Referenced by parseParameters().

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

◆ parseParameters()

void serial::xml_parser_utils::parseParameters ( const XmlElement parameters,
params::Parameters params_out 
)

Parses the <parameters> block into the isolated context parameters.

Parameters
parametersThe <parameters> XML element.
params_outThe Parameters struct to mutate with parsed values.

Definition at line 612 of file xml_parser_utils.cpp.

613 {
614 params_out.start = get_child_real_type(parameters, "starttime");
615 params_out.end = get_child_real_type(parameters, "endtime");
616 LOG(logging::Level::INFO, "Simulation time set from {:.5f} to {:.5f} seconds", params_out.start,
617 params_out.end);
618
619 params_out.rate = get_child_real_type(parameters, "rate");
620 if (params_out.rate <= 0)
621 {
622 throw std::runtime_error("Sampling rate must be > 0");
623 }
624 LOG(logging::Level::DEBUG, "Sample rate set to: {:.5f}", params_out.rate);
625
626 parseOptionalNumericParameters(parameters, params_out);
627 parseRotationAngleUnit(parameters, params_out);
628 const bool origin_set = parseOriginParameter(parameters, params_out);
629 parseCoordinateSystemParameter(parameters, params_out, origin_set);
630 }

References logging::DEBUG, get_child_real_type(), logging::INFO, LOG, max, parseCoordinateSystemParameter(), parseOptionalNumericParameters(), parseOriginParameter(), and parseRotationAngleUnit().

Referenced by processParsedDocument().

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

◆ parsePlatform()

void serial::xml_parser_utils::parsePlatform ( const XmlElement platform,
ParserContext ctx,
const std::function< void(const XmlElement &, std::string_view)> &  register_name,
const ReferenceLookup refs 
)

Parses a complete <platform> block, including its motion paths and sub-elements.

Parameters
platformThe <platform> XML element.
ctxThe current parser context.
register_nameCallback used to ensure unique naming.
refsLookup tables for resolving references.

Definition at line 1295 of file xml_parser_utils.cpp.

1298 {
1299 std::string const name = XmlElement::getSafeAttribute(platform, "name");
1300 const SimId id = assign_id_from_attribute("platform '" + name + "'", ObjectType::Platform);
1301 auto plat = std::make_unique<radar::Platform>(name, id);
1302
1304
1305 if (const XmlElement motion_path = platform.childElement("motionpath", 0); motion_path.isValid())
1306 {
1308 }
1309
1310 const XmlElement rot_path = platform.childElement("rotationpath", 0);
1311 if (const XmlElement fixed_rot = platform.childElement("fixedrotation", 0);
1312 rot_path.isValid() && fixed_rot.isValid())
1313 {
1315 "Both <rotationpath> and <fixedrotation> are declared for platform {}. Only <rotationpath> will be "
1316 "used.",
1317 plat->getName());
1318 parseRotationPath(rot_path, plat.get(), ctx.parameters.rotation_angle_unit);
1319 }
1320 else if (rot_path.isValid())
1321 {
1322 parseRotationPath(rot_path, plat.get(), ctx.parameters.rotation_angle_unit);
1323 }
1324 else if (fixed_rot.isValid())
1325 {
1326 parseFixedRotation(fixed_rot, plat.get(), ctx.parameters.rotation_angle_unit);
1327 }
1328
1329 ctx.world->add(std::move(plat));
1330 }
void parseFixedRotation(const XmlElement &rotation, radar::Platform *platform, const params::RotationAngleUnit unit)
Parses a <fixedrotation> block and attaches it to a Platform.
void parsePlatformElements(const XmlElement &platform, ParserContext &ctx, radar::Platform *plat, const std::function< void(const XmlElement &, std::string_view)> &register_name, const ReferenceLookup &refs)
Iterates and parses all children elements (radars, targets) of a platform.
void parseMotionPath(const XmlElement &motionPath, radar::Platform *platform)
Parses a <motionpath> block and attaches it to a Platform.

References assign_id_from_attribute(), logging::ERROR, XmlElement::getSafeAttribute(), LOG, max, parseFixedRotation(), parseMotionPath(), parsePlatformElements(), parseRotationPath(), and Platform.

Referenced by processParsedDocument().

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

◆ parsePlatformElements()

void serial::xml_parser_utils::parsePlatformElements ( const XmlElement platform,
ParserContext ctx,
radar::Platform plat,
const std::function< void(const XmlElement &, std::string_view)> &  register_name,
const ReferenceLookup refs 
)

Iterates and parses all children elements (radars, targets) of a platform.

Parameters
platformThe <platform> XML element.
ctxThe current parser context.
platThe Platform object to attach parsed elements to.
register_nameCallback used to ensure unique naming globally across parsed objects.
refsLookup tables for resolving references.

Definition at line 1259 of file xml_parser_utils.cpp.

1262 {
1263 auto parseChildrenWithRefs = [&](const std::string& elementName, auto parseFunc)
1264 {
1265 unsigned index = 0;
1266 while (true)
1267 {
1268 const XmlElement element = platform.childElement(elementName, index++);
1269 if (!element.isValid())
1270 break;
1273 }
1274 };
1275
1276 auto parseChildrenWithoutRefs = [&](const std::string& elementName, auto parseFunc)
1277 {
1278 unsigned index = 0;
1279 while (true)
1280 {
1281 const XmlElement element = platform.childElement(elementName, index++);
1282 if (!element.isValid())
1283 break;
1286 }
1287 };
1288
1289 parseChildrenWithRefs("monostatic", parseMonostatic);
1290 parseChildrenWithRefs("transmitter", parseTransmitter);
1291 parseChildrenWithRefs("receiver", parseReceiver);
1292 parseChildrenWithoutRefs("target", parseTarget);
1293 }

References max, parseMonostatic(), parseReceiver(), parseTarget(), and parseTransmitter().

Referenced by parsePlatform().

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

◆ parseReceiver()

radar::Receiver * serial::xml_parser_utils::parseReceiver ( const XmlElement receiver,
radar::Platform platform,
ParserContext ctx,
const ReferenceLookup refs 
)

Parses a <receiver> block, resolves its dependencies, and adds it to the World.

Parameters
receiverThe <receiver> XML element.
platformThe platform this receiver belongs to.
ctxThe current parser context.
refsLookup tables for resolving antenna and timing references.
Returns
A pointer to the newly created Receiver object.

Definition at line 1179 of file xml_parser_utils.cpp.

1181 {
1182 const std::string name = XmlElement::getSafeAttribute(receiver, "name");
1183 const radar::OperationMode mode = parse_mode_elements(receiver, "Receiver '" + name + "'");
1185 }
const Receiver & receiver

References XmlElement::getSafeAttribute(), max, parseReceiverWithMode(), and receiver.

Referenced by parsePlatformElements().

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

◆ parseReceiverWithMode()

static radar::Receiver * serial::xml_parser_utils::parseReceiverWithMode ( const XmlElement receiver,
radar::Platform platform,
ParserContext ctx,
const ReferenceLookup refs,
const radar::OperationMode  mode 
)
static

Parses a receiver after its operation mode has already been determined.

Definition at line 1092 of file xml_parser_utils.cpp.

1095 {
1096 const std::string name = XmlElement::getSafeAttribute(receiver, "name");
1097 const SimId id = assign_id_from_attribute("receiver '" + name + "'", ObjectType::Receiver);
1098 const XmlElement pulsed_mode_element = receiver.childElement("pulsed_mode", 0);
1099 const bool is_pulsed = mode == radar::OperationMode::PULSED_MODE;
1100
1101 auto receiver_obj = std::make_unique<radar::Receiver>(platform, name, next_seed(*ctx.master_seeder), mode, id);
1102
1103 const SimId ant_id = resolve_reference_id(receiver, "antenna", "receiver '" + name + "'", *refs.antennas);
1104 const antenna::Antenna* antenna = ctx.world->findAntenna(ant_id);
1105 if (antenna == nullptr)
1106 {
1107 throw XmlException("Antenna ID '" + std::to_string(ant_id) + "' not found for receiver '" + name + "'");
1108 }
1109 receiver_obj->setAntenna(antenna);
1110
1111 if (!receiver.childElement("noise_temp", 0).isValid())
1112 {
1113 LOG(logging::Level::DEBUG, "Receiver '{}' does not specify noise temperature",
1114 receiver_obj->getName().c_str());
1115 }
1116 else
1117 {
1118 try
1119 {
1120 receiver_obj->setNoiseTemperature(get_child_real_type(receiver, "noise_temp"));
1121 }
1122 catch (const XmlException&)
1123 {
1124 LOG(logging::Level::WARNING, "Receiver '{}' has an empty noise temperature; using default.",
1125 receiver_obj->getName().c_str());
1126 }
1127 }
1128
1129 if (is_pulsed)
1130 {
1131 const RealType window_length = get_child_real_type(pulsed_mode_element, "window_length");
1132 if (window_length <= 0)
1133 {
1134 throw XmlException("<window_length> must be positive for receiver '" + name + "'");
1135 }
1136
1138 if (prf <= 0)
1139 {
1140 throw XmlException("<prf> must be positive for receiver '" + name + "'");
1141 }
1142
1143 const RealType window_skip = get_child_real_type(pulsed_mode_element, "window_skip");
1144 if (window_skip < 0)
1145 {
1146 throw XmlException("<window_skip> must not be negative for receiver '" + name + "'");
1147 }
1148 receiver_obj->setWindowProperties(window_length, prf, window_skip);
1149 }
1150 const SimId timing_id = resolve_reference_id(receiver, "timing", "receiver '" + name + "'", *refs.timings);
1151 receiver_obj->setTiming(resolve_timing_instance(timing_id, ctx, "receiver '" + name + "'"));
1152
1153 if (get_attribute_bool(receiver, "nodirect", false))
1154 {
1156 LOG(logging::Level::DEBUG, "Ignoring direct signals for receiver '{}'", receiver_obj->getName().c_str());
1157 }
1158
1159 if (get_attribute_bool(receiver, "nopropagationloss", false))
1160 {
1162 LOG(logging::Level::DEBUG, "Ignoring propagation losses for receiver '{}'",
1163 receiver_obj->getName().c_str());
1164 }
1165
1166 RealType const pri = is_pulsed ? (1.0 / receiver_obj->getWindowPrf()) : 0.0;
1168 if (!schedule.empty())
1169 {
1170 receiver_obj->setSchedule(std::move(schedule));
1171 }
1172
1173 parse_receiver_dechirp_config(receiver, *receiver_obj, "Receiver '" + name + "'");
1174
1175 ctx.world->add(std::move(receiver_obj));
1176 return ctx.world->getReceivers().back().get();
1177 }
Abstract base class representing an antenna.
@ FLAG_NODIRECT
Disable direct-path reception.
@ FLAG_NOPROPLOSS
Disable propagation-loss scaling.
@ PULSED_MODE
The component operates in a pulsed mode.
std::vector< radar::SchedulePeriod > parseSchedule(const XmlElement &parent, const std::string &parentName, const bool isPulsed, const RealType pri)
Parses a schedule (active periods) for a transmitter or receiver.
SimId resolve_reference_id(const XmlElement &element, const std::string &attributeName, const std::string &owner, const std::unordered_map< std::string, SimId > &name_map)
Resolves an XML string reference into an internal SimId.
bool get_attribute_bool(const XmlElement &element, const std::string &attributeName, const bool defaultVal)
Extracts a boolean value from a named attribute.

References assign_id_from_attribute(), logging::DEBUG, radar::Receiver::FLAG_NODIRECT, radar::Receiver::FLAG_NOPROPLOSS, get_attribute_bool(), get_child_real_type(), XmlElement::getSafeAttribute(), LOG, max, parseSchedule(), radar::PULSED_MODE, Receiver, receiver, resolve_reference_id(), and logging::WARNING.

Referenced by parseMonostatic(), and parseReceiver().

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

◆ parseRotationAngleUnit()

void serial::xml_parser_utils::parseRotationAngleUnit ( const XmlElement parameters,
params::Parameters params_out 
)

Definition at line 488 of file xml_parser_utils.cpp.

489 {
490 try
491 {
492 const auto unit_token = parameters.childElement("rotationangleunit", 0).getText();
493 if (!unit_token.empty())
494 {
495 if (const auto unit = params::rotationAngleUnitFromToken(unit_token))
496 {
497 params_out.rotation_angle_unit = *unit;
498 }
499 else
500 {
501 throw XmlException("Unsupported rotation angle unit '" + unit_token + "'.");
502 }
503 }
504 }
505 catch (const XmlException&)
506 {
507 }
508 }
std::string getText() const
Get the text content of the XML element.
std::optional< RotationAngleUnit > rotationAngleUnitFromToken(const std::string_view token) noexcept
Parses a rotation angle unit from an XML token.
Definition parameters.h:362

References XmlElement::childElement(), XmlElement::getText(), max, and params::rotationAngleUnitFromToken().

Referenced by parseParameters().

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

◆ parseRotationPath()

void serial::xml_parser_utils::parseRotationPath ( const XmlElement rotation,
radar::Platform platform,
params::RotationAngleUnit  unit 
)

Parses a <rotationpath> block and attaches it to a Platform.

Parameters
rotationThe <rotationpath> XML element.
platformThe platform to modify.

Definition at line 918 of file xml_parser_utils.cpp.

919 {
920 math::RotationPath* path = platform->getRotationPath();
921 try
922 {
923 if (const std::string interp = XmlElement::getSafeAttribute(rotation, "interpolation"); interp == "linear")
924 {
926 }
927 else if (interp == "cubic")
928 {
930 }
931 else if (interp == "static")
932 {
934 }
935 else
936 {
937 throw XmlException("Unsupported interpolation type: " + interp);
938 }
939 }
940 catch (XmlException&)
941 {
943 "Failed to set RotationPath interpolation type for platform {}. Defaulting to static",
944 platform->getName());
946 }
947
948 unsigned waypoint_index = 0;
949 while (true)
950 {
951 XmlElement const waypoint = rotation.childElement("rotationwaypoint", waypoint_index);
952 if (!waypoint.isValid())
953 {
954 break;
955 }
956
957 try
958 {
959 LOG(logging::Level::TRACE, "Adding waypoint {} to rotation path for platform {}.", waypoint_index,
960 platform->getName());
961
962 const RealType az_deg = get_child_real_type(waypoint, "azimuth");
963 const RealType el_deg = get_child_real_type(waypoint, "elevation");
964 const RealType time = get_child_real_type(waypoint, "time");
965 const std::string owner =
966 std::format("platform '{}' rotation waypoint {}", platform->getName(), waypoint_index);
967
968 rotation_warning_utils::maybe_warn_about_rotation_value(
969 az_deg, unit, rotation_warning_utils::ValueKind::Angle, "XML", owner, "azimuth");
970 rotation_warning_utils::maybe_warn_about_rotation_value(
971 el_deg, unit, rotation_warning_utils::ValueKind::Angle, "XML", owner, "elevation");
972
973 path->addCoord(rotation_angle_utils::external_rotation_to_internal(az_deg, el_deg, time, unit));
974 }
975 catch (const XmlException& e)
976 {
977 LOG(logging::Level::ERROR, "Failed to add waypoint to rotation path. Discarding waypoint. {}",
978 e.what());
979 }
981 }
982 path->finalize();
983 }
void finalize()
Finalizes the rotation path for interpolation.
void setInterp(InterpType setinterp) noexcept
Sets the interpolation type for the path.
void addCoord(const RotationCoord &coord) noexcept
Adds a rotation coordinate to the path.
@ INTERP_STATIC
Hold the first rotation for all query times.
@ INTERP_LINEAR
Linearly interpolate between neighboring rotations.
@ INTERP_CUBIC
Cubically interpolate between neighboring rotations.

References math::RotationPath::addCoord(), serial::rotation_warning_utils::Angle, logging::ERROR, serial::rotation_angle_utils::external_rotation_to_internal(), math::RotationPath::finalize(), get_child_real_type(), XmlElement::getSafeAttribute(), math::RotationPath::INTERP_CUBIC, math::RotationPath::INTERP_LINEAR, math::RotationPath::INTERP_STATIC, LOG, max, serial::rotation_warning_utils::maybe_warn_about_rotation_value(), math::RotationPath::setInterp(), and logging::TRACE.

Referenced by parsePlatform().

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

◆ parseSchedule()

std::vector< radar::SchedulePeriod > serial::xml_parser_utils::parseSchedule ( const XmlElement parent,
const std::string &  parentName,
bool  isPulsed,
RealType  pri = 0.0 
)

Parses a schedule (active periods) for a transmitter or receiver.

Parameters
parentThe parent XML element that might contain a <schedule> block.
parentNameName of the parent for error logging.
isPulsedTrue if the owning object operates in pulsed mode (used for PRI validation).
priThe pulse repetition interval, if applicable.
Returns
A vector of parsed and validated SchedulePeriod objects.

Definition at line 372 of file xml_parser_utils.cpp.

374 {
375 std::vector<radar::SchedulePeriod> raw_periods;
376 if (const XmlElement schedule_element = parent.childElement("schedule", 0); schedule_element.isValid())
377 {
378 unsigned p_idx = 0;
379 while (true)
380 {
381 XmlElement const period_element = schedule_element.childElement("period", p_idx++);
382 if (!period_element.isValid())
383 {
384 break;
385 }
386 try
387 {
388 const RealType start = std::stod(XmlElement::getSafeAttribute(period_element, "start"));
389 const RealType end = std::stod(XmlElement::getSafeAttribute(period_element, "end"));
390 raw_periods.push_back({start, end});
391 }
392 catch (const std::exception& e)
393 {
394 LOG(logging::Level::WARNING, "Failed to parse schedule period for '{}': {}", parentName, e.what());
395 }
396 }
397 }
399 }
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 XmlElement::getSafeAttribute(), LOG, max, radar::processRawSchedule(), and logging::WARNING.

Referenced by parseReceiverWithMode(), and parseTransmitterWithMode().

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

◆ parseTarget()

void serial::xml_parser_utils::parseTarget ( const XmlElement target,
radar::Platform platform,
ParserContext ctx 
)

Parses a <target> block and adds it to the World.

Parameters
targetThe <target> XML element.
platformThe platform this target belongs to.
ctxThe current parser context.

Definition at line 1207 of file xml_parser_utils.cpp.

1208 {
1209 const std::string name = XmlElement::getSafeAttribute(target, "name");
1210 const SimId id = assign_id_from_attribute("target '" + name + "'", ObjectType::Target);
1211
1212 const XmlElement rcs_element = target.childElement("rcs", 0);
1213 if (!rcs_element.isValid())
1214 {
1215 throw XmlException("<rcs> element is required in <target>!");
1216 }
1217
1218 const std::string rcs_type = XmlElement::getSafeAttribute(rcs_element, "type");
1219 std::unique_ptr<radar::Target> target_obj;
1220 const unsigned seed = next_seed(*ctx.master_seeder);
1221
1222 if (rcs_type == "isotropic")
1223 {
1224 target_obj = radar::createIsoTarget(platform, name, get_child_real_type(rcs_element, "value"), seed, id);
1225 }
1226 else if (rcs_type == "file")
1227 {
1228 // Defer to dependency-injected file loader
1229 target_obj = ctx.loaders.loadFileTarget(platform, name,
1231 }
1232 else
1233 {
1234 throw XmlException("Unsupported RCS type: " + rcs_type);
1235 }
1236
1237 if (const XmlElement model = target.childElement("model", 0); model.isValid())
1238 {
1239 if (const std::string model_type = XmlElement::getSafeAttribute(model, "type"); model_type == "constant")
1240 {
1241 target_obj->setFluctuationModel(std::make_unique<radar::RcsConst>());
1242 }
1243 else if (model_type == "chisquare" || model_type == "gamma")
1244 {
1245 target_obj->setFluctuationModel(
1246 std::make_unique<radar::RcsChiSquare>(target_obj->getRngEngine(), get_child_real_type(model, "k")));
1247 }
1248 else
1249 {
1250 throw XmlException("Unsupported model type: " + model_type);
1251 }
1252 }
1253
1254 LOG(logging::Level::DEBUG, "Added target {} with RCS type {} to platform {}", name, rcs_type,
1255 platform->getName());
1256 ctx.world->add(std::move(target_obj));
1257 }
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

References assign_id_from_attribute(), radar::createIsoTarget(), logging::DEBUG, get_child_real_type(), XmlElement::getSafeAttribute(), LOG, max, and Target.

Referenced by parsePlatformElements().

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

◆ parseTiming()

void serial::xml_parser_utils::parseTiming ( const XmlElement timing,
ParserContext ctx 
)

Parses a <timing> block and adds the prototype timing to the World.

Parameters
timingThe <timing> XML element.
ctxThe current parser context.

Definition at line 735 of file xml_parser_utils.cpp.

736 {
737 const std::string name = XmlElement::getSafeAttribute(timing, "name");
738 const SimId id = assign_id_from_attribute("timing '" + name + "'", ObjectType::Timing);
739 const RealType freq = get_child_real_type(timing, "frequency");
740 auto timing_obj = std::make_unique<timing::PrototypeTiming>(name, id);
741
742 timing_obj->setFrequency(freq);
743
744 unsigned noise_index = 0;
745 while (true)
746 {
747 XmlElement const noise_element = timing.childElement("noise_entry", noise_index++);
748 if (!noise_element.isValid())
749 {
750 break;
751 }
752
753 timing_obj->setAlpha(get_child_real_type(noise_element, "alpha"),
754 get_child_real_type(noise_element, "weight"));
755 }
756
758 [&](const std::string& element_name, const std::string& description, auto setter)
759 {
760 if (!timing.childElement(element_name, 0).isValid())
761 {
762 LOG(logging::Level::DEBUG, "Clock section '{}' does not specify {}.", name, description);
763 return;
764 }
765 try
766 {
768 }
769 catch (const XmlException&)
770 {
771 LOG(logging::Level::WARNING, "Clock section '{}' has an empty {}. Using default.", name, description);
772 }
773 };
774
775 set_optional_timing_parameter("freq_offset", "frequency offset",
776 [&](const RealType value) { timing_obj->setFreqOffset(value); });
777 set_optional_timing_parameter("random_freq_offset_stdev", "random frequency offset",
778 [&](const RealType value) { timing_obj->setRandomFreqOffsetStdev(value); });
779 set_optional_timing_parameter("phase_offset", "phase offset",
780 [&](const RealType value) { timing_obj->setPhaseOffset(value); });
781 set_optional_timing_parameter("random_phase_offset_stdev", "random phase offset",
782 [&](const RealType value) { timing_obj->setRandomPhaseOffsetStdev(value); });
783
784 if (get_attribute_bool(timing, "synconpulse", false))
785 {
786 timing_obj->setSyncOnPulse();
787 }
788
789 ctx.world->add(std::move(timing_obj));
790 }

References assign_id_from_attribute(), logging::DEBUG, get_attribute_bool(), get_child_real_type(), XmlElement::getSafeAttribute(), LOG, max, Timing, and logging::WARNING.

Referenced by processParsedDocument().

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

◆ parseTransmitter()

radar::Transmitter * serial::xml_parser_utils::parseTransmitter ( const XmlElement transmitter,
radar::Platform platform,
ParserContext ctx,
const ReferenceLookup refs 
)

Parses a <transmitter> block, resolves its dependencies, and adds it to the World.

Parameters
transmitterThe <transmitter> XML element.
platformThe platform this transmitter belongs to.
ctxThe current parser context.
refsLookup tables for resolving waveform, antenna, and timing references.
Returns
A pointer to the newly created Transmitter object.

Definition at line 1078 of file xml_parser_utils.cpp.

1080 {
1081 const std::string name = XmlElement::getSafeAttribute(transmitter, "name");
1082 const radar::OperationMode mode = parse_mode_elements(transmitter, "Transmitter '" + name + "'");
1083 if (const XmlElement fmcw_mode = transmitter.childElement("fmcw_mode", 0);
1085 {
1086 throw XmlException("Transmitter '" + name + "' fmcw_mode must not contain dechirp configuration.");
1087 }
1088 return parseTransmitterWithMode(transmitter, platform, ctx, refs, mode);
1089 }
const Transmitter & transmitter

References XmlElement::getSafeAttribute(), max, parseTransmitterWithMode(), and transmitter.

Referenced by parsePlatformElements().

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

◆ parseTransmitterWithMode()

static radar::Transmitter * serial::xml_parser_utils::parseTransmitterWithMode ( const XmlElement transmitter,
radar::Platform platform,
ParserContext ctx,
const ReferenceLookup refs,
const radar::OperationMode  mode 
)
static

Parses a transmitter after its operation mode has already been determined.

Definition at line 1021 of file xml_parser_utils.cpp.

1024 {
1025 const std::string name = XmlElement::getSafeAttribute(transmitter, "name");
1026 const SimId id = assign_id_from_attribute("transmitter '" + name + "'", ObjectType::Transmitter);
1027 const XmlElement pulsed_mode_element = transmitter.childElement("pulsed_mode", 0);
1028 const bool is_pulsed = mode == radar::OperationMode::PULSED_MODE;
1029
1030 auto transmitter_obj = std::make_unique<radar::Transmitter>(platform, name, mode, id);
1031
1032 const SimId waveform_id =
1033 resolve_reference_id(transmitter, "waveform", "transmitter '" + name + "'", *refs.waveforms);
1034 fers_signal::RadarSignal* wave = ctx.world->findWaveform(waveform_id);
1035 if (wave == nullptr)
1036 {
1037 throw XmlException("Waveform ID '" + std::to_string(waveform_id) + "' not found for transmitter '" + name +
1038 "'");
1039 }
1040 validate_fmcw_waveform(*wave, "Waveform '" + wave->getName() + "'");
1041 validate_waveform_mode_match(*wave, mode, "Transmitter '" + name + "'");
1042 transmitter_obj->setWave(wave);
1043
1044 if (is_pulsed)
1045 {
1046 transmitter_obj->setPrf(get_child_real_type(pulsed_mode_element, "prf"));
1047 }
1048
1049 const SimId antenna_id =
1050 resolve_reference_id(transmitter, "antenna", "transmitter '" + name + "'", *refs.antennas);
1051 const antenna::Antenna* ant = ctx.world->findAntenna(antenna_id);
1052 if (ant == nullptr)
1053 {
1054 throw XmlException("Antenna ID '" + std::to_string(antenna_id) + "' not found for transmitter '" + name +
1055 "'");
1056 }
1057 transmitter_obj->setAntenna(ant);
1058
1059 const SimId timing_id =
1060 resolve_reference_id(transmitter, "timing", "transmitter '" + name + "'", *refs.timings);
1061 transmitter_obj->setTiming(resolve_timing_instance(timing_id, ctx, "transmitter '" + name + "'"));
1062
1063 RealType const pri = is_pulsed ? (1.0 / transmitter_obj->getPrf()) : 0.0;
1065 if (wave->isFmcwFamily())
1066 {
1067 validate_fmcw_schedule(schedule, *wave, "Transmitter '" + name + "'");
1068 }
1069 if (!schedule.empty())
1070 {
1071 transmitter_obj->setSchedule(std::move(schedule));
1072 }
1073
1074 ctx.world->add(std::move(transmitter_obj));
1075 return ctx.world->getTransmitters().back().get();
1076 }
Class representing a radar signal with associated properties.

References assign_id_from_attribute(), get_child_real_type(), XmlElement::getSafeAttribute(), max, parseSchedule(), radar::PULSED_MODE, resolve_reference_id(), Transmitter, and transmitter.

Referenced by parseMonostatic(), and parseTransmitter().

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

◆ parseUnsignedParameter()

unsigned serial::xml_parser_utils::parseUnsignedParameter ( const std::string_view  param_name,
const RealType  raw_value 
)

Definition at line 401 of file xml_parser_utils.cpp.

402 {
403 if (!std::isfinite(raw_value))
404 {
405 throw XmlException(std::format("Parameter '{}' must be finite.", param_name));
406 }
407 if (raw_value < 0.0)
408 {
409 throw XmlException(std::format("Parameter '{}' must be non-negative.", param_name));
410 }
411
412 const RealType floored_value = std::floor(raw_value);
413 if (floored_value > static_cast<RealType>(std::numeric_limits<unsigned>::max()))
414 {
415 throw XmlException(std::format("Parameter '{}' exceeds the supported unsigned range.", param_name));
416 }
417
418 return static_cast<unsigned>(floored_value);
419 }

References max.

Referenced by parseOptionalNumericParameters(), and setOptionalUnsignedParameter().

+ Here is the caller graph for this function:

◆ parseUtmCoordinateSystem()

void serial::xml_parser_utils::parseUtmCoordinateSystem ( const XmlElement cs_element,
params::Parameters params_out 
)

Definition at line 541 of file xml_parser_utils.cpp.

542 {
543 params_out.coordinate_frame = params::CoordinateFrame::UTM;
544 params_out.utm_zone = std::stoi(XmlElement::getSafeAttribute(cs_element, "zone"));
545 const std::string hem_str = XmlElement::getSafeAttribute(cs_element, "hemisphere");
546
547 if (params_out.utm_zone < GeographicLib::UTMUPS::MINUTMZONE ||
548 params_out.utm_zone > GeographicLib::UTMUPS::MAXUTMZONE)
549 {
550 throw XmlException("KML UTM zone " + std::to_string(params_out.utm_zone) +
551 " is invalid; must be in [1, 60].");
552 }
553 if (hem_str == "N" || hem_str == "n")
554 {
555 params_out.utm_north_hemisphere = true;
556 }
557 else if (hem_str == "S" || hem_str == "s")
558 {
559 params_out.utm_north_hemisphere = false;
560 }
561 else
562 {
563 throw XmlException("KML UTM hemisphere '" + hem_str + "' is invalid; must be 'N' or 'S'.");
564 }
565 LOG(logging::Level::INFO, "KML coordinate system set to UTM, zone {}{}", params_out.utm_zone,
566 params_out.utm_north_hemisphere ? 'N' : 'S');
567 }
@ UTM
Universal Transverse Mercator.

References XmlElement::getSafeAttribute(), logging::INFO, LOG, max, and params::UTM.

Referenced by parseCoordinateSystemParameter().

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

◆ parseWaveform()

void serial::xml_parser_utils::parseWaveform ( const XmlElement waveform,
ParserContext ctx 
)

Parses a <waveform> block and adds it to the World.

Parameters
waveformThe <waveform> XML element.
ctxThe current parser context.

Definition at line 632 of file xml_parser_utils.cpp.

633 {
634 const std::string name = XmlElement::getSafeAttribute(waveform, "name");
635 const SimId id = assign_id_from_attribute("waveform '" + name + "'", ObjectType::Waveform);
636
637 const auto power = get_child_real_type(waveform, "power");
638 const auto carrier = get_child_real_type(waveform, "carrier_frequency");
639
640 if (const XmlElement pulsed_file = waveform.childElement("pulsed_from_file", 0); pulsed_file.isValid())
641 {
642 const std::string filename_str = XmlElement::getSafeAttribute(pulsed_file, "filename");
643 fs::path pulse_path(filename_str);
644
645 if (!fs::exists(pulse_path))
646 {
647 pulse_path = ctx.base_dir / filename_str;
648 }
649
650 // Defer to dependency-injected file loader
651 auto wave = ctx.loaders.loadWaveform(name, pulse_path, power, carrier, id);
652 ctx.world->add(std::move(wave));
653 }
654 else if (waveform.childElement("cw", 0).isValid())
655 {
656 auto cw_signal = std::make_unique<fers_signal::CwSignal>();
657 auto wave = std::make_unique<fers_signal::RadarSignal>(
658 name, power, carrier, ctx.parameters.end - ctx.parameters.start, std::move(cw_signal), id);
659 ctx.world->add(std::move(wave));
660 }
661 else if (const XmlElement fmcw_element = waveform.childElement("fmcw_linear_chirp", 0); fmcw_element.isValid())
662 {
663 const auto direction =
665 const RealType chirp_bandwidth = get_child_real_type(fmcw_element, "chirp_bandwidth");
666 const RealType chirp_duration = get_child_real_type(fmcw_element, "chirp_duration");
667 const RealType chirp_period = get_child_real_type(fmcw_element, "chirp_period");
668
669 RealType start_frequency_offset = 0.0;
670 if (const auto start_offset = fmcw_element.childElement("start_frequency_offset", 0);
671 start_offset.isValid())
672 {
673 start_frequency_offset = get_child_real_type(fmcw_element, "start_frequency_offset");
674 }
675
676 std::optional<std::size_t> chirp_count;
677 if (const auto chirp_count_element = fmcw_element.childElement("chirp_count", 0);
678 chirp_count_element.isValid())
679 {
680 const RealType raw_count = get_child_real_type(fmcw_element, "chirp_count");
681 if (raw_count <= 0.0 || std::floor(raw_count) != raw_count)
682 {
683 throw XmlException("Waveform '" + name + "' has an invalid chirp_count.");
684 }
685 chirp_count = static_cast<std::size_t>(raw_count);
686 }
687
688 auto fmcw_signal = std::make_unique<fers_signal::FmcwChirpSignal>(
689 chirp_bandwidth, chirp_duration, chirp_period, start_frequency_offset, chirp_count, direction);
690 // RadarSignal length is the active chirp duration, not T_rep. The repeat period only spaces chirps.
691 auto wave = std::make_unique<fers_signal::RadarSignal>(name, power, carrier, chirp_duration,
692 std::move(fmcw_signal), id);
693 validate_fmcw_waveform(*wave, "Waveform '" + name + "'");
694 ctx.world->add(std::move(wave));
695 }
696 else if (const XmlElement fmcw_triangle_element = waveform.childElement("fmcw_triangle", 0);
697 fmcw_triangle_element.isValid())
698 {
699 const RealType chirp_bandwidth = get_child_real_type(fmcw_triangle_element, "chirp_bandwidth");
700 const RealType chirp_duration = get_child_real_type(fmcw_triangle_element, "chirp_duration");
701
702 RealType start_frequency_offset = 0.0;
703 if (const auto start_offset = fmcw_triangle_element.childElement("start_frequency_offset", 0);
704 start_offset.isValid())
705 {
706 start_frequency_offset = get_child_real_type(fmcw_triangle_element, "start_frequency_offset");
707 }
708
709 std::optional<std::size_t> triangle_count;
710 if (const auto triangle_count_element = fmcw_triangle_element.childElement("triangle_count", 0);
711 triangle_count_element.isValid())
712 {
714 if (raw_count <= 0.0 || std::floor(raw_count) != raw_count)
715 {
716 throw XmlException("Waveform '" + name + "' has an invalid triangle_count.");
717 }
718 triangle_count = static_cast<std::size_t>(raw_count);
719 }
720
721 auto fmcw_signal = std::make_unique<fers_signal::FmcwTriangleSignal>(
722 chirp_bandwidth, chirp_duration, start_frequency_offset, triangle_count);
723 auto wave = std::make_unique<fers_signal::RadarSignal>(
724 name, power, carrier, fmcw_signal->getTrianglePeriod(), std::move(fmcw_signal), id);
725 validate_fmcw_waveform(*wave, "Waveform '" + name + "'");
726 ctx.world->add(std::move(wave));
727 }
728 else
729 {
730 LOG(logging::Level::FATAL, "Unsupported waveform type for '{}'", name);
731 throw XmlException("Unsupported waveform type for '" + name + "'");
732 }
733 }
FmcwChirpDirection parseFmcwChirpDirection(const std::string_view direction)
Parses a schema chirp direction token.

References assign_id_from_attribute(), XmlElement::childElement(), logging::FATAL, get_child_real_type(), XmlElement::getSafeAttribute(), XmlElement::isValid(), LOG, max, fers_signal::parseFmcwChirpDirection(), and Waveform.

Referenced by processParsedDocument().

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

◆ processParsedDocument()

void serial::xml_parser_utils::processParsedDocument ( const XmlDocument doc,
ParserContext ctx 
)

Coordinates the full parsing of a validated XML document tree.

This is the root parsing function that iterates over parameters, waveforms, timings, antennas, and platforms. It populates the World in the proper order and triggers initial event scheduling.

Parameters
docThe parsed XML document tree.
ctxThe parser context containing the World, isolated parameters, and asset loaders.

Definition at line 1402 of file xml_parser_utils.cpp.

1403 {
1404 const XmlElement root = doc.getRootElement();
1405 if (root.name() != "simulation")
1406 {
1407 throw XmlException("Root element is not <simulation>!");
1408 }
1409
1410 std::unordered_map<std::string, std::string> name_registry;
1411 name_registry.reserve(64); // TODO: reserve 64?
1412 const auto register_name = [&](const XmlElement& element, const std::string_view kind)
1413 {
1414 const std::string name = XmlElement::getSafeAttribute(element, "name");
1415 const auto [iter, inserted] = name_registry.emplace(name, std::string(kind));
1416 if (!inserted)
1417 {
1418 throw XmlException("Duplicate name '" + name + "' found for " + std::string(kind) +
1419 "; previously used by " + iter->second + ".");
1420 }
1421 };
1422
1423 try
1424 {
1425 ctx.parameters.simulation_name = XmlElement::getSafeAttribute(root, "name");
1426 if (!ctx.parameters.simulation_name.empty())
1427 {
1428 LOG(logging::Level::INFO, "Simulation name set to: {}", ctx.parameters.simulation_name);
1429 }
1430 }
1431 catch (const XmlException&)
1432 {
1433 LOG(logging::Level::WARNING, "No 'name' attribute found in <simulation> tag. KML name will default.");
1434 }
1435
1436 parseParameters(root.childElement("parameters", 0), ctx.parameters);
1437
1438 params::params = ctx.parameters;
1439
1440 auto parseElements =
1441 [](const XmlElement& parent, const std::string& elementName, ParserContext& parser_ctx, auto parseFunction)
1442 {
1443 unsigned index = 0;
1444 while (true)
1445 {
1446 XmlElement const element = parent.childElement(elementName, index++);
1447 if (!element.isValid())
1448 break;
1450 }
1451 };
1452
1453 parseElements(root, "waveform", ctx,
1454 [&](const XmlElement& p, ParserContext& c)
1455 {
1456 register_name(p, "waveform");
1457 parseWaveform(p, c);
1458 });
1459
1460 parseElements(root, "timing", ctx,
1461 [&](const XmlElement& p, ParserContext& c)
1462 {
1463 register_name(p, "timing");
1464 parseTiming(p, c);
1465 });
1466
1467 parseElements(root, "antenna", ctx,
1468 [&](const XmlElement& p, ParserContext& c)
1469 {
1470 register_name(p, "antenna");
1471 parseAntenna(p, c);
1472 });
1473
1474 std::unordered_map<std::string, SimId> waveform_refs;
1475 std::unordered_map<std::string, SimId> antenna_refs;
1476 std::unordered_map<std::string, SimId> timing_refs;
1477 waveform_refs.reserve(ctx.world->getWaveforms().size());
1478 antenna_refs.reserve(ctx.world->getAntennas().size());
1479 timing_refs.reserve(ctx.world->getTimings().size());
1480
1481 for (const auto& [id, waveform] : ctx.world->getWaveforms())
1482 waveform_refs.emplace(waveform->getName(), id);
1483 for (const auto& [id, antenna] : ctx.world->getAntennas())
1484 antenna_refs.emplace(antenna->getName(), id);
1485 for (const auto& [id, timing] : ctx.world->getTimings())
1486 timing_refs.emplace(timing->getName(), id);
1487
1488 const ReferenceLookup refs{&waveform_refs, &antenna_refs, &timing_refs};
1489
1490 parseElements(root, "platform", ctx,
1491 [&](const XmlElement& p, ParserContext& c)
1492 {
1493 register_name(p, "platform");
1495 });
1496
1497 ctx.world->resolveReceiverDechirpReferences();
1498
1499 ctx.world->scheduleInitialEvents();
1500
1501 LOG(logging::Level::DEBUG, "Initial Event Queue State:\n{}", ctx.world->dumpEventQueue());
1502 }
Parameters params
Global simulation parameter state.
Definition parameters.h:85
void parseAntenna(const XmlElement &antenna, ParserContext &ctx)
Parses an <antenna> block and adds it to the World.
void parseWaveform(const XmlElement &waveform, ParserContext &ctx)
Parses a <waveform> block and adds it to the World.
void parseTiming(const XmlElement &timing, ParserContext &ctx)
Parses a <timing> block and adds the prototype timing to the World.
void parseParameters(const XmlElement &parameters, params::Parameters &params_out)
Parses the <parameters> block into the isolated context parameters.
void parsePlatform(const XmlElement &platform, ParserContext &ctx, const std::function< void(const XmlElement &, std::string_view)> &register_name, const ReferenceLookup &refs)
Parses a complete <platform> block, including its motion paths and sub-elements.
RealType c

References c, logging::DEBUG, XmlElement::getSafeAttribute(), logging::INFO, LOG, max, params::params, parseAntenna(), parseParameters(), parsePlatform(), parseTiming(), parseWaveform(), and logging::WARNING.

Referenced by serial::parseSimulation(), and serial::parseSimulationFromString().

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

◆ resolve_reference_id()

SimId serial::xml_parser_utils::resolve_reference_id ( const XmlElement element,
const std::string &  attributeName,
const std::string &  owner,
const std::unordered_map< std::string, SimId > &  name_map 
)

Resolves an XML string reference into an internal SimId.

Parameters
elementThe XML element containing the string reference attribute.
attributeNameThe name of the attribute containing the reference string.
ownerA description of the object making the reference (used for error messages).
name_mapThe lookup table mapping string names to SimIds.
Returns
The resolved SimId.
Exceptions
XmlExceptionif the reference cannot be resolved or is missing.

Definition at line 356 of file xml_parser_utils.cpp.

358 {
359 const std::string value = XmlElement::getSafeAttribute(element, attributeName);
360 if (value.empty())
361 {
362 throw XmlException("Missing " + attributeName + " for " + owner + ".");
363 }
364 const auto it = name_map.find(value);
365 if (it != name_map.end())
366 {
367 return it->second;
368 }
369 throw XmlException("Unknown " + attributeName + " '" + value + "' for " + owner + ".");
370 }

References XmlElement::getSafeAttribute(), and max.

Referenced by parseReceiverWithMode(), and parseTransmitterWithMode().

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

◆ setOptionalRealParameter()

template<typename Setter >
void serial::xml_parser_utils::setOptionalRealParameter ( const XmlElement parameters,
const std::string &  param_name,
const RealType  default_value,
Setter  setter 
)

Definition at line 422 of file xml_parser_utils.cpp.

424 {
425 if (!parameters.childElement(param_name, 0).isValid())
426 {
427 LOG(logging::Level::DEBUG, "Parameter '{}' not specified. Using default value {}.", param_name,
429 return;
430 }
431
432 setter(get_child_real_type(parameters, param_name));
433 }

References XmlElement::childElement(), logging::DEBUG, get_child_real_type(), XmlElement::isValid(), LOG, and max.

Referenced by parseOptionalNumericParameters().

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

◆ setOptionalUnsignedParameter()

template<typename Setter >
void serial::xml_parser_utils::setOptionalUnsignedParameter ( const XmlElement parameters,
const std::string &  param_name,
const unsigned  default_value,
Setter  setter 
)

Definition at line 436 of file xml_parser_utils.cpp.

438 {
439 if (!parameters.childElement(param_name, 0).isValid())
440 {
441 LOG(logging::Level::DEBUG, "Parameter '{}' not specified. Using default value {}.", param_name,
443 return;
444 }
445
446 setter(parseUnsignedParameter(param_name, get_child_real_type(parameters, param_name)));
447 }

References XmlElement::childElement(), logging::DEBUG, get_child_real_type(), XmlElement::isValid(), LOG, max, and parseUnsignedParameter().

Referenced by parseOptionalNumericParameters().

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

◆ validateXml()

void serial::xml_parser_utils::validateXml ( bool  didCombine,
const XmlDocument mainDoc 
)

Validates an XML document against the embedded DTD and XSD schemas.

Parameters
didCombineFlag indicating whether the document contains merged includes (used for formatting log messages).
mainDocThe XML document to validate.
Exceptions
XmlExceptionif validation fails.

Definition at line 1384 of file xml_parser_utils.cpp.

1385 {
1386 LOG(logging::Level::DEBUG, "Validating the{}XML file...", didCombine ? " combined " : " ");
1387 if (!mainDoc.validateWithDtd(fers_xml_dtd))
1388 {
1389 LOG(logging::Level::FATAL, "{} XML file failed DTD validation!", didCombine ? "Combined" : "Main");
1390 throw XmlException("XML file failed DTD validation!");
1391 }
1392 LOG(logging::Level::DEBUG, "{} XML file passed DTD validation.", didCombine ? "Combined" : "Main");
1393
1394 if (!mainDoc.validateWithXsd(fers_xml_xsd))
1395 {
1396 LOG(logging::Level::FATAL, "{} XML file failed XSD validation!", didCombine ? "Combined" : "Main");
1397 throw XmlException("XML file failed XSD validation!");
1398 }
1399 LOG(logging::Level::DEBUG, "{} XML file passed XSD validation.", didCombine ? "Combined" : "Main");
1400 }

References logging::DEBUG, logging::FATAL, LOG, and max.

Referenced by serial::parseSimulation(), and serial::parseSimulationFromString().

+ Here is the caller graph for this function: