FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
api.cpp File Reference

Implementation of the C-style FFI for the libfers core library. More...

#include <algorithm>
#include <core/logging.h>
#include <core/parameters.h>
#include <core/sim_id.h>
#include <cstring>
#include <filesystem>
#include <format>
#include <functional>
#include <libfers/api.h>
#include <math/path.h>
#include <math/rotation_path.h>
#include <mutex>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
#include "antenna/antenna_factory.h"
#include "core/fers_context.h"
#include "core/sim_threading.h"
#include "core/thread_pool.h"
#include "serial/json_serializer.h"
#include "serial/kml_generator.h"
#include "serial/rotation_angle_utils.h"
#include "serial/rotation_warning_utils.h"
#include "serial/xml_parser.h"
#include "serial/xml_serializer.h"
#include "signal/radar_signal.h"
#include "simulation/channel_model.h"
+ Include dependency graph for api.cpp:

Go to the source code of this file.

Classes

struct  fers_context
 

Functions

static void handle_api_exception (const std::exception &e, const std::string &function_name)
 Centralized exception handler for the C-API boundary.
 
static void begin_warning_capture () noexcept
 
static void complete_warning_capture ()
 
static void discard_warning_capture () noexcept
 
fers_context_tfers_context_create ()
 Creates a new FERS simulation context.
 
void fers_context_destroy (fers_context_t *context)
 Destroys a FERS simulation context and releases all associated memory.
 
static logging::Level map_api_log_level (fers_log_level_t level)
 
static fers_log_level_t map_internal_log_level (logging::Level level)
 
int fers_configure_logging (fers_log_level_t level, const char *log_file_path)
 Configures the internal logger.
 
fers_log_level_t fers_get_log_level ()
 Returns the current internal logger level.
 
void fers_set_log_callback (fers_log_callback_t callback, void *user_data)
 Registers a callback for formatted log lines.
 
void fers_log (fers_log_level_t level, const char *message)
 Submits a log message to the library's unified logging system.
 
int fers_set_thread_count (unsigned num_threads)
 Sets the number of worker threads for the simulation.
 
int fers_set_output_directory (fers_context_t *context, const char *out_dir)
 Sets the output directory for simulation results.
 
int fers_load_scenario_from_xml_file (fers_context_t *context, const char *xml_filepath, const int validate)
 Loads a scenario into the context from a FERS XML file.
 
int fers_load_scenario_from_xml_string (fers_context_t *context, const char *xml_content, const int validate)
 Loads a scenario into the context from a FERS XML string.
 
char * fers_get_scenario_as_json (fers_context_t *context)
 Serializes the current simulation scenario into a JSON string.
 
char * fers_get_scenario_as_xml (fers_context_t *context)
 Serializes the current simulation scenario into a FERS XML string.
 
char * fers_get_last_output_metadata_json (fers_context_t *context)
 Returns JSON metadata for the most recent simulation output files.
 
int fers_update_platform_from_json (fers_context_t *context, uint64_t id, const char *json)
 Updates a single platform's paths and name from JSON without full context recreation.
 
int fers_update_parameters_from_json (fers_context_t *context, const char *json)
 Updates the global simulation parameters from JSON without full context recreation.
 
int fers_update_antenna_from_json (fers_context_t *context, const char *json)
 Updates a single antenna from JSON without full context recreation.
 
int fers_update_waveform_from_json (fers_context_t *context, const char *json)
 Updates a single waveform from JSON without full context recreation.
 
int fers_update_transmitter_from_json (fers_context_t *context, uint64_t id, const char *json)
 Updates a single transmitter from JSON without full context recreation.
 
int fers_update_receiver_from_json (fers_context_t *context, uint64_t id, const char *json)
 Updates a single receiver from JSON without full context recreation.
 
int fers_update_target_from_json (fers_context_t *context, uint64_t id, const char *json)
 Updates a single target from JSON without full context recreation.
 
int fers_update_monostatic_from_json (fers_context_t *context, const char *json)
 Updates a monostatic radar from JSON without full context recreation.
 
int fers_update_timing_from_json (fers_context_t *context, uint64_t id, const char *json)
 Updates a single timing source from JSON without full context recreation.
 
int fers_update_scenario_from_json (fers_context_t *context, const char *scenario_json)
 Updates the simulation scenario from a JSON string.
 
char * fers_get_last_error_message ()
 Retrieves the last error message that occurred on the current thread.
 
char * fers_get_last_warning_messages_json ()
 Returns the last deduplicated rotation-unit warning list for the calling thread as JSON.
 
void fers_free_string (char *str)
 Frees a string that was allocated and returned by the libfers API.
 
int fers_run_simulation (fers_context_t *context, fers_progress_callback_t callback, void *user_data)
 Runs the simulation defined in the provided context.
 
int fers_generate_kml (const fers_context_t *context, const char *output_kml_filepath)
 Generates a KML file for visualizing the scenario in the context.
 
math::Path::InterpType to_cpp_interp_type (const fers_interp_type_t type)
 
math::RotationPath::InterpType to_cpp_rot_interp_type (const fers_interp_type_t type)
 
fers_interpolated_path_tfers_get_interpolated_motion_path (const fers_motion_waypoint_t *waypoints, const size_t waypoint_count, const fers_interp_type_t interp_type, const size_t num_points)
 Calculates an interpolated motion path from a set of waypoints.
 
void fers_free_interpolated_motion_path (fers_interpolated_path_t *path)
 Frees the memory allocated for an interpolated motion path.
 
fers_interpolated_rotation_path_tfers_get_interpolated_rotation_path (const fers_rotation_waypoint_t *waypoints, const size_t waypoint_count, const fers_interp_type_t interp_type, const fers_angle_unit_t angle_unit, const size_t num_points)
 Calculates an interpolated rotation path from a set of waypoints.
 
void fers_free_interpolated_rotation_path (fers_interpolated_rotation_path_t *path)
 Frees the memory allocated for an interpolated rotation path.
 
fers_antenna_pattern_data_tfers_get_antenna_pattern (const fers_context_t *context, const uint64_t antenna_id, const size_t az_samples, const size_t el_samples, const double frequency_hz)
 Samples the gain pattern of a specified antenna and provides the data.
 
void fers_free_antenna_pattern_data (fers_antenna_pattern_data_t *data)
 Frees the memory allocated for an antenna pattern data structure.
 
fers_visual_link_list_tfers_calculate_preview_links (const fers_context_t *context, const double time)
 Calculates visual links for a specific simulation time.
 
void fers_free_preview_links (fers_visual_link_list_t *list)
 Frees the memory allocated for a preview link list.
 

Variables

thread_local std::string last_error_message
 
thread_local std::vector< std::string > last_warning_messages
 

Detailed Description

Implementation of the C-style FFI for the libfers core library.

This file provides the C implementations for the functions declared in api.h. It acts as the bridge between the C ABI and the C++ core, handling object creation/destruction, exception catching, error reporting, and type casting.

Definition in file api.cpp.

Function Documentation

◆ begin_warning_capture()

static void begin_warning_capture ( )
staticnoexcept

Definition at line 69 of file api.cpp.

70{
73}
thread_local std::vector< std::string > last_warning_messages
Definition api.cpp:52

References serial::rotation_warning_utils::clear_captured_warnings(), and last_warning_messages.

Referenced by fers_load_scenario_from_xml_file(), fers_load_scenario_from_xml_string(), fers_update_parameters_from_json(), fers_update_platform_from_json(), and fers_update_scenario_from_json().

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

◆ complete_warning_capture()

static void complete_warning_capture ( )
static

Definition at line 75 of file api.cpp.

References last_warning_messages, and serial::rotation_warning_utils::take_captured_warnings().

Referenced by fers_load_scenario_from_xml_file(), fers_load_scenario_from_xml_string(), fers_update_parameters_from_json(), fers_update_platform_from_json(), and fers_update_scenario_from_json().

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

◆ discard_warning_capture()

static void discard_warning_capture ( )
staticnoexcept

Definition at line 80 of file api.cpp.

References serial::rotation_warning_utils::clear_captured_warnings(), and last_warning_messages.

Referenced by fers_context_create(), fers_load_scenario_from_xml_file(), fers_load_scenario_from_xml_string(), fers_update_parameters_from_json(), fers_update_platform_from_json(), and fers_update_scenario_from_json().

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

◆ fers_calculate_preview_links()

fers_visual_link_list_t * fers_calculate_preview_links ( const fers_context_t context,
double  time 
)

Calculates visual links for a specific simulation time.

Parameters
contextThe simulation context.
timeThe simulation time in seconds.
Returns
A pointer to a link list. Caller must free with fers_free_preview_links.

Definition at line 1137 of file api.cpp.

1138{
1139 last_error_message.clear();
1140 if (context == nullptr)
1141 {
1142 last_error_message = "Invalid context passed to fers_calculate_preview_links";
1144 return nullptr;
1145 }
1146
1147 try
1148 {
1149 const auto* ctx = reinterpret_cast<const FersContext*>(context);
1150 // Call the core physics logic in channel_model.cpp
1151 const auto cpp_links = simulation::calculatePreviewLinks(*ctx->getWorld(), time);
1152
1153 // Convert C++ vector to C-API struct
1154 auto* result = new fers_visual_link_list_t();
1155 result->count = cpp_links.size();
1156
1157 if (!cpp_links.empty())
1158 {
1159 result->links = new fers_visual_link_t[result->count];
1160 for (size_t i = 0; i < result->count; ++i)
1161 {
1162 const auto& src = cpp_links[i];
1163 auto& dst = result->links[i];
1164
1165 // Map enums
1166 switch (src.type)
1167 {
1170 break;
1172 dst.type = FERS_LINK_BISTATIC_TX_TGT;
1173 break;
1175 dst.type = FERS_LINK_BISTATIC_TGT_RX;
1176 break;
1178 dst.type = FERS_LINK_DIRECT_TX_RX;
1179 break;
1180 }
1181
1182 dst.quality = (src.quality == simulation::LinkQuality::Strong) ? FERS_LINK_STRONG : FERS_LINK_WEAK;
1183
1184 // Safe string copy
1185 std::strncpy(dst.label, src.label.c_str(), sizeof(dst.label) - 1);
1186 dst.label[sizeof(dst.label) - 1] = '\0';
1187
1188 dst.source_id = static_cast<uint64_t>(src.source_id);
1189 dst.dest_id = static_cast<uint64_t>(src.dest_id);
1190 dst.origin_id = static_cast<uint64_t>(src.origin_id);
1191 }
1192 }
1193 else
1194 {
1195 result->links = nullptr;
1196 }
1197 return result;
1198 }
1199 catch (const std::exception& e)
1200 {
1201 handle_api_exception(e, "fers_calculate_preview_links");
1202 return nullptr;
1203 }
1204}
static void handle_api_exception(const std::exception &e, const std::string &function_name)
Centralized exception handler for the C-API boundary.
Definition api.cpp:63
thread_local std::string last_error_message
Definition api.cpp:51
@ FERS_LINK_BISTATIC_TX_TGT
Definition api.h:595
@ FERS_LINK_MONOSTATIC
Definition api.h:594
@ FERS_LINK_BISTATIC_TGT_RX
Definition api.h:596
@ FERS_LINK_DIRECT_TX_RX
Definition api.h:597
@ FERS_LINK_WEAK
Definition api.h:586
@ FERS_LINK_STRONG
Definition api.h:585
Manages the lifetime and state of a single FERS simulation scenario.
#define LOG(level,...)
Definition logging.h:19
@ ERROR
Error level for error events.
std::vector< PreviewLink > calculatePreviewLinks(const core::World &world, const RealType time)
Calculates all visual links for the current world state at a specific time.
@ DirectTxRx
Interference path.
@ Monostatic
Combined Tx/Rx path.
@ BistaticTgtRx
Scattered path.
@ BistaticTxTgt
Illuminator path.

References simulation::BistaticTgtRx, simulation::BistaticTxTgt, simulation::calculatePreviewLinks(), simulation::DirectTxRx, logging::ERROR, FERS_LINK_BISTATIC_TGT_RX, FERS_LINK_BISTATIC_TX_TGT, FERS_LINK_DIRECT_TX_RX, FERS_LINK_MONOSTATIC, FERS_LINK_STRONG, FERS_LINK_WEAK, handle_api_exception(), last_error_message, LOG, simulation::Monostatic, simulation::Strong, and fers_visual_link_t::type.

+ Here is the call graph for this function:

◆ fers_configure_logging()

int fers_configure_logging ( fers_log_level_t  level,
const char *  log_file_path 
)

Configures the internal logger.

Parameters
levelThe minimum severity level to log.
log_file_pathOptional path to a log file. Pass NULL to disable file logging.
Returns
0 on success, non-zero on error.

Definition at line 188 of file api.cpp.

189{
190 last_error_message.clear();
191 try
192 {
194 if ((log_file_path != nullptr) && ((*log_file_path) != 0))
195 {
196 auto result = logging::logger.logToFile(log_file_path);
197 if (!result)
198 {
199 last_error_message = result.error();
200 return 1;
201 }
202 }
203 return 0;
204 }
205 catch (const std::exception& e)
206 {
207 handle_api_exception(e, "fers_configure_logging");
208 return 1;
209 }
210}
static logging::Level map_api_log_level(fers_log_level_t level)
Definition api.cpp:118
std::expected< void, std::string > logToFile(const std::string &filePath) noexcept
Sets the log file path to log messages to a file.
Definition logging.cpp:90
void setLevel(Level level) noexcept
Sets the logging level.
Definition logging.cpp:25
Logger logger
Externally available logger object.
Definition logging.cpp:23

References handle_api_exception(), last_error_message, logging::logger, logging::Logger::logToFile(), map_api_log_level(), and logging::Logger::setLevel().

Referenced by main().

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

◆ fers_context_create()

fers_context_t * fers_context_create ( )

Creates a new FERS simulation context.

Allocates and initializes a new, empty simulation context in memory. This context serves as the container for a scenario loaded via one of the fers_load_... or fers_update_... functions.

Note
In a C API, RAII is not available. The caller is responsible for destroying the returned context using fers_context_destroy() to prevent resource leaks.
Returns
A non-null opaque pointer (handle) to the simulation context on success. Returns NULL on failure (e.g., out of memory).

Definition at line 88 of file api.cpp.

89{
90 last_error_message.clear();
92 try
93 {
94 return new fers_context_t();
95 }
96 catch (const std::bad_alloc& e)
97 {
98 handle_api_exception(e, "fers_context_create");
99 return nullptr;
100 }
101 catch (const std::exception& e)
102 {
103 handle_api_exception(e, "fers_context_create");
104 return nullptr;
105 }
106}
static void discard_warning_capture() noexcept
Definition api.cpp:80
struct fers_context fers_context_t
Definition api.h:26

References discard_warning_capture(), handle_api_exception(), and last_error_message.

Referenced by main().

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

◆ fers_context_destroy()

void fers_context_destroy ( fers_context_t context)

Destroys a FERS simulation context and releases all associated memory.

This function must be called for every context created by fers_context_create() to ensure proper cleanup of the underlying C++ objects. Accessing the context handle after calling this function results in undefined behavior.

Parameters
contextA valid pointer to a fers_context_t handle. If context is NULL, the function performs no action for safety and does not set an error.

Definition at line 108 of file api.cpp.

109{
110 if (context == nullptr)
111 {
112 return;
113 }
114 delete context;
115}

Referenced by main().

+ Here is the caller graph for this function:

◆ fers_free_antenna_pattern_data()

void fers_free_antenna_pattern_data ( fers_antenna_pattern_data_t data)

Frees the memory allocated for an antenna pattern data structure.

Parameters
dataA pointer to the fers_antenna_pattern_data_t struct to free.

Definition at line 1126 of file api.cpp.

1127{
1128 if (data != nullptr)
1129 {
1130 delete[] data->gains;
1131 delete data;
1132 }
1133}

References fers_antenna_pattern_data_t::gains.

◆ fers_free_interpolated_motion_path()

void fers_free_interpolated_motion_path ( fers_interpolated_path_t path)

Frees the memory allocated for an interpolated motion path.

Parameters
pathA pointer to the fers_interpolated_path_t struct to free.

Definition at line 933 of file api.cpp.

934{
935 if (path != nullptr)
936 {
937 delete[] path->points;
938 delete path;
939 }
940}
fers_interpolated_point_t * points
Definition api.h:516

References fers_interpolated_path_t::points.

◆ fers_free_interpolated_rotation_path()

void fers_free_interpolated_rotation_path ( fers_interpolated_rotation_path_t path)

Frees the memory allocated for an interpolated rotation path.

Parameters
pathA pointer to the fers_interpolated_rotation_path_t struct to free.

Definition at line 1029 of file api.cpp.

1030{
1031 if (path != nullptr)
1032 {
1033 delete[] path->points;
1034 delete path;
1035 }
1036}
fers_interpolated_rotation_point_t * points
Definition api.h:526

References fers_interpolated_rotation_path_t::points.

◆ fers_free_preview_links()

void fers_free_preview_links ( fers_visual_link_list_t list)

Frees the memory allocated for a preview link list.

Parameters
listThe list to free.

Definition at line 1206 of file api.cpp.

1207{
1208 if (list != nullptr)
1209 {
1210 delete[] list->links;
1211 delete list;
1212 }
1213}

References fers_visual_link_list_t::links.

◆ fers_free_string()

void fers_free_string ( char *  str)

Frees a string that was allocated and returned by the libfers API.

This function must be used to release memory for any string returned by functions like fers_get_scenario_as_json or fers_get_last_error_message. It exists to ensure that the memory deallocation mechanism (free) matches the allocation mechanism (strdup/malloc) used within the C++ library, preventing potential crashes from mismatched allocators across language boundaries.

Parameters
strA pointer to the string to be freed. If str is NULL, no action is taken.

Definition at line 752 of file api.cpp.

753{
754 if (str != nullptr)
755 {
756 free(str);
757 }
758}

Referenced by main().

+ Here is the caller graph for this function:

◆ fers_generate_kml()

int fers_generate_kml ( const fers_context_t context,
const char *  output_kml_filepath 
)

Generates a KML file for visualizing the scenario in the context.

This utility exists to provide a simple, out-of-the-box method for users to validate and visualize the geographic layout and motion paths of their scenarios in common external tools like Google Earth.

Parameters
contextA valid fers_context_t handle containing a loaded scenario.
output_kml_filepathA null-terminated UTF-8 string for the output KML file path.
Returns
0 on success, a non-zero error code on failure. Use fers_get_last_error_message() to retrieve error details.

Definition at line 798 of file api.cpp.

799{
800 last_error_message.clear();
801 if ((context == nullptr) || (output_kml_filepath == nullptr))
802 {
803 last_error_message = "Invalid arguments: context or output_kml_filepath is NULL.";
805 return -1;
806 }
807
808 const auto* ctx = reinterpret_cast<const FersContext*>(context);
809
810 try
811 {
812 if (serial::KmlGenerator::generateKml(*ctx->getWorld(), output_kml_filepath))
813 {
814 return 0; // Success
815 }
816
817 last_error_message = "KML generation failed for an unknown reason.";
819 return 2; // Generation failed
820 }
821 catch (const std::exception& e)
822 {
823 handle_api_exception(e, "fers_generate_kml");
824 return 1; // Exception thrown
825 }
826}
static bool generateKml(const core::World &world, const std::string &outputKmlPath)
Generates a KML file from a pre-built simulation world.

References logging::ERROR, serial::KmlGenerator::generateKml(), handle_api_exception(), last_error_message, and LOG.

Referenced by main().

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

◆ fers_get_antenna_pattern()

fers_antenna_pattern_data_t * fers_get_antenna_pattern ( const fers_context_t context,
uint64_t  antenna_id,
size_t  az_samples,
size_t  el_samples,
double  frequency_hz 
)

Samples the gain pattern of a specified antenna and provides the data.

This function calculates the antenna's far-field gain at a specified resolution over the full sphere of directions (azimuth and elevation). The resulting gain values are linear (not in dB) and normalized relative to the pattern's peak gain. This is a stateless utility useful for UI previews and analysis.

Parameters
contextA valid fers_context_t handle containing a loaded scenario with the antenna.
antenna_idThe unique ID of the antenna asset to sample.
az_samplesThe desired number of sample points along the azimuth axis. Must be at least 2 to span the full azimuth range.
el_samplesThe desired number of sample points along the elevation axis. Must be at least 2 to span the full elevation range.
frequency_hzThe frequency in Hz to use for gain calculation (affects aperture antennas).
Returns
A pointer to a fers_antenna_pattern_data_t struct containing the results. Returns NULL on failure (e.g., antenna not found). The caller owns the returned struct and must free it with fers_free_antenna_pattern_data.

Definition at line 1040 of file api.cpp.

1043{
1044 last_error_message.clear();
1045 if ((context == nullptr) || az_samples < 2 || el_samples < 2)
1046 {
1047 last_error_message = "Invalid arguments: context must be non-null and sample counts must be >= 2.";
1049 return nullptr;
1050 }
1051
1052 try
1053 {
1054 const auto* ctx = reinterpret_cast<const FersContext*>(context);
1055 antenna::Antenna* ant = ctx->getWorld()->findAntenna(static_cast<SimId>(antenna_id));
1056
1057 if (ant == nullptr)
1058 {
1059 last_error_message = "Antenna ID '" + std::to_string(antenna_id) + "' not found in the world.";
1061 return nullptr;
1062 }
1063
1064 // TODO: Currently only using the first-found waveform. This is incorrect but also difficult to represent
1065 // correctly in scenarios with multiple waveforms as the gain for squarehorn and parabolic antennas
1066 // depends on the wavelength. Hence a decision needs to be made about whether to return multiple patterns
1067 // per waveform or have the user specify a representative wavelength in the UI per antenna.
1068 // Calculate wavelength from the provided frequency.
1069 // Default to 1GHz (0.3m) if frequency is invalid/zero, though the UI should prevent this
1070 // for antennas that strictly require it (Horn/Parabolic).
1071 RealType wavelength = 0.3;
1072 if (frequency_hz > 0.0)
1073 {
1074 wavelength = params::c() / frequency_hz;
1075 }
1076
1077 auto* data = new fers_antenna_pattern_data_t();
1078 data->az_count = az_samples;
1079 data->el_count = el_samples;
1080 const size_t total_samples = az_samples * el_samples;
1081 data->gains = new double[total_samples];
1082
1083 // The reference angle (boresight) is implicitly the local X-axis in the FERS engine.
1084 // We pass a zero rotation to get the gain relative to this boresight.
1085 const math::SVec3 ref_angle(1.0, 0.0, 0.0);
1086 double max_gain = 0.0;
1087
1088 const RealType az_denominator = static_cast<RealType>(az_samples - 1);
1089 const RealType el_denominator = static_cast<RealType>(el_samples - 1);
1090
1091 for (size_t i = 0; i < el_samples; ++i)
1092 {
1093 // Elevation from -PI/2 to PI/2
1094 const RealType elevation = (static_cast<RealType>(i) / el_denominator) * PI - (PI / 2.0);
1095 for (size_t j = 0; j < az_samples; ++j)
1096 {
1097 // Azimuth from -PI to PI
1098 const RealType azimuth = (static_cast<RealType>(j) / az_denominator) * 2.0 * PI - PI;
1099 const math::SVec3 sample_angle(1.0, azimuth, elevation);
1100 const RealType gain = ant->getGain(sample_angle, ref_angle, wavelength);
1101 data->gains[i * az_samples + j] = gain;
1102 max_gain = std::max(gain, max_gain);
1103 }
1104 }
1105
1106 data->max_gain = max_gain;
1107
1108 // Normalize the gains
1109 if (max_gain > 0)
1110 {
1111 for (size_t i = 0; i < total_samples; ++i)
1112 {
1113 data->gains[i] /= max_gain;
1114 }
1115 }
1116
1117 return data;
1118 }
1119 catch (const std::exception& e)
1120 {
1121 handle_api_exception(e, "fers_get_antenna_pattern");
1122 return nullptr;
1123 }
1124}
Abstract base class representing an antenna.
virtual RealType getGain(const math::SVec3 &angle, const math::SVec3 &refangle, RealType wavelength) const =0
Computes the gain of the antenna based on the input angle and reference angle.
A class representing a vector in spherical coordinates.
double RealType
Type for real numbers.
Definition config.h:27
constexpr RealType PI
Mathematical constant π (pi).
Definition config.h:43
RealType c() noexcept
Get the speed of light.
Definition parameters.h:91
uint64_t SimId
64-bit Unique Simulation ID.
Definition sim_id.h:18
Represents a sampled 2D antenna gain pattern.
Definition api.h:402

References params::c(), logging::ERROR, antenna::Antenna::getGain(), handle_api_exception(), last_error_message, LOG, and PI.

+ Here is the call graph for this function:

◆ fers_get_interpolated_motion_path()

fers_interpolated_path_t * fers_get_interpolated_motion_path ( const fers_motion_waypoint_t waypoints,
size_t  waypoint_count,
fers_interp_type_t  interp_type,
size_t  num_points 
)

Calculates an interpolated motion path from a set of waypoints.

This function is a stateless utility that computes the path without needing a full simulation context. It is useful for UI previews.

Parameters
waypointsAn array of fers_motion_waypoint_t structs.
waypoint_countThe number of waypoints in the array.
interp_typeThe interpolation algorithm to use.
num_pointsThe desired number of points in the output interpolated path.
Returns
A pointer to a fers_interpolated_path_t struct containing the results. Returns NULL on failure. The caller owns the returned struct and must free it with fers_free_interpolated_motion_path.

Definition at line 858 of file api.cpp.

862{
863 last_error_message.clear();
864 if ((waypoints == nullptr) || waypoint_count == 0 || num_points == 0)
865 {
866 last_error_message = "Invalid arguments: waypoints cannot be null and counts must be > 0.";
868 return nullptr;
869 }
870 if (interp_type == FERS_INTERP_CUBIC && waypoint_count < 2)
871 {
872 last_error_message = "Cubic interpolation requires at least 2 waypoints.";
874 return nullptr;
875 }
876
877 try
878 {
879 math::Path path;
880 path.setInterp(to_cpp_interp_type(interp_type));
881
882 for (size_t i = 0; i < waypoint_count; ++i)
883 {
885 c.t = waypoints[i].time;
886 c.pos.x = waypoints[i].x;
887 c.pos.y = waypoints[i].y;
888 c.pos.z = waypoints[i].z;
889 path.addCoord(c);
890 }
891
892 path.finalize();
893
894 auto* result_path = new fers_interpolated_path_t();
895 result_path->points = new fers_interpolated_point_t[num_points];
896 result_path->count = num_points;
897
898 const double start_time = waypoints[0].time;
899 const double end_time = waypoints[waypoint_count - 1].time;
900 const double duration = end_time - start_time;
901
902 // Handle static case separately
903 if (waypoint_count < 2 || duration <= 0)
904 {
905 const math::Vec3 pos = path.getPosition(start_time);
906 for (size_t i = 0; i < num_points; ++i)
907 {
908 result_path->points[i] = {pos.x, pos.y, pos.z, 0.0, 0.0, 0.0};
909 }
910 return result_path;
911 }
912
913 const double time_step =
914 duration / static_cast<double>(num_points > 1 ? num_points - 1 : static_cast<size_t>(1));
915
916 for (size_t i = 0; i < num_points; ++i)
917 {
918 const double t = start_time + static_cast<double>(i) * time_step;
919 const math::Vec3 pos = path.getPosition(t);
920 const math::Vec3 vel = path.getVelocity(t);
921 result_path->points[i] = {pos.x, pos.y, pos.z, vel.x, vel.y, vel.z};
922 }
923
924 return result_path;
925 }
926 catch (const std::exception& e)
927 {
928 handle_api_exception(e, "fers_get_interpolated_motion_path");
929 return nullptr;
930 }
931}
math::Path::InterpType to_cpp_interp_type(const fers_interp_type_t type)
Definition api.cpp:829
@ FERS_INTERP_CUBIC
Definition api.h:450
Represents a path with coordinates and allows for various interpolation methods.
Definition path.h:30
Vec3 getPosition(RealType t) const
Retrieves the position at a given time along the path.
Definition path.cpp:36
void setInterp(InterpType settype) noexcept
Changes the interpolation type.
Definition path.cpp:164
Vec3 getVelocity(RealType t) const
Retrieves the velocity at a given time along the path.
Definition path.cpp:60
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.
RealType x
The x component of the vector.
RealType z
The z component of the vector.
RealType y
The y component of the vector.
A container for an array of interpolated motion path points.
Definition api.h:515
Represents a single interpolated point on a motion path.
Definition api.h:490
double x
X coordinate in meters (East in ENU).
Definition api.h:469
double time
Time in seconds.
Definition api.h:468
double y
Y coordinate in meters (North in ENU).
Definition api.h:470
double z
Z coordinate in meters (Up/Altitude in ENU).
Definition api.h:471
Represents a position in 3D space with an associated time.
Definition coord.h:24

References math::Path::addCoord(), logging::ERROR, FERS_INTERP_CUBIC, math::Path::finalize(), math::Path::getPosition(), math::Path::getVelocity(), handle_api_exception(), last_error_message, LOG, math::Path::setInterp(), fers_motion_waypoint_t::time, to_cpp_interp_type(), fers_motion_waypoint_t::x, math::Vec3::x, fers_motion_waypoint_t::y, math::Vec3::y, fers_motion_waypoint_t::z, and math::Vec3::z.

+ Here is the call graph for this function:

◆ fers_get_interpolated_rotation_path()

fers_interpolated_rotation_path_t * fers_get_interpolated_rotation_path ( const fers_rotation_waypoint_t waypoints,
size_t  waypoint_count,
fers_interp_type_t  interp_type,
fers_angle_unit_t  angle_unit,
size_t  num_points 
)

Calculates an interpolated rotation path from a set of waypoints.

This function is a stateless utility for UI previews.

Parameters
waypointsAn array of fers_rotation_waypoint_t structs.
waypoint_countThe number of waypoints in the array.
interp_typeThe interpolation algorithm to use (STATIC, LINEAR, CUBIC).
angle_unitThe unit used by the waypoint angles and desired output angles.
num_pointsThe desired number of points in the output interpolated path.
Returns
A pointer to a fers_interpolated_rotation_path_t struct containing the results. Returns NULL on failure. The caller owns the returned struct and must free it with fers_free_interpolated_rotation_path.

Definition at line 942 of file api.cpp.

947{
948 last_error_message.clear();
949 last_warning_messages.clear();
951 if ((waypoints == nullptr) || waypoint_count == 0 || num_points == 0)
952 {
953 last_error_message = "Invalid arguments: waypoints cannot be null and counts must be > 0.";
955 return nullptr;
956 }
957 if (interp_type == FERS_INTERP_CUBIC && waypoint_count < 2)
958 {
959 last_error_message = "Cubic interpolation requires at least 2 waypoints.";
961 return nullptr;
962 }
963
964 try
965 {
966 const auto unit =
969 path.setInterp(to_cpp_rot_interp_type(interp_type));
970
971 for (size_t i = 0; i < waypoint_count; ++i)
972 {
974 waypoints[i].azimuth, unit, serial::rotation_warning_utils::ValueKind::Angle, "C-API",
975 std::format("rotation waypoint {}", i), "azimuth");
977 waypoints[i].elevation, unit, serial::rotation_warning_utils::ValueKind::Angle, "C-API",
978 std::format("rotation waypoint {}", i), "elevation");
980 waypoints[i].azimuth, waypoints[i].elevation, waypoints[i].time, unit));
981 }
982
983 path.finalize();
984
985 auto* result_path = new fers_interpolated_rotation_path_t();
986 result_path->points = new fers_interpolated_rotation_point_t[num_points];
987 result_path->count = num_points;
988
989 const double start_time = waypoints[0].time;
990 const double end_time = waypoints[waypoint_count - 1].time;
991 const double duration = end_time - start_time;
992
993 // Handle static case separately
994 if (waypoint_count < 2 || duration <= 0)
995 {
996 const math::SVec3 rot = path.getPosition(start_time);
997 for (size_t i = 0; i < num_points; ++i)
998 {
999 result_path->points[i] = fers_interpolated_rotation_point_t{
1002 }
1003 return result_path;
1004 }
1005
1006 const double time_step =
1007 duration / static_cast<double>(num_points > 1 ? num_points - 1 : static_cast<size_t>(1));
1008
1009 for (size_t i = 0; i < num_points; ++i)
1010 {
1011 const double t = start_time + static_cast<double>(i) * time_step;
1012 const math::SVec3 rot = path.getPosition(t);
1013
1014 result_path->points[i] = fers_interpolated_rotation_point_t{
1017 }
1018
1019 return result_path;
1020 }
1021 catch (const std::exception& e)
1022 {
1024 handle_api_exception(e, "fers_get_interpolated_rotation_path");
1025 return nullptr;
1026 }
1027}
math::RotationPath::InterpType to_cpp_rot_interp_type(const fers_interp_type_t type)
Definition api.cpp:843
@ FERS_ANGLE_UNIT_RAD
Definition api.h:459
Manages rotational paths with different interpolation techniques.
void finalize()
Finalizes the rotation path for interpolation.
void setInterp(InterpType setinterp) noexcept
Sets the interpolation type for the path.
SVec3 getPosition(RealType t) const
Gets the rotational position at a given time.
void addCoord(const RotationCoord &coord) noexcept
Adds a rotation coordinate to the path.
RealType elevation
The elevation angle of the vector.
RealType azimuth
The azimuth angle of the vector.
@ Radians
Compass azimuth and elevation expressed in radians.
@ Degrees
Compass azimuth and elevation expressed in degrees.
RealType internal_elevation_to_external(const RealType elevation, const params::RotationAngleUnit unit) noexcept
math::RotationCoord external_rotation_to_internal(const RealType azimuth, const RealType elevation, const RealType time, const params::RotationAngleUnit unit) noexcept
RealType internal_azimuth_to_external(const RealType azimuth, const params::RotationAngleUnit unit) noexcept
void maybe_warn_about_rotation_value(const RealType value, const params::RotationAngleUnit declared_unit, const ValueKind kind, const std::string_view source, const std::string_view owner, const std::string_view field)
A container for an array of interpolated rotation path points.
Definition api.h:525
Represents a single interpolated point on a rotation path.
Definition api.h:504
double time
Time in seconds.
Definition api.h:480

References math::RotationPath::addCoord(), serial::rotation_warning_utils::Angle, math::SVec3::azimuth, serial::rotation_warning_utils::clear_captured_warnings(), params::Degrees, math::SVec3::elevation, logging::ERROR, serial::rotation_angle_utils::external_rotation_to_internal(), FERS_ANGLE_UNIT_RAD, FERS_INTERP_CUBIC, math::RotationPath::finalize(), math::RotationPath::getPosition(), handle_api_exception(), serial::rotation_angle_utils::internal_azimuth_to_external(), serial::rotation_angle_utils::internal_elevation_to_external(), last_error_message, last_warning_messages, LOG, serial::rotation_warning_utils::maybe_warn_about_rotation_value(), params::Radians, math::RotationPath::setInterp(), fers_rotation_waypoint_t::time, and to_cpp_rot_interp_type().

+ Here is the call graph for this function:

◆ fers_get_last_error_message()

char * fers_get_last_error_message ( )

Retrieves the last error message that occurred on the current thread.

Because C++ exceptions cannot safely propagate across the FFI boundary into other languages, this function provides the standard C-style error reporting mechanism. The error state is stored in a thread-local variable to ensure that concurrent API calls from different threads do not overwrite each other's error messages. The error is cleared at the start of each fallible API call.

Note
Memory Management: The returned string's ownership is transferred to the caller. It MUST be freed using fers_free_string() to prevent memory leaks.
Returns
A dynamically allocated, null-terminated C-string containing the last error message, or NULL if no error has occurred.

Definition at line 728 of file api.cpp.

729{
730 if (last_error_message.empty())
731 {
732 return nullptr; // No error to report
733 }
734 // `strdup` allocates with `malloc`, which is part of the C standard ABI,
735 // making it safe to transfer ownership across the FFI boundary. The caller
736 // must then free this memory using `fers_free_string`.
737 return strdup(last_error_message.c_str());
738}

References last_error_message.

Referenced by main().

+ Here is the caller graph for this function:

◆ fers_get_last_output_metadata_json()

char * fers_get_last_output_metadata_json ( fers_context_t context)

Returns JSON metadata for the most recent simulation output files.

The returned JSON describes generated HDF5 file structure and sample ranges. The caller owns the returned string and must free it with fers_free_string. If no simulation has completed yet, the returned JSON contains an empty files array.

Parameters
contextA valid fers_context_t handle.
Returns
A heap-allocated JSON string, or NULL on error.

Definition at line 427 of file api.cpp.

428{
429 last_error_message.clear();
430 if (context == nullptr)
431 {
432 last_error_message = "Invalid context provided to fers_get_last_output_metadata_json.";
434 return nullptr;
435 }
436
437 const auto* ctx = reinterpret_cast<FersContext*>(context);
438 try
439 {
440 const std::string json_str = ctx->getLastOutputMetadataJson();
441 return strdup(json_str.c_str());
442 }
443 catch (const std::exception& e)
444 {
445 handle_api_exception(e, "fers_get_last_output_metadata_json");
446 return nullptr;
447 }
448}
std::string getLastOutputMetadataJson() const

References logging::ERROR, FersContext::getLastOutputMetadataJson(), handle_api_exception(), last_error_message, and LOG.

+ Here is the call graph for this function:

◆ fers_get_last_warning_messages_json()

char * fers_get_last_warning_messages_json ( )

Returns the last deduplicated rotation-unit warning list for the calling thread as JSON.

The returned value is a JSON array of strings. It is populated by successful XML/JSON load and update calls that detect suspicious rotation values. The caller owns the string and must free it with fers_free_string().

Returns
A dynamically allocated JSON array string, or NULL if no warnings are available.

Definition at line 740 of file api.cpp.

741{
742 if (last_warning_messages.empty())
743 {
744 return nullptr;
745 }
746
747 const std::string warning_json = nlohmann::json(last_warning_messages).dump();
748 last_warning_messages.clear();
749 return strdup(warning_json.c_str());
750}

References last_warning_messages.

◆ fers_get_log_level()

fers_log_level_t fers_get_log_level ( )

Returns the current internal logger level.

Definition at line 212 of file api.cpp.

212{ return map_internal_log_level(logging::logger.getLevel()); }
static fers_log_level_t map_internal_log_level(logging::Level level)
Definition api.cpp:141

References logging::logger, and map_internal_log_level().

+ Here is the call graph for this function:

◆ fers_get_scenario_as_json()

char * fers_get_scenario_as_json ( fers_context_t context)

Serializes the current simulation scenario into a JSON string.

This function is the primary method for the UI to retrieve the full state of the simulation. JSON is used as the interchange format because it is lightweight, human-readable, and natively supported by web technologies, making it trivial to parse and use in the React/TypeScript frontend.

Note
Memory Management: The returned string is allocated by this library and its ownership is transferred to the caller. It is crucial to free this string using fers_free_string() to prevent memory leaks.
Parameters
contextA valid fers_context_t handle.
Returns
A dynamically allocated, null-terminated C-string containing the JSON representation of the scenario. Returns NULL on failure.

Definition at line 370 of file api.cpp.

371{
372 last_error_message.clear();
373 if (context == nullptr)
374 {
375 last_error_message = "Invalid context provided to fers_get_scenario_as_json.";
377 return nullptr;
378 }
379
380 const auto* ctx = reinterpret_cast<FersContext*>(context);
381 try
382 {
383 const nlohmann::json j = serial::world_to_json(*ctx->getWorld());
384 const std::string json_str = j.dump(2);
385 // A heap-allocated copy of the string is returned. This is necessary
386 // to transfer ownership of the memory across the FFI boundary to a
387 // client that will free it using `fers_free_string`.
388 return strdup(json_str.c_str());
389 }
390 catch (const std::exception& e)
391 {
392 handle_api_exception(e, "fers_get_scenario_as_json");
393 return nullptr;
394 }
395}
nlohmann::json world_to_json(const core::World &world)
Serializes the entire simulation world into a nlohmann::json object.

References logging::ERROR, handle_api_exception(), last_error_message, LOG, and serial::world_to_json().

+ Here is the call graph for this function:

◆ fers_get_scenario_as_xml()

char * fers_get_scenario_as_xml ( fers_context_t context)

Serializes the current simulation scenario into a FERS XML string.

This function enables exporting the in-memory state back into the standard FERS XML file format. This is essential for interoperability with legacy tools and for allowing a user to save a scenario that was created or modified in the UI.

Note
Memory Management: The returned string is dynamically allocated and its ownership is transferred to the caller. It must be freed using fers_free_string() to prevent memory leaks.
Parameters
contextA valid fers_context_t handle.
Returns
A dynamically allocated, null-terminated C-string containing the XML representation of the scenario. Returns NULL on failure.

Definition at line 397 of file api.cpp.

398{
399 last_error_message.clear();
400 if (context == nullptr)
401 {
402 last_error_message = "Invalid context provided to fers_get_scenario_as_xml.";
404 return nullptr;
405 }
406
407 const auto* ctx = reinterpret_cast<FersContext*>(context);
408 try
409 {
410 const std::string xml_str = serial::world_to_xml_string(*ctx->getWorld());
411 if (xml_str.empty())
412 {
413 throw std::runtime_error("XML serialization resulted in an empty string.");
414 }
415 // `strdup` is used to create a heap-allocated string that can be safely
416 // passed across the FFI boundary. The client is responsible for freeing
417 // this memory with `fers_free_string`.
418 return strdup(xml_str.c_str());
419 }
420 catch (const std::exception& e)
421 {
422 handle_api_exception(e, "fers_get_scenario_as_xml");
423 return nullptr;
424 }
425}
std::string world_to_xml_string(const core::World &world)
Serializes the entire simulation world into an XML formatted string.

References logging::ERROR, handle_api_exception(), last_error_message, LOG, and serial::world_to_xml_string().

+ Here is the call graph for this function:

◆ fers_load_scenario_from_xml_file()

int fers_load_scenario_from_xml_file ( fers_context_t context,
const char *  xml_filepath,
int  validate 
)

Loads a scenario into the context from a FERS XML file.

This is the standard method for initializing a simulation context from a file on disk. It is essential for interoperability with the CLI and legacy workflows that rely on the FERS XML format.

Parameters
contextA valid fers_context_t handle.
xml_filepathA null-terminated UTF-8 string for the input XML file path.
validateA boolean (0 or 1) indicating whether to validate the XML against the embedded FERS schema. Validation is recommended to ensure scenario correctness.
Returns
0 on success, a non-zero error code on failure. Use fers_get_last_error_message() to retrieve error details.

Definition at line 274 of file api.cpp.

275{
276 last_error_message.clear();
278 if ((context == nullptr) || (xml_filepath == nullptr))
279 {
280 last_error_message = "Invalid arguments: context or xml_filepath is NULL.";
283 return -1;
284 }
285
286 auto* ctx = reinterpret_cast<FersContext*>(context);
287 try
288 {
289 // Set default output directory to the scenario file's directory
290 std::filesystem::path p(xml_filepath);
291 auto parent = p.parent_path();
292 if (parent.empty())
293 parent = ".";
294 ctx->setOutputDir(parent.string());
295
296 serial::parseSimulation(xml_filepath, ctx->getWorld(), static_cast<bool>(validate), ctx->getMasterSeeder());
297
298 // After parsing, seed the master random number generator. This is done
299 // to ensure simulation reproducibility. If the scenario specifies a seed,
300 // it is used; otherwise, a non-deterministic seed is generated so that
301 // subsequent runs are unique by default.
302 if (params::params.random_seed)
303 {
304 LOG(logging::Level::INFO, "Using master seed from scenario file: {}", *params::params.random_seed);
305 ctx->getMasterSeeder().seed(*params::params.random_seed);
306 }
307 else
308 {
309 const auto seed = std::random_device{}();
310 LOG(logging::Level::INFO, "No master seed provided in scenario. Using random_device seed: {}", seed);
312 ctx->getMasterSeeder().seed(seed);
313 }
315 return 0; // Success
316 }
317 catch (const std::exception& e)
318 {
320 handle_api_exception(e, "fers_load_scenario_from_xml_file");
321 return 1; // Error
322 }
323}
static void begin_warning_capture() noexcept
Definition api.cpp:69
static void complete_warning_capture()
Definition api.cpp:75
@ INFO
Info level for informational messages.
Parameters params
Definition parameters.h:85
void parseSimulation(const std::string &filename, core::World *world, const bool validate, std::mt19937 &masterSeeder)
Parses a simulation configuration from an XML file.
std::optional< unsigned > random_seed
Random seed for simulation.
Definition parameters.h:70

References begin_warning_capture(), complete_warning_capture(), discard_warning_capture(), logging::ERROR, handle_api_exception(), logging::INFO, last_error_message, LOG, params::params, serial::parseSimulation(), and params::Parameters::random_seed.

Referenced by main().

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

◆ fers_load_scenario_from_xml_string()

int fers_load_scenario_from_xml_string ( fers_context_t context,
const char *  xml_content,
int  validate 
)

Loads a scenario into the context from a FERS XML string.

This function provides a way to load a scenario from an in-memory string, avoiding file I/O. It is useful for test harnesses or for UIs that manage scenarios as text content before parsing.

Parameters
contextA valid fers_context_t handle.
xml_contentA null-terminated UTF-8 string containing the FERS scenario in XML format.
validateA boolean (0 or 1) indicating whether to validate the XML against the embedded FERS schema.
Returns
0 on success, a non-zero error code on failure. Use fers_get_last_error_message() to retrieve error details.

Definition at line 325 of file api.cpp.

326{
327 last_error_message.clear();
329 if ((context == nullptr) || (xml_content == nullptr))
330 {
331 last_error_message = "Invalid arguments: context or xml_content is NULL.";
334 return -1;
335 }
336
337 auto* ctx = reinterpret_cast<FersContext*>(context);
338 try
339 {
340 serial::parseSimulationFromString(xml_content, ctx->getWorld(), static_cast<bool>(validate),
341 ctx->getMasterSeeder());
342
343 // After parsing, seed the master random number generator. This ensures
344 // that if the scenario provides a seed, the simulation will be
345 // reproducible. If not, a random seed is used to ensure unique runs.
346 if (params::params.random_seed)
347 {
348 LOG(logging::Level::INFO, "Using master seed from scenario string: {}", *params::params.random_seed);
349 ctx->getMasterSeeder().seed(*params::params.random_seed);
350 }
351 else
352 {
353 const auto seed = std::random_device{}();
354 LOG(logging::Level::INFO, "No master seed provided in scenario. Using random_device seed: {}", seed);
356 ctx->getMasterSeeder().seed(seed);
357 }
358
360 return 0; // Success
361 }
362 catch (const std::exception& e)
363 {
365 handle_api_exception(e, "fers_load_scenario_from_xml_string");
366 return 1; // Parsing or logic error
367 }
368}
void parseSimulationFromString(const std::string &xmlContent, core::World *world, const bool validate, std::mt19937 &masterSeeder)
Parses a simulation configuration directly from an XML string in memory.

References begin_warning_capture(), complete_warning_capture(), discard_warning_capture(), logging::ERROR, handle_api_exception(), logging::INFO, last_error_message, LOG, params::params, serial::parseSimulationFromString(), and params::Parameters::random_seed.

+ Here is the call graph for this function:

◆ fers_log()

void fers_log ( fers_log_level_t  level,
const char *  message 
)

Submits a log message to the library's unified logging system.

This ensures CLI messages match the format (timestamps, alignment) of library messages.

Definition at line 225 of file api.cpp.

226{
227 if (message == nullptr)
228 return;
229 // We pass a default source_location because C-API calls don't provide C++ source info
230 logging::logger.log(map_api_log_level(level), message, std::source_location::current());
231}
void log(Level level, const std::string &message, const std::source_location &location=std::source_location::current()) noexcept
Logs a message with a specific log level and source location.
Definition logging.cpp:45

References logging::Logger::log(), logging::logger, and map_api_log_level().

+ Here is the call graph for this function:

◆ fers_run_simulation()

int fers_run_simulation ( fers_context_t context,
fers_progress_callback_t  callback,
void *  user_data 
)

Runs the simulation defined in the provided context.

This function is synchronous and will block the calling thread until the simulation is complete. This design keeps the API simple. For use in a responsive UI, it is the responsibility of the caller (e.g., the Tauri backend) to invoke this function on a separate worker thread to avoid freezing the user interface.

Parameters
contextA valid fers_context_t handle containing a loaded scenario.
callbackA function pointer to a progress callback. Can be NULL.
user_dataAn opaque pointer passed to the callback function.
Returns
0 on success, a non-zero error code on failure. Use fers_get_last_error_message() to retrieve error details.

Definition at line 760 of file api.cpp.

761{
762 last_error_message.clear();
763 if (context == nullptr)
764 {
765 last_error_message = "Invalid context provided to fers_run_simulation.";
767 return -1;
768 }
769
770 auto* ctx = reinterpret_cast<FersContext*>(context);
771
772 // Wrap the C-style callback in a std::function for easier use in C++.
773 // This also handles the case where the callback is null.
774 std::function<void(const std::string&, int, int)> progress_fn;
775 if (callback != nullptr)
776 {
777 progress_fn = [callback, user_data](const std::string& msg, const int current, const int total)
778 { callback(msg.c_str(), current, total, user_data); };
779 }
780
781 try
782 {
784
785 ctx->clearLastOutputMetadata();
786 const auto output_metadata = core::runEventDrivenSim(ctx->getWorld(), pool, progress_fn, ctx->getOutputDir());
787 ctx->setLastOutputMetadata(output_metadata);
788
789 return 0;
790 }
791 catch (const std::exception& e)
792 {
793 handle_api_exception(e, "fers_run_simulation");
794 return 1;
795 }
796}
A simple thread pool implementation.
Definition thread_pool.h:29
OutputMetadata runEventDrivenSim(World *world, pool::ThreadPool &pool, const std::function< void(const std::string &, int, int)> &progress_callback, const std::string &output_dir)
Runs the unified, event-driven radar simulation.
unsigned renderThreads() noexcept
Get the number of worker threads.
Definition parameters.h:145

References logging::ERROR, handle_api_exception(), last_error_message, LOG, params::renderThreads(), and core::runEventDrivenSim().

Referenced by main().

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

◆ fers_set_log_callback()

void fers_set_log_callback ( fers_log_callback_t  callback,
void *  user_data 
)

Registers a callback for formatted log lines.

Pass NULL as callback to disable log callbacks.

Definition at line 214 of file api.cpp.

215{
216 {
217 std::scoped_lock lock(log_callback_mutex);
218 log_callback = callback;
219 log_callback_user_data = user_data;
220 }
221
222 logging::logger.setCallback(callback == nullptr ? nullptr : forward_log_callback, nullptr);
223}
void setCallback(Callback callback, void *user_data) noexcept
Sets an optional callback that receives each formatted log line.
Definition logging.cpp:83

References logging::logger, and logging::Logger::setCallback().

+ Here is the call graph for this function:

◆ fers_set_output_directory()

int fers_set_output_directory ( fers_context_t context,
const char *  out_dir 
)

Sets the output directory for simulation results.

Parameters
contextA valid fers_context_t handle.
out_dirA null-terminated UTF-8 string for the output directory path.
Returns
0 on success, non-zero on error.

Definition at line 252 of file api.cpp.

253{
254 last_error_message.clear();
255 if ((context == nullptr) || (out_dir == nullptr))
256 {
257 last_error_message = "Invalid arguments: context or out_dir is NULL.";
259 return -1;
260 }
261 auto* ctx = reinterpret_cast<FersContext*>(context);
262 try
263 {
264 ctx->setOutputDir(out_dir);
265 return 0;
266 }
267 catch (const std::exception& e)
268 {
269 handle_api_exception(e, "fers_set_output_directory");
270 return 1;
271 }
272}
void setOutputDir(std::string dir)
Sets the output directory for simulation results.

References logging::ERROR, handle_api_exception(), last_error_message, LOG, and FersContext::setOutputDir().

Referenced by main().

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

◆ fers_set_thread_count()

int fers_set_thread_count ( unsigned  num_threads)

Sets the number of worker threads for the simulation.

Parameters
num_threadsThe number of threads to use.
Returns
0 on success, non-zero on error. This function clears any previous thread-local error message at entry, like the other fallible API calls.

Definition at line 233 of file api.cpp.

234{
235 last_error_message.clear();
236 try
237 {
238 if (auto res = params::setThreads(num_threads); !res)
239 {
240 last_error_message = res.error();
241 return 1;
242 }
243 return 0;
244 }
245 catch (const std::exception& e)
246 {
247 handle_api_exception(e, "fers_set_thread_count");
248 return 1;
249 }
250}
std::expected< void, std::string > setThreads(const unsigned threads) noexcept
Set the number of worker threads.
Definition parameters.h:260

References handle_api_exception(), last_error_message, and params::setThreads().

Referenced by main().

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

◆ fers_update_antenna_from_json()

int fers_update_antenna_from_json ( fers_context_t context,
const char *  json 
)

Updates a single antenna from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
jsonThe JSON string for the antenna.
Returns
0 on success, non-zero on failure.

Definition at line 511 of file api.cpp.

512{
513 last_error_message.clear();
514 if ((context == nullptr) || (json == nullptr))
515 return -1;
516 auto* ctx = reinterpret_cast<FersContext*>(context);
517 try
518 {
519 auto j = nlohmann::json::parse(json);
520 auto id = j.at("id").is_string() ? std::stoull(j.at("id").get<std::string>()) : j.at("id").get<uint64_t>();
521 auto* ant = ctx->getWorld()->findAntenna(id);
522 if (ant == nullptr)
523 {
524 last_error_message = "Antenna not found";
525 return 1;
526 }
527 serial::update_antenna_from_json(j, ant, *ctx->getWorld());
528 return 0;
529 }
530 catch (const std::exception& e)
531 {
532 handle_api_exception(e, "fers_update_antenna_from_json");
533 return 1;
534 }
535}
void update_antenna_from_json(const nlohmann::json &j, antenna::Antenna *ant, core::World &world)
Updates an antenna from JSON without full context recreation.

References handle_api_exception(), last_error_message, and serial::update_antenna_from_json().

+ Here is the call graph for this function:

◆ fers_update_monostatic_from_json()

int fers_update_monostatic_from_json ( fers_context_t context,
const char *  json 
)

Updates a monostatic radar from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
jsonThe JSON string for the monostatic component.
Returns
0 on success, non-zero on failure.

Definition at line 635 of file api.cpp.

636{
637 last_error_message.clear();
638 if ((context == nullptr) || (json == nullptr))
639 return -1;
640 auto* ctx = reinterpret_cast<FersContext*>(context);
641 try
642 {
643 auto j = nlohmann::json::parse(json);
644 uint64_t tx_id =
645 j.at("tx_id").is_string() ? std::stoull(j.at("tx_id").get<std::string>()) : j.at("tx_id").get<uint64_t>();
646 uint64_t rx_id =
647 j.at("rx_id").is_string() ? std::stoull(j.at("rx_id").get<std::string>()) : j.at("rx_id").get<uint64_t>();
648 auto* tx = ctx->getWorld()->findTransmitter(tx_id);
649 auto* rx = ctx->getWorld()->findReceiver(rx_id);
650 if ((tx == nullptr) || (rx == nullptr))
651 {
652 last_error_message = "Monostatic components not found";
653 return 1;
654 }
655 serial::update_monostatic_from_json(j, tx, rx, *ctx->getWorld(), ctx->getMasterSeeder());
656 return 0;
657 }
658 catch (const std::exception& e)
659 {
660 handle_api_exception(e, "fers_update_monostatic_from_json");
661 return 1;
662 }
663}
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.

References handle_api_exception(), last_error_message, and serial::update_monostatic_from_json().

+ Here is the call graph for this function:

◆ fers_update_parameters_from_json()

int fers_update_parameters_from_json ( fers_context_t context,
const char *  json 
)

Updates the global simulation parameters from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
jsonThe JSON string for the parameters.
Returns
0 on success, non-zero on failure.

Definition at line 486 of file api.cpp.

487{
488 last_error_message.clear();
490 if ((context == nullptr) || (json == nullptr))
491 {
493 return -1;
494 }
495 auto* ctx = reinterpret_cast<FersContext*>(context);
496 try
497 {
498 auto j = nlohmann::json::parse(json);
499 serial::update_parameters_from_json(j, ctx->getMasterSeeder());
501 return 0;
502 }
503 catch (const std::exception& e)
504 {
506 handle_api_exception(e, "fers_update_parameters_from_json");
507 return 1;
508 }
509}
void update_parameters_from_json(const nlohmann::json &j, std::mt19937 &masterSeeder)
Updates global simulation parameters from JSON.

References begin_warning_capture(), complete_warning_capture(), discard_warning_capture(), handle_api_exception(), last_error_message, and serial::update_parameters_from_json().

+ Here is the call graph for this function:

◆ fers_update_platform_from_json()

int fers_update_platform_from_json ( fers_context_t context,
uint64_t  id,
const char *  json 
)

Updates a single platform's paths and name from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
idThe unique ID of the platform.
jsonThe JSON string for the platform.
Returns
0 on success, non-zero on failure.

Definition at line 450 of file api.cpp.

451{
452 last_error_message.clear();
454 if ((context == nullptr) || (json == nullptr))
455 {
457 return -1;
458 }
459 auto* ctx = reinterpret_cast<FersContext*>(context);
460 try
461 {
462 auto* p = ctx->getWorld()->findPlatform(id);
463 if (p == nullptr)
464 {
465 last_error_message = "Platform not found";
467 return 1;
468 }
469 auto j = nlohmann::json::parse(json);
471 if (j.contains("name"))
472 {
473 p->setName(j.at("name").get<std::string>());
474 }
476 return 0;
477 }
478 catch (const std::exception& e)
479 {
481 handle_api_exception(e, "fers_update_platform_from_json");
482 return 1;
483 }
484}
core::World * getWorld() const noexcept
Retrieves a pointer to the simulation world.
radar::Platform * findPlatform(const SimId id)
Finds a platform by ID.
Definition world.cpp:94
void update_platform_paths_from_json(const nlohmann::json &j, radar::Platform *plat)
Updates a platform's motion and rotation paths from JSON.

References begin_warning_capture(), complete_warning_capture(), discard_warning_capture(), core::World::findPlatform(), FersContext::getWorld(), handle_api_exception(), last_error_message, and serial::update_platform_paths_from_json().

+ Here is the call graph for this function:

◆ fers_update_receiver_from_json()

int fers_update_receiver_from_json ( fers_context_t context,
uint64_t  id,
const char *  json 
)

Updates a single receiver from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
idThe unique ID of the receiver.
jsonThe JSON string for the receiver.
Returns
0 on success, non-zero on failure.

Definition at line 585 of file api.cpp.

586{
587 last_error_message.clear();
588 if ((context == nullptr) || (json == nullptr))
589 return -1;
590 auto* ctx = reinterpret_cast<FersContext*>(context);
591 try
592 {
593 auto* rx = ctx->getWorld()->findReceiver(id);
594 if (rx == nullptr)
595 {
596 last_error_message = "Receiver not found";
597 return 1;
598 }
599 auto j = nlohmann::json::parse(json);
600 serial::update_receiver_from_json(j, rx, *ctx->getWorld(), ctx->getMasterSeeder());
601 return 0;
602 }
603 catch (const std::exception& e)
604 {
605 handle_api_exception(e, "fers_update_receiver_from_json");
606 return 1;
607 }
608}
radar::Receiver * findReceiver(const SimId id)
Finds a receiver by ID.
Definition world.cpp:112
void 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.

References core::World::findReceiver(), FersContext::getWorld(), handle_api_exception(), last_error_message, and serial::update_receiver_from_json().

+ Here is the call graph for this function:

◆ fers_update_scenario_from_json()

int fers_update_scenario_from_json ( fers_context_t context,
const char *  scenario_json 
)

Updates the simulation scenario from a JSON string.

This is the primary method for the UI to push its state back to the C++ core. It performs a full replacement of the existing scenario.

Parameters
contextA valid fers_context_t handle.
scenario_jsonA null-terminated UTF-8 string containing the FERS scenario in JSON format.
Returns
0 on success. 1 on generic logic error. 2 on JSON parsing/schema validation error. Use fers_get_last_error_message() to retrieve error details.

Definition at line 689 of file api.cpp.

690{
691 last_error_message.clear();
693 if ((context == nullptr) || (scenario_json == nullptr))
694 {
695 last_error_message = "Invalid arguments: context or scenario_json is NULL.";
698 return -1;
699 }
700
701 auto* ctx = reinterpret_cast<FersContext*>(context);
702 try
703 {
704 const nlohmann::json j = nlohmann::json::parse(scenario_json);
705 serial::json_to_world(j, *ctx->getWorld(), ctx->getMasterSeeder());
707
708 return 0; // Success
709 }
710 catch (const nlohmann::json::exception& e)
711 {
712 // A specific catch block for JSON errors is used to provide more
713 // detailed feedback to the client (e.g., the UI), which can help
714 // developers diagnose schema or data format issues more easily.
715 last_error_message = "JSON parsing/deserialization error: " + std::string(e.what());
716 LOG(logging::Level::ERROR, "API Error in {}: {}", "fers_update_scenario_from_json", last_error_message);
718 return 2; // JSON error
719 }
720 catch (const std::exception& e)
721 {
723 handle_api_exception(e, "fers_update_scenario_from_json");
724 return 1; // Generic error
725 }
726}
void json_to_world(const nlohmann::json &j, core::World &world, std::mt19937 &masterSeeder)
Deserializes a nlohmann::json object and reconstructs the simulation world.

References begin_warning_capture(), complete_warning_capture(), discard_warning_capture(), logging::ERROR, handle_api_exception(), serial::json_to_world(), last_error_message, and LOG.

+ Here is the call graph for this function:

◆ fers_update_target_from_json()

int fers_update_target_from_json ( fers_context_t context,
uint64_t  id,
const char *  json 
)

Updates a single target from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
idThe unique ID of the target.
jsonThe JSON string for the target.
Returns
0 on success, non-zero on failure.

Definition at line 610 of file api.cpp.

611{
612 last_error_message.clear();
613 if ((context == nullptr) || (json == nullptr))
614 return -1;
615 auto* ctx = reinterpret_cast<FersContext*>(context);
616 try
617 {
618 auto* tgt = ctx->getWorld()->findTarget(id);
619 if (tgt == nullptr)
620 {
621 last_error_message = "Target not found";
622 return 1;
623 }
624 auto j = nlohmann::json::parse(json);
625 serial::update_target_from_json(j, tgt, *ctx->getWorld(), ctx->getMasterSeeder());
626 return 0;
627 }
628 catch (const std::exception& e)
629 {
630 handle_api_exception(e, "fers_update_target_from_json");
631 return 1;
632 }
633}
radar::Target * findTarget(const SimId id)
Finds a target by ID.
Definition world.cpp:120
void 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.

References core::World::findTarget(), FersContext::getWorld(), handle_api_exception(), last_error_message, and serial::update_target_from_json().

+ Here is the call graph for this function:

◆ fers_update_timing_from_json()

int fers_update_timing_from_json ( fers_context_t context,
uint64_t  id,
const char *  json 
)

Updates a single timing source from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
idThe unique ID of the timing source.
jsonThe JSON string for the timing source.
Returns
0 on success, non-zero on failure.

Definition at line 665 of file api.cpp.

666{
667 last_error_message.clear();
668 if ((context == nullptr) || (json == nullptr))
669 return -1;
670 auto* ctx = reinterpret_cast<FersContext*>(context);
671 try
672 {
673 if (ctx->getWorld()->findTiming(id) == nullptr)
674 {
675 last_error_message = "Timing not found";
676 return 1;
677 }
678 auto j = nlohmann::json::parse(json);
679 serial::update_timing_from_json(j, *ctx->getWorld(), id);
680 return 0;
681 }
682 catch (const std::exception& e)
683 {
684 handle_api_exception(e, "fers_update_timing_from_json");
685 return 1;
686 }
687}
void update_timing_from_json(const nlohmann::json &j, core::World &world, const SimId id)
Updates a timing source from JSON without full context recreation.

References handle_api_exception(), last_error_message, and serial::update_timing_from_json().

+ Here is the call graph for this function:

◆ fers_update_transmitter_from_json()

int fers_update_transmitter_from_json ( fers_context_t context,
uint64_t  id,
const char *  json 
)

Updates a single transmitter from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
idThe unique ID of the transmitter.
jsonThe JSON string for the transmitter.
Returns
0 on success, non-zero on failure.

Definition at line 560 of file api.cpp.

561{
562 last_error_message.clear();
563 if ((context == nullptr) || (json == nullptr))
564 return -1;
565 auto* ctx = reinterpret_cast<FersContext*>(context);
566 try
567 {
568 auto* tx = ctx->getWorld()->findTransmitter(id);
569 if (tx == nullptr)
570 {
571 last_error_message = "Transmitter not found";
572 return 1;
573 }
574 auto j = nlohmann::json::parse(json);
575 serial::update_transmitter_from_json(j, tx, *ctx->getWorld(), ctx->getMasterSeeder());
576 return 0;
577 }
578 catch (const std::exception& e)
579 {
580 handle_api_exception(e, "fers_update_transmitter_from_json");
581 return 1;
582 }
583}
radar::Transmitter * findTransmitter(const SimId id)
Finds a transmitter by ID.
Definition world.cpp:104
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.

References core::World::findTransmitter(), FersContext::getWorld(), handle_api_exception(), last_error_message, and serial::update_transmitter_from_json().

+ Here is the call graph for this function:

◆ fers_update_waveform_from_json()

int fers_update_waveform_from_json ( fers_context_t context,
const char *  json 
)

Updates a single waveform from JSON without full context recreation.

Parameters
contextA valid fers_context_t handle.
jsonThe JSON string for the waveform.
Returns
0 on success, non-zero on failure.

Definition at line 537 of file api.cpp.

538{
539 last_error_message.clear();
540 if ((context == nullptr) || (json == nullptr))
541 return -1;
542 auto* ctx = reinterpret_cast<FersContext*>(context);
543 try
544 {
545 auto j = nlohmann::json::parse(json);
547 if (wf)
548 {
549 ctx->getWorld()->replace(std::move(wf));
550 }
551 return 0;
552 }
553 catch (const std::exception& e)
554 {
555 handle_api_exception(e, "fers_update_waveform_from_json");
556 return 1;
557 }
558}
std::unique_ptr< fers_signal::RadarSignal > parse_waveform_from_json(const nlohmann::json &j)
Parses a Waveform from JSON.

References handle_api_exception(), last_error_message, and serial::parse_waveform_from_json().

+ Here is the call graph for this function:

◆ handle_api_exception()

static void handle_api_exception ( const std::exception &  e,
const std::string &  function_name 
)
static

Centralized exception handler for the C-API boundary.

This function catches standard C++ exceptions, records their what() message into the thread-local error storage, and logs the error. This prevents C++ exceptions from propagating across the FFI boundary, which would be undefined behavior.

Parameters
eThe exception that was caught.
function_nameThe name of the API function where the error occurred.

Definition at line 63 of file api.cpp.

64{
65 last_error_message = e.what();
66 LOG(logging::Level::ERROR, "API Error in {}: {}", function_name, last_error_message);
67}

References logging::ERROR, last_error_message, and LOG.

Referenced by fers_calculate_preview_links(), fers_configure_logging(), fers_context_create(), fers_generate_kml(), fers_get_antenna_pattern(), fers_get_interpolated_motion_path(), fers_get_interpolated_rotation_path(), fers_get_last_output_metadata_json(), fers_get_scenario_as_json(), fers_get_scenario_as_xml(), fers_load_scenario_from_xml_file(), fers_load_scenario_from_xml_string(), fers_run_simulation(), fers_set_output_directory(), fers_set_thread_count(), fers_update_antenna_from_json(), fers_update_monostatic_from_json(), fers_update_parameters_from_json(), fers_update_platform_from_json(), fers_update_receiver_from_json(), fers_update_scenario_from_json(), fers_update_target_from_json(), fers_update_timing_from_json(), fers_update_transmitter_from_json(), and fers_update_waveform_from_json().

+ Here is the caller graph for this function:

◆ map_api_log_level()

static logging::Level map_api_log_level ( fers_log_level_t  level)
static

Definition at line 118 of file api.cpp.

119{
120 switch (level)
121 {
122 case FERS_LOG_TRACE:
124 case FERS_LOG_DEBUG:
126 case FERS_LOG_INFO:
128 case FERS_LOG_WARNING:
130 case FERS_LOG_ERROR:
132 case FERS_LOG_FATAL:
134 case FERS_LOG_OFF:
135 return logging::Level::OFF;
136 default:
138 }
139}
@ FERS_LOG_FATAL
Definition api.h:92
@ FERS_LOG_DEBUG
Definition api.h:88
@ FERS_LOG_ERROR
Definition api.h:91
@ FERS_LOG_OFF
Definition api.h:93
@ FERS_LOG_INFO
Definition api.h:89
@ FERS_LOG_TRACE
Definition api.h:87
@ FERS_LOG_WARNING
Definition api.h:90
@ WARNING
Warning level for potentially harmful situations.
@ FATAL
Fatal level for severe error events.
@ TRACE
Trace level for detailed debugging information.
@ OFF
Special level to disable all logging.
@ DEBUG
Debug level for general debugging information.

References logging::DEBUG, logging::ERROR, logging::FATAL, FERS_LOG_DEBUG, FERS_LOG_ERROR, FERS_LOG_FATAL, FERS_LOG_INFO, FERS_LOG_OFF, FERS_LOG_TRACE, FERS_LOG_WARNING, logging::INFO, logging::OFF, logging::TRACE, and logging::WARNING.

Referenced by fers_configure_logging(), and fers_log().

+ Here is the caller graph for this function:

◆ map_internal_log_level()

static fers_log_level_t map_internal_log_level ( logging::Level  level)
static

Definition at line 141 of file api.cpp.

142{
143 switch (level)
144 {
146 return FERS_LOG_TRACE;
148 return FERS_LOG_DEBUG;
150 return FERS_LOG_INFO;
152 return FERS_LOG_WARNING;
154 return FERS_LOG_ERROR;
156 return FERS_LOG_FATAL;
158 return FERS_LOG_OFF;
159 default:
160 return FERS_LOG_INFO;
161 }
162}

References logging::DEBUG, logging::ERROR, logging::FATAL, FERS_LOG_DEBUG, FERS_LOG_ERROR, FERS_LOG_FATAL, FERS_LOG_INFO, FERS_LOG_OFF, FERS_LOG_TRACE, FERS_LOG_WARNING, logging::INFO, logging::OFF, logging::TRACE, and logging::WARNING.

Referenced by fers_get_log_level().

+ Here is the caller graph for this function:

◆ to_cpp_interp_type()

math::Path::InterpType to_cpp_interp_type ( const fers_interp_type_t  type)

Definition at line 829 of file api.cpp.

830{
831 switch (type)
832 {
838 default:
840 }
841}
@ FERS_INTERP_STATIC
Definition api.h:448
@ FERS_INTERP_LINEAR
Definition api.h:449

References FERS_INTERP_CUBIC, FERS_INTERP_LINEAR, FERS_INTERP_STATIC, math::Path::INTERP_CUBIC, math::Path::INTERP_LINEAR, and math::Path::INTERP_STATIC.

Referenced by fers_get_interpolated_motion_path().

+ Here is the caller graph for this function:

◆ to_cpp_rot_interp_type()

math::RotationPath::InterpType to_cpp_rot_interp_type ( const fers_interp_type_t  type)

Definition at line 843 of file api.cpp.

References FERS_INTERP_CUBIC, FERS_INTERP_LINEAR, FERS_INTERP_STATIC, math::RotationPath::INTERP_CUBIC, math::RotationPath::INTERP_LINEAR, and math::RotationPath::INTERP_STATIC.

Referenced by fers_get_interpolated_rotation_path().

+ Here is the caller graph for this function:

Variable Documentation

◆ last_error_message

◆ last_warning_messages

thread_local std::vector<std::string> last_warning_messages