29#include <nlohmann/json.hpp>
41#include "fers_version.h"
106 catch (
const std::bad_alloc& e)
111 catch (
const std::exception& e)
120 if (context ==
nullptr)
177 constexpr std::uint64_t nanoseconds_per_second = 1'000'000'000ULL;
178 constexpr std::uint64_t max_vrt_utc_epoch_ns =
179 static_cast<std::uint64_t
>(std::numeric_limits<std::uint32_t>::max()) * nanoseconds_per_second +
180 (nanoseconds_per_second - 1ULL);
182 void set_api_error(std::string message)
188 [[nodiscard]]
bool is_valid_vita49_epoch(
const std::uint64_t epoch_unix_nanoseconds)
noexcept
190 return epoch_unix_nanoseconds <= max_vrt_utc_epoch_ns;
193 [[nodiscard]]
bool is_valid_vita49_fullscale(
const double fullscale)
noexcept
195 return std::isfinite(fullscale) && fullscale > 0.0;
198 [[nodiscard]]
bool is_valid_vita49_max_payload(
const std::uint16_t max_udp_payload)
noexcept
200 return max_udp_payload >= 64 && max_udp_payload <= 65507;
203 [[nodiscard]] std::optional<std::string> validate_vita49_config_for_run(
const core::OutputConfig& config)
211 return "VITA49 endpoint host must be non-empty.";
215 return "VITA49 endpoint port must be in the range 1..65535.";
219 return "VITA49 fullscale must be a positive finite value.";
223 return "VITA49 max UDP payload must be between 64 and 65507 bytes.";
227 return "VITA49 queue depth must be greater than zero.";
232 return "VITA49 epoch must fit the VRT 32-bit UTC seconds timestamp field.";
237 void copy_visual_link_label(
fers_visual_link_t& destination,
const std::string& source)
noexcept
239 const std::size_t count = std::min(source.size(),
sizeof(destination.label) - 1);
240 std::copy_n(source.begin(), count, std::begin(destination.label));
241 destination.label[count] =
'\0';
242 destination.label[
sizeof(destination.label) - 1] =
'\0';
247 auto timestamp_to_json = [](
const std::optional<core::Vita49Timestamp>& timestamp) -> nlohmann::json
249 if (!timestamp.has_value())
253 return {{
"integer_seconds", timestamp->integer_seconds},
254 {
"fractional_picoseconds", timestamp->fractional_picoseconds}};
261 {
"mode", stream.
mode},
271 {
"first_sample_time",
273 : nlohmann::json(nullptr)},
280 [[nodiscard]] std::string output_stats_to_json_string(
const core::OutputStats& stats)
282 nlohmann::json streams = nlohmann::json::array();
283 for (
const auto& stream : stats.streams)
285 streams.push_back(stream_stats_to_json(stream));
289 {
"epoch_unix_nanoseconds",
nullptr},
290 {
"streams", streams}};
295 return result.dump();
298 [[nodiscard]] std::string
299 packet_trace_batch_to_json_string(std::span<const core::ReceiverOutputPacketTrace> packets)
301 auto timestamp_to_json = [](
const std::optional<core::Vita49Timestamp>& timestamp) -> nlohmann::json
303 if (!timestamp.has_value())
307 return {{
"integer_seconds", timestamp->integer_seconds},
308 {
"fractional_picoseconds", timestamp->fractional_picoseconds}};
311 nlohmann::json batch = nlohmann::json::array();
312 for (
const auto& packet : packets)
314 batch.push_back({{
"sequence", packet.sequence},
315 {
"event", packet.event},
316 {
"stream_id", packet.stream_id},
317 {
"byte_count", packet.byte_count},
318 {
"sample_count", packet.sample_count},
319 {
"first_sample_time", packet.first_sample_time},
320 {
"timestamp", timestamp_to_json(packet.timestamp)},
321 {
"data_packet", packet.data_packet},
322 {
"context_packet", packet.context_packet},
323 {
"dropped", packet.dropped},
324 {
"over_range", packet.over_range},
325 {
"sample_loss", packet.sample_loss}});
330 std::mutex log_callback_mutex;
332 void* log_callback_user_data =
nullptr;
335 void forward_log_callback(
const logging::Level level,
const std::string& line,
void* )
338 void* user_data =
nullptr;
341 std::scoped_lock
const lock(log_callback_mutex);
342 callback = log_callback;
343 user_data = log_callback_user_data;
346 if (callback !=
nullptr)
359 if ((log_file_path !=
nullptr) && ((*log_file_path) != 0))
370 catch (
const std::exception& e)
384 std::scoped_lock
const lock(log_callback_mutex);
385 log_callback = callback;
386 log_callback_user_data = user_data;
394 if (message ==
nullptr)
412 catch (
const std::exception& e)
422 if ((context ==
nullptr) || (out_dir ==
nullptr))
424 set_api_error(
"Invalid arguments: context or out_dir is NULL.");
433 catch (
const std::exception& e)
443 if (context ==
nullptr)
445 set_api_error(
"Invalid arguments: context is NULL.");
454 ctx->setOutputConfig(std::move(config));
457 catch (
const std::exception& e)
467 if (context ==
nullptr)
469 set_api_error(
"Invalid arguments: context is NULL.");
474 set_api_error(
"Invalid VITA49 endpoint: host is NULL.");
479 set_api_error(
"Invalid VITA49 endpoint: host must be non-empty.");
484 set_api_error(
"Invalid VITA49 endpoint: port must be in the range 1..65535.");
495 ctx->setOutputConfig(std::move(config));
498 catch (
const std::exception& e)
508 if (context ==
nullptr)
510 set_api_error(
"Invalid arguments: context is NULL.");
513 if (!is_valid_vita49_fullscale(fullscale))
515 set_api_error(
"Invalid VITA49 fullscale: value must be positive and finite.");
522 ctx->setOutputConfig(std::move(config));
529 if (context ==
nullptr)
531 set_api_error(
"Invalid arguments: context is NULL.");
534 if (!is_valid_vita49_epoch(epoch_unix_nanoseconds))
536 set_api_error(
"Invalid VITA49 epoch: value must fit the VRT 32-bit UTC seconds timestamp field.");
543 ctx->setOutputConfig(std::move(config));
550 if (context ==
nullptr)
552 set_api_error(
"Invalid arguments: context is NULL.");
555 if (!is_valid_vita49_max_payload(max_udp_payload))
557 set_api_error(
"Invalid VITA49 max UDP payload: value must be between 64 and 65507 bytes.");
564 ctx->setOutputConfig(std::move(config));
571 if (context ==
nullptr)
573 set_api_error(
"Invalid arguments: context is NULL.");
576 if (queue_depth == 0)
578 set_api_error(
"Invalid VITA49 queue depth: value must be greater than zero.");
585 ctx->setOutputConfig(std::move(config));
592 if (context ==
nullptr)
594 set_api_error(
"Invalid arguments: context is NULL.");
601 ctx->setOutputConfig(std::move(config));
609 if ((context ==
nullptr) || (xml_filepath ==
nullptr))
621 std::filesystem::path
const p(xml_filepath);
622 auto parent = p.parent_path();
625 ctx->setOutputDir(parent.string());
640 const auto seed = std::random_device{}();
643 ctx->getMasterSeeder().seed(seed);
648 catch (
const std::exception& e)
660 if ((context ==
nullptr) || (xml_content ==
nullptr))
672 ctx->getMasterSeeder());
684 const auto seed = std::random_device{}();
687 ctx->getMasterSeeder().seed(seed);
693 catch (
const std::exception& e)
704 if (context ==
nullptr)
711 const auto* ctx = context;
715 const std::string json_str = j.dump(2);
719 return strdup(json_str.c_str());
721 catch (
const std::exception& e)
731 if (context ==
nullptr)
738 const auto* ctx = context;
744 throw std::runtime_error(
"XML serialization resulted in an empty string.");
749 return strdup(xml_str.c_str());
751 catch (
const std::exception& e)
761 if (context ==
nullptr)
763 last_error_message =
"Invalid context provided to fers_get_last_output_metadata_json.";
768 const auto* ctx = context;
772 return strdup(json_str.c_str());
774 catch (
const std::exception& e)
784 if (context ==
nullptr)
797 return strdup(json_str.c_str());
799 catch (
const std::exception& e)
810 if ((context ==
nullptr) || (json ==
nullptr))
825 auto j = nlohmann::json::parse(json);
827 if (j.contains(
"name"))
829 p->setName(j.at(
"name").get<std::string>());
834 catch (
const std::exception& e)
846 if ((context ==
nullptr) || (json ==
nullptr))
854 auto j = nlohmann::json::parse(json);
859 catch (
const std::exception& e)
870 if ((context ==
nullptr) || (json ==
nullptr))
875 auto j = nlohmann::json::parse(json);
876 auto id = j.at(
"id").is_string() ? std::stoull(j.at(
"id").get<std::string>()) : j.at(
"id").get<uint64_t>();
877 auto* ant = ctx->getWorld()->findAntenna(
id);
886 catch (
const std::exception& e)
896 if ((context ==
nullptr) || (json ==
nullptr))
901 auto j = nlohmann::json::parse(json);
905 ctx->getWorld()->replace(std::move(wf));
909 catch (
const std::exception& e)
919 if ((context ==
nullptr) || (json ==
nullptr))
930 auto j = nlohmann::json::parse(json);
934 catch (
const std::exception& e)
944 if ((context ==
nullptr) || (json ==
nullptr))
955 auto j = nlohmann::json::parse(json);
959 catch (
const std::exception& e)
969 if ((context ==
nullptr) || (json ==
nullptr))
980 auto j = nlohmann::json::parse(json);
984 catch (
const std::exception& e)
994 if ((context ==
nullptr) || (json ==
nullptr))
999 auto j = nlohmann::json::parse(json);
1000 uint64_t
const tx_id =
1001 j.at(
"tx_id").is_string() ? std::stoull(j.at(
"tx_id").get<std::string>()) : j.at(
"tx_id").get<uint64_t>();
1002 uint64_t
const rx_id =
1003 j.at(
"rx_id").is_string() ? std::stoull(j.at(
"rx_id").get<std::string>()) : j.at(
"rx_id").get<uint64_t>();
1004 auto* tx = ctx->getWorld()->findTransmitter(tx_id);
1005 auto* rx = ctx->getWorld()->findReceiver(rx_id);
1006 if ((tx ==
nullptr) || (rx ==
nullptr))
1014 catch (
const std::exception& e)
1024 if ((context ==
nullptr) || (json ==
nullptr))
1026 auto* ctx = context;
1029 if (ctx->getWorld()->findTiming(
id) ==
nullptr)
1034 auto j = nlohmann::json::parse(json);
1038 catch (
const std::exception& e)
1049 if ((context ==
nullptr) || (scenario_json ==
nullptr))
1057 auto* ctx = context;
1060 const nlohmann::json j = nlohmann::json::parse(scenario_json);
1066 catch (
const nlohmann::json::exception& e)
1076 catch (
const std::exception& e)
1106 return strdup(warning_json.c_str());
1120 struct SimulationRunRequest
1124 void* progress_user_data;
1126 void* cancel_user_data;
1128 void* vita49_telemetry_user_data;
1129 const char* function_name;
1130 const char* invalid_context_message;
1133 int run_simulation_common(
const SimulationRunRequest& request)
1136 if (request.context ==
nullptr)
1143 auto* ctx = request.context;
1145 std::function<void(
const std::string&,
int,
int)> progress_fn;
1146 if (request.progress_callback !=
nullptr)
1148 progress_fn = [&request](
const std::string& msg,
const int current,
const int total)
1149 { request.progress_callback(msg.c_str(), current, total, request.progress_user_data); };
1152 std::function<bool()> cancel_fn;
1153 if (request.cancel_callback !=
nullptr)
1155 cancel_fn = [&request] {
return request.cancel_callback(request.cancel_user_data) != 0; };
1159 if (request.vita49_telemetry_callback !=
nullptr)
1161 telemetry_fn = [&request](
const std::optional<core::OutputStats>& stats,
1162 std::span<const core::ReceiverOutputPacketTrace> packets)
1164 std::string stats_json;
1165 std::string packet_batch_json;
1166 const char* stats_ptr =
nullptr;
1167 const char* packet_batch_ptr =
nullptr;
1169 if (stats.has_value())
1171 stats_json = output_stats_to_json_string(*stats);
1172 stats_ptr = stats_json.c_str();
1174 if (!packets.empty())
1176 packet_batch_json = packet_trace_batch_to_json_string(packets);
1177 packet_batch_ptr = packet_batch_json.c_str();
1180 request.vita49_telemetry_callback(stats_ptr, packet_batch_ptr, request.vita49_telemetry_user_data);
1186 if (
const auto validation_error = validate_vita49_config_for_run(ctx->getOutputConfig()))
1188 set_api_error(*validation_error);
1194 ctx->clearLastOutputMetadata();
1195 bool cancelled =
false;
1196 auto output_metadata =
1198 std::move(cancel_fn), &cancelled, std::move(telemetry_fn));
1199 ctx->setLastOutputMetadata(output_metadata);
1201 return cancelled ? 2 : 0;
1203 catch (
const std::exception& e)
1213 return run_simulation_common(
1214 SimulationRunRequest{.context = context,
1215 .progress_callback = callback,
1216 .progress_user_data = user_data,
1217 .cancel_callback =
nullptr,
1218 .cancel_user_data =
nullptr,
1219 .vita49_telemetry_callback =
nullptr,
1220 .vita49_telemetry_user_data =
nullptr,
1221 .function_name =
"fers_run_simulation",
1222 .invalid_context_message =
"Invalid context provided to fers_run_simulation."});
1229 return run_simulation_common(
1230 SimulationRunRequest{.context = context,
1231 .progress_callback = progress_callback,
1232 .progress_user_data = progress_user_data,
1233 .cancel_callback = cancel_callback,
1234 .cancel_user_data = cancel_user_data,
1235 .vita49_telemetry_callback = vita49_telemetry_callback,
1236 .vita49_telemetry_user_data = vita49_telemetry_user_data,
1237 .function_name =
"fers_run_simulation_ex",
1238 .invalid_context_message =
"Invalid context provided to fers_run_simulation_ex."});
1244 if ((context ==
nullptr) || (output_kml_filepath ==
nullptr))
1251 const auto* ctx = context;
1265 catch (
const std::exception& e)
1303 const size_t waypoint_count,
1305 const size_t num_points)
1308 if ((waypoints ==
nullptr) || waypoint_count == 0 || num_points == 0)
1310 last_error_message =
"Invalid arguments: waypoints cannot be null and counts must be > 0.";
1326 for (
size_t i = 0; i < waypoint_count; ++i)
1330 c.pos.x = waypoints[i].
x;
1331 c.pos.y = waypoints[i].
y;
1332 c.pos.z = waypoints[i].
z;
1342 result_path->count = num_points;
1344 const double start_time = waypoints[0].
time;
1345 const double end_time = waypoints[waypoint_count - 1].
time;
1346 const double duration = end_time - start_time;
1349 if (waypoint_count < 2 || duration <= 0)
1352 for (
size_t i = 0; i < num_points; ++i)
1354 result_path->points[i] = {pos.
x, pos.
y, pos.
z, 0.0, 0.0, 0.0};
1359 const double time_step =
1360 duration /
static_cast<double>(num_points > 1 ? num_points - 1 :
static_cast<size_t>(1));
1362 for (
size_t i = 0; i < num_points; ++i)
1364 const double t = start_time +
static_cast<double>(i) * time_step;
1367 result_path->points[i] = {pos.
x, pos.y, pos.z, vel.
x, vel.
y, vel.
z};
1372 catch (
const std::exception& e)
1381 if (path !=
nullptr)
1392 const size_t waypoint_count,
1395 const size_t num_points)
1400 if ((waypoints ==
nullptr) || waypoint_count == 0 || num_points == 0)
1402 last_error_message =
"Invalid arguments: waypoints cannot be null and counts must be > 0.";
1420 for (
size_t i = 0; i < waypoint_count; ++i)
1424 std::format(
"rotation waypoint {}", i),
"azimuth");
1427 std::format(
"rotation waypoint {}", i),
"elevation");
1429 waypoints[i].azimuth, waypoints[i].elevation, waypoints[i].time, unit));
1438 result_path->count = num_points;
1440 const double start_time = waypoints[0].
time;
1441 const double end_time = waypoints[waypoint_count - 1].
time;
1442 const double duration = end_time - start_time;
1445 if (waypoint_count < 2 || duration <= 0)
1448 for (
size_t i = 0; i < num_points; ++i)
1457 const double time_step =
1458 duration /
static_cast<double>(num_points > 1 ? num_points - 1 :
static_cast<size_t>(1));
1460 for (
size_t i = 0; i < num_points; ++i)
1462 const double t = start_time +
static_cast<double>(i) * time_step;
1472 catch (
const std::exception& e)
1482 if (path !=
nullptr)
1495 const size_t az_samples,
const size_t el_samples,
1496 const double frequency_hz)
1499 if ((context ==
nullptr) || az_samples < 2 || el_samples < 2)
1501 last_error_message =
"Invalid arguments: context must be non-null and sample counts must be >= 2.";
1508 const auto* ctx = context;
1513 last_error_message =
"Antenna ID '" + std::to_string(antenna_id) +
"' not found in the world.";
1526 if (frequency_hz > 0.0)
1528 wavelength =
params::c() / frequency_hz;
1533 data->az_count = az_samples;
1534 data->el_count = el_samples;
1535 const size_t total_samples = az_samples * el_samples;
1537 data->gains =
new double[total_samples];
1542 double max_gain = 0.0;
1544 const auto az_denominator =
static_cast<RealType>(az_samples - 1);
1545 const auto el_denominator =
static_cast<RealType>(el_samples - 1);
1547 for (
size_t i = 0; i < el_samples; ++i)
1551 for (
size_t j = 0; j < az_samples; ++j)
1555 const math::SVec3 sample_angle(1.0, azimuth, elevation);
1556 const RealType gain = ant->
getGain(sample_angle, ref_angle, wavelength);
1557 data->gains[i * az_samples + j] = gain;
1558 max_gain = std::max(gain, max_gain);
1562 data->max_gain = max_gain;
1567 for (
size_t i = 0; i < total_samples; ++i)
1569 data->gains[i] /= max_gain;
1575 catch (
const std::exception& e)
1584 if (data !=
nullptr)
1587 delete[] data->
gains;
1598 if (context ==
nullptr)
1607 const auto* ctx = context;
1614 result->count = cpp_links.size();
1616 if (!cpp_links.empty())
1620 for (
size_t i = 0; i < result->count; ++i)
1622 const auto& src = cpp_links[i];
1623 auto& dst = result->links[i];
1644 copy_visual_link_label(dst, src.label);
1646 dst.source_id =
static_cast<uint64_t
>(src.source_id);
1647 dst.dest_id =
static_cast<uint64_t
>(src.dest_id);
1648 dst.origin_id =
static_cast<uint64_t
>(src.origin_id);
1650 dst.actual_power_dbm = src.actual_power_dbm;
1651 dst.display_value = src.display_value;
1656 result->links =
nullptr;
1660 catch (
const std::exception& e)
1669 if (list !=
nullptr)
1672 delete[] list->
links;
Header file defining various types of antennas and their gain patterns.
static void discard_warning_capture() noexcept
char * fers_get_scenario_as_xml(fers_context_t *context)
Serializes the current simulation scenario into a FERS XML string.
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_run_simulation_ex(fers_context_t *context, fers_progress_callback_t progress_callback, void *progress_user_data, fers_cancel_callback_t cancel_callback, void *cancel_user_data, fers_vita49_telemetry_callback_t vita49_telemetry_callback, void *vita49_telemetry_user_data)
Runs the simulation with optional progress, cancellation, and VITA telemetry callbacks.
void fers_free_antenna_pattern_data(fers_antenna_pattern_data_t *data)
Frees the memory allocated for an antenna pattern data structure.
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.
void fers_log(fers_log_level_t level, const char *message)
Submits a log message to the library's unified logging system.
char * fers_get_last_warning_messages_json()
Returns the last deduplicated rotation-unit warning list for the calling thread as JSON.
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_set_vita49_queue_depth(fers_context_t *context, const std::uint32_t queue_depth)
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_use_hdf5_output(fers_context_t *context)
Resets the context output mode to the default HDF5 output.
char * fers_get_last_error_message()
Retrieves the last error message that occurred on the current thread.
int fers_enable_vita49_udp_output(fers_context_t *context, const char *host, const std::uint16_t port)
void fers_free_interpolated_motion_path(fers_interpolated_path_t *path)
Frees the memory allocated for an interpolated motion path.
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_generate_kml(const fers_context_t *context, const char *output_kml_filepath)
Generates a KML file for visualizing the scenario in the context.
char * fers_get_memory_projection_json(fers_context_t *context)
Returns a JSON projection of simulation startup memory and HDF5 payload size.
int fers_set_output_directory(fers_context_t *context, const char *out_dir)
Sets the output directory for simulation results.
void fers_context_destroy(fers_context_t *context)
Destroys a FERS simulation context and releases all associated memory.
static void handle_api_exception(const std::exception &e, const std::string &function_name)
Centralized exception handler for the C-API boundary.
fers_context_t * fers_context_create()
Creates a new FERS simulation context.
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.
void fers_free_preview_links(fers_visual_link_list_t *list)
Frees the memory allocated for a preview link list.
int fers_set_vita49_max_udp_payload(fers_context_t *context, const std::uint16_t max_udp_payload)
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.
fers_interpolated_path_t * fers_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.
int fers_run_simulation(fers_context_t *context, fers_progress_callback_t callback, void *user_data)
Runs the simulation defined in the provided context.
void fers_free_string(char *str)
Frees a string that was allocated and returned by the libfers API.
fers_log_level_t fers_get_log_level()
Returns the current internal logger level.
static void begin_warning_capture() noexcept
math::RotationPath::InterpType to_cpp_rot_interp_type(const fers_interp_type_t type)
fers_antenna_pattern_data_t * fers_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.
const char * fers_get_version(void)
Returns the library version string.
thread_local std::vector< std::string > last_warning_messages
int fers_configure_logging(fers_log_level_t level, const char *log_file_path)
Configures the internal logger.
void fers_free_interpolated_rotation_path(fers_interpolated_rotation_path_t *path)
Frees the memory allocated for an interpolated rotation path.
math::Path::InterpType to_cpp_interp_type(const fers_interp_type_t type)
void fers_set_log_callback(fers_log_callback_t callback, void *user_data)
Registers a callback for formatted log lines.
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.
fers_visual_link_list_t * fers_calculate_preview_links(const fers_context_t *context, const double time)
Calculates visual links for a specific simulation time.
char * fers_get_scenario_as_json(fers_context_t *context)
Serializes the current simulation scenario into a JSON string.
int fers_set_vita49_packet_trace_enabled(fers_context_t *context, const int enabled)
Enables or disables FERS VITA 49.2 packet trace telemetry.
static void complete_warning_capture()
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_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_set_vita49_epoch_unix_nanoseconds(fers_context_t *context, const std::uint64_t epoch_unix_nanoseconds)
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.
static logging::Level map_api_log_level(fers_log_level_t level)
fers_interpolated_rotation_path_t * fers_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.
static fers_log_level_t map_internal_log_level(logging::Level level)
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_output_metadata_json(fers_context_t *context)
Returns JSON metadata for the most recent simulation output files.
int fers_set_thread_count(unsigned num_threads)
Sets the number of worker threads for the simulation.
int fers_set_vita49_fullscale(fers_context_t *context, const double fullscale)
Sets the fixed ADC full-scale value used by the FERS VITA 49.2 int16 IQ profile.
thread_local std::string last_error_message
fers_angle_unit_t
Units used for external rotation angles and rates.
void(* fers_progress_callback_t)(const char *message, int current, int total, void *user_data)
A function pointer type for progress reporting callbacks.
int(* fers_cancel_callback_t)(void *user_data)
A function pointer type for cooperative simulation cancellation.
void(* fers_log_callback_t)(fers_log_level_t level, const char *line, void *user_data)
A function pointer type for receiving formatted log lines.
fers_log_level_t
Log levels for the FERS library.
@ FERS_LOG_FATAL
Fatal logging for unrecoverable failures.
@ FERS_LOG_DEBUG
Debug-level diagnostic logging.
@ FERS_LOG_ERROR
Error logging for failed operations.
@ FERS_LOG_OFF
Disables logging output.
@ FERS_LOG_INFO
Informational logging.
@ FERS_LOG_TRACE
Trace-level diagnostic logging.
@ FERS_LOG_WARNING
Warning logging for recoverable issues.
fers_interp_type_t
Defines the interpolation methods available for path generation.
void(* fers_vita49_telemetry_callback_t)(const char *stats_json, const char *packet_batch_json, void *user_data)
A function pointer type for VITA 49.2 live telemetry callbacks.
struct fers_context fers_context_t
@ FERS_LINK_BISTATIC_TX_TGT
@ FERS_LINK_BISTATIC_TGT_RX
Header for radar channel propagation and interaction models.
Manages the lifetime and state of a single FERS simulation scenario.
void setOutputDir(std::string dir)
Sets the output directory for simulation results.
core::World * getWorld() const noexcept
Retrieves a pointer to the simulation world.
std::string getLastOutputMetadataJson() const
Serializes the last simulation output metadata as JSON.
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.
radar::Target * findTarget(const SimId id)
Finds a target by ID.
radar::Receiver * findReceiver(const SimId id)
Finds a receiver by ID.
radar::Transmitter * findTransmitter(const SimId id)
Finds a transmitter by ID.
radar::Platform * findPlatform(const SimId id)
Finds a platform by ID.
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.
std::expected< void, std::string > logToFile(const std::string &filePath) noexcept
Sets the log file path to log messages to a file.
void setCallback(Callback callback, void *user_data) noexcept
Sets an optional callback that receives each formatted log line.
void setLevel(Level level) noexcept
Sets the logging level.
Represents a path with coordinates and allows for various interpolation methods.
Vec3 getPosition(RealType t) const
Retrieves the position at a given time along the path.
InterpType
Types of interpolation supported by the Path class.
@ 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.
Vec3 getVelocity(RealType t) const
Retrieves the velocity at a given time along the path.
void addCoord(const Coord &coord) noexcept
Adds a coordinate to the path.
void finalize()
Finalizes the path, preparing it for interpolation.
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.
InterpType
Enumeration for types of interpolation.
@ INTERP_STATIC
Hold the first rotation for all query times.
@ INTERP_LINEAR
Linearly interpolate between neighboring rotations.
@ INTERP_CUBIC
Cubically interpolate between neighboring rotations.
A class representing a vector in spherical coordinates.
RealType elevation
The elevation angle of the vector.
RealType azimuth
The azimuth angle of the vector.
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 simple thread pool implementation.
static std::expected< void, std::string > generateKml(const core::World &world, const std::string &outputKmlPath)
Generates a KML file from a pre-built simulation world.
double RealType
Type for real numbers.
constexpr RealType PI
Mathematical constant π (pi).
Internal C++ class that encapsulates the state of a simulation instance.
Provides functions to serialize and deserialize the simulation world to/from JSON.
KML file generator for geographical visualization of FERS scenarios.
Header file for the logging system.
Startup memory and output-size projection helpers for simulations.
OutputMetadata runEventDrivenSim(World *world, pool::ThreadPool &pool, const std::function< void(const std::string &, int, int)> &progress_callback, const std::string &output_dir, const OutputConfig &output_config, std::function< bool()> cancel_callback, bool *cancelled, ReceiverOutputTelemetryCallback telemetry_callback)
Runs the unified, event-driven radar simulation.
std::function< void(const std::optional< OutputStats > &, std::span< const ReceiverOutputPacketTrace >)> ReceiverOutputTelemetryCallback
std::string memoryProjectionToJsonString(const SimulationMemoryProjection &projection)
Serializes a simulation memory projection as JSON.
bool isVita49Enabled(const OutputConfig &config) noexcept
SimulationMemoryProjection projectSimulationMemory(const World &world)
Projects startup memory and rendered-output sizes for a simulation world.
@ WARNING
Warning level for potentially harmful situations.
@ FATAL
Fatal level for severe error events.
@ TRACE
Trace level for detailed debugging information.
@ INFO
Info level for informational messages.
@ OFF
Special level to disable all logging.
@ ERROR
Error level for error events.
@ DEBUG
Debug level for general debugging information.
Logger logger
Externally available logger object.
unsigned renderThreads() noexcept
Get the number of worker threads.
std::expected< void, std::string > setThreads(const unsigned threads) noexcept
Set the number of worker threads.
@ Radians
Compass azimuth and elevation expressed in radians.
@ Degrees
Compass azimuth and elevation expressed in degrees.
Parameters params
Global simulation parameter state.
RealType c() noexcept
Get the speed of light.
RealType internal_elevation_to_external(const RealType elevation, const params::RotationAngleUnit unit) noexcept
Converts an internal elevation angle to the external unit.
math::RotationCoord external_rotation_to_internal(const RealType azimuth, const RealType elevation, const RealType time, const params::RotationAngleUnit unit) noexcept
Converts external compass azimuth/elevation into internal rotation coordinates.
RealType internal_azimuth_to_external(const RealType azimuth, const params::RotationAngleUnit unit) noexcept
Converts an internal azimuth angle to the external compass convention.
std::vector< std::string > take_captured_warnings()
Returns and clears the thread-local captured rotation warnings.
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)
Emits or captures a warning when a rotation value likely uses the wrong unit.
@ Angle
Absolute rotation angle.
void clear_captured_warnings() noexcept
Clears the thread-local captured rotation warnings.
void update_platform_paths_from_json(const nlohmann::json &j, radar::Platform *plat)
Updates a platform's motion and rotation paths from JSON.
void update_parameters_from_json(const nlohmann::json &j, std::mt19937 &masterSeeder)
Updates global simulation parameters from JSON.
void json_to_world(const nlohmann::json &j, core::World &world, std::mt19937 &masterSeeder)
Deserializes a nlohmann::json object and reconstructs the simulation world.
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.
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.
void update_monostatic_from_json(const nlohmann::json &j, radar::Transmitter *tx, radar::Receiver *rx, core::World &world, std::mt19937 &masterSeeder)
Updates a monostatic radar from JSON without full context recreation.
void update_transmitter_from_json(const nlohmann::json &j, radar::Transmitter *tx, core::World &world, std::mt19937 &)
Updates a transmitter from JSON without full context recreation.
void parseSimulation(const std::string &filename, core::World *world, const bool validate, std::mt19937 &masterSeeder)
Parses a simulation configuration from an XML file.
void update_antenna_from_json(const nlohmann::json &j, antenna::Antenna *ant, core::World &world)
Updates an antenna from JSON without full context recreation.
void update_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.
nlohmann::json world_to_json(const core::World &world)
Serializes the entire simulation world into a nlohmann::json object.
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.
std::string world_to_xml_string(const core::World &world)
Serializes the entire simulation world into an XML formatted string.
std::unique_ptr< fers_signal::RadarSignal > parse_waveform_from_json(const nlohmann::json &j)
Parses a Waveform from JSON.
@ DirectTxRx
Interference path.
@ Monostatic
Combined Tx/Rx path.
@ BistaticTgtRx
Scattered path.
@ BistaticTxTgt
Illuminator path.
std::vector< PreviewLink > calculatePreviewLinks(const core::World &world, const RealType time)
Calculates all visual links for the current world state at a specific time.
Defines the Parameters struct and provides methods for managing simulation parameters.
Provides the definition and functionality of the Path class for handling coordinate-based paths with ...
Classes for handling radar waveforms and signals.
Defines the RotationPath class for handling rotational paths with different interpolation types.
uint64_t SimId
64-bit Unique Simulation ID.
Header file for the main simulation runner.
Vita49OutputConfig vita49
std::optional< std::uint64_t > epoch_unix_nanoseconds
RealType reference_frequency
std::uint64_t packets_emitted
std::optional< Vita49Timestamp > first_timestamp
std::optional< RealType > first_sample_time
std::optional< RealType > end_sample_time
std::string receiver_name
std::uint64_t late_packet_count
std::uint64_t samples_emitted
std::uint64_t over_range_count
std::uint64_t context_packets
std::uint64_t packets_dropped
std::uint64_t samples_dropped
std::optional< Vita49Timestamp > end_timestamp
bool packet_trace_enabled
std::uint32_t queue_depth
std::uint16_t max_udp_payload
std::optional< std::uint64_t > epoch_unix_nanoseconds
Represents a sampled 2D antenna gain pattern.
double * gains
Flat array of gain values [el_count * az_count].
A container for an array of interpolated motion path points.
fers_interpolated_point_t * points
Heap-allocated interpolated motion points.
Represents a single interpolated point on a motion path.
A container for an array of interpolated rotation path points.
fers_interpolated_rotation_point_t * points
Heap-allocated interpolated rotation points.
Represents a single interpolated point on a rotation path.
Represents a single waypoint for a motion path.
double x
X coordinate in meters (East in ENU).
double time
Time in seconds.
double y
Y coordinate in meters (North in ENU).
double z
Z coordinate in meters (Up/Altitude in ENU).
Represents a single waypoint for a rotation path.
double time
Time in seconds.
A container for a list of visual links.
fers_visual_link_t * links
Heap-allocated visual link array.
Represents a single renderable line segment metadata.
fers_link_type_t type
Type of the link (Monostatic, Bistatic, etc.).
Represents a position in 3D space with an associated time.
std::optional< unsigned > random_seed
Random seed for simulation.
A simple thread pool implementation.
High-level facade for parsing XML configuration files into the FERS simulation environment.
Provides functions to serialize the simulation world back into the FERS XML format.