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},
272 {
"first_sample_time",
274 : nlohmann::json(nullptr)},
281 [[nodiscard]] std::string output_stats_to_json_string(
const core::OutputStats& stats)
283 nlohmann::json streams = nlohmann::json::array();
284 for (
const auto& stream : stats.streams)
286 streams.push_back(stream_stats_to_json(stream));
290 {
"epoch_unix_nanoseconds",
nullptr},
291 {
"streams", streams}};
296 return result.dump();
299 [[nodiscard]] std::string
300 packet_trace_batch_to_json_string(std::span<const core::ReceiverOutputPacketTrace> packets)
302 auto timestamp_to_json = [](
const std::optional<core::Vita49Timestamp>& timestamp) -> nlohmann::json
304 if (!timestamp.has_value())
308 return {{
"integer_seconds", timestamp->integer_seconds},
309 {
"fractional_picoseconds", timestamp->fractional_picoseconds}};
312 nlohmann::json batch = nlohmann::json::array();
313 for (
const auto& packet : packets)
315 batch.push_back({{
"sequence", packet.sequence},
316 {
"event", packet.event},
317 {
"stream_id", packet.stream_id},
318 {
"byte_count", packet.byte_count},
319 {
"sample_count", packet.sample_count},
320 {
"first_sample_time", packet.first_sample_time},
321 {
"timestamp", timestamp_to_json(packet.timestamp)},
322 {
"data_packet", packet.data_packet},
323 {
"context_packet", packet.context_packet},
324 {
"dropped", packet.dropped},
325 {
"over_range", packet.over_range},
326 {
"sample_loss", packet.sample_loss}});
331 std::mutex log_callback_mutex;
333 void* log_callback_user_data =
nullptr;
336 void forward_log_callback(
const logging::Level level,
const std::string& line,
void* )
339 void* user_data =
nullptr;
342 std::scoped_lock
const lock(log_callback_mutex);
343 callback = log_callback;
344 user_data = log_callback_user_data;
347 if (callback !=
nullptr)
360 if ((log_file_path !=
nullptr) && ((*log_file_path) != 0))
371 catch (
const std::exception& e)
385 std::scoped_lock
const lock(log_callback_mutex);
386 log_callback = callback;
387 log_callback_user_data = user_data;
395 if (message ==
nullptr)
413 catch (
const std::exception& e)
423 if ((context ==
nullptr) || (out_dir ==
nullptr))
425 set_api_error(
"Invalid arguments: context or out_dir is NULL.");
434 catch (
const std::exception& e)
444 if (context ==
nullptr)
446 set_api_error(
"Invalid arguments: context is NULL.");
455 ctx->setOutputConfig(std::move(config));
458 catch (
const std::exception& e)
468 if (context ==
nullptr)
470 set_api_error(
"Invalid arguments: context is NULL.");
475 set_api_error(
"Invalid VITA49 endpoint: host is NULL.");
480 set_api_error(
"Invalid VITA49 endpoint: host must be non-empty.");
485 set_api_error(
"Invalid VITA49 endpoint: port must be in the range 1..65535.");
496 ctx->setOutputConfig(std::move(config));
499 catch (
const std::exception& e)
509 if (context ==
nullptr)
511 set_api_error(
"Invalid arguments: context is NULL.");
514 if (!is_valid_vita49_fullscale(fullscale))
516 set_api_error(
"Invalid VITA49 fullscale: value must be positive and finite.");
523 ctx->setOutputConfig(std::move(config));
530 if (context ==
nullptr)
532 set_api_error(
"Invalid arguments: context is NULL.");
535 if (!is_valid_vita49_epoch(epoch_unix_nanoseconds))
537 set_api_error(
"Invalid VITA49 epoch: value must fit the VRT 32-bit UTC seconds timestamp field.");
544 ctx->setOutputConfig(std::move(config));
551 if (context ==
nullptr)
553 set_api_error(
"Invalid arguments: context is NULL.");
556 if (!is_valid_vita49_max_payload(max_udp_payload))
558 set_api_error(
"Invalid VITA49 max UDP payload: value must be between 64 and 65507 bytes.");
565 ctx->setOutputConfig(std::move(config));
572 if (context ==
nullptr)
574 set_api_error(
"Invalid arguments: context is NULL.");
577 if (queue_depth == 0)
579 set_api_error(
"Invalid VITA49 queue depth: value must be greater than zero.");
586 ctx->setOutputConfig(std::move(config));
593 if (context ==
nullptr)
595 set_api_error(
"Invalid arguments: context is NULL.");
602 ctx->setOutputConfig(std::move(config));
610 if ((context ==
nullptr) || (xml_filepath ==
nullptr))
622 std::filesystem::path
const p(xml_filepath);
623 auto parent = p.parent_path();
626 ctx->setOutputDir(parent.string());
641 const auto seed = std::random_device{}();
644 ctx->getMasterSeeder().seed(seed);
649 catch (
const std::exception& e)
661 if ((context ==
nullptr) || (xml_content ==
nullptr))
673 ctx->getMasterSeeder());
685 const auto seed = std::random_device{}();
688 ctx->getMasterSeeder().seed(seed);
694 catch (
const std::exception& e)
705 if (context ==
nullptr)
712 const auto* ctx = context;
716 const std::string json_str = j.dump(2);
720 return strdup(json_str.c_str());
722 catch (
const std::exception& e)
732 if (context ==
nullptr)
739 const auto* ctx = context;
745 throw std::runtime_error(
"XML serialization resulted in an empty string.");
750 return strdup(xml_str.c_str());
752 catch (
const std::exception& e)
762 if (context ==
nullptr)
764 last_error_message =
"Invalid context provided to fers_get_last_output_metadata_json.";
769 const auto* ctx = context;
773 return strdup(json_str.c_str());
775 catch (
const std::exception& e)
785 if (context ==
nullptr)
798 return strdup(json_str.c_str());
800 catch (
const std::exception& e)
811 if ((context ==
nullptr) || (json ==
nullptr))
826 auto j = nlohmann::json::parse(json);
828 if (j.contains(
"name"))
830 p->setName(j.at(
"name").get<std::string>());
835 catch (
const std::exception& e)
847 if ((context ==
nullptr) || (json ==
nullptr))
855 auto j = nlohmann::json::parse(json);
860 catch (
const std::exception& e)
871 if ((context ==
nullptr) || (json ==
nullptr))
876 auto j = nlohmann::json::parse(json);
877 auto id = j.at(
"id").is_string() ? std::stoull(j.at(
"id").get<std::string>()) : j.at(
"id").get<uint64_t>();
878 auto* ant = ctx->getWorld()->findAntenna(
id);
887 catch (
const std::exception& e)
897 if ((context ==
nullptr) || (json ==
nullptr))
902 auto j = nlohmann::json::parse(json);
906 ctx->getWorld()->replace(std::move(wf));
910 catch (
const std::exception& e)
920 if ((context ==
nullptr) || (json ==
nullptr))
931 auto j = nlohmann::json::parse(json);
935 catch (
const std::exception& e)
945 if ((context ==
nullptr) || (json ==
nullptr))
956 auto j = nlohmann::json::parse(json);
960 catch (
const std::exception& e)
970 if ((context ==
nullptr) || (json ==
nullptr))
981 auto j = nlohmann::json::parse(json);
985 catch (
const std::exception& e)
995 if ((context ==
nullptr) || (json ==
nullptr))
1000 auto j = nlohmann::json::parse(json);
1001 uint64_t
const tx_id =
1002 j.at(
"tx_id").is_string() ? std::stoull(j.at(
"tx_id").get<std::string>()) : j.at(
"tx_id").get<uint64_t>();
1003 uint64_t
const rx_id =
1004 j.at(
"rx_id").is_string() ? std::stoull(j.at(
"rx_id").get<std::string>()) : j.at(
"rx_id").get<uint64_t>();
1005 auto* tx = ctx->getWorld()->findTransmitter(tx_id);
1006 auto* rx = ctx->getWorld()->findReceiver(rx_id);
1007 if ((tx ==
nullptr) || (rx ==
nullptr))
1015 catch (
const std::exception& e)
1025 if ((context ==
nullptr) || (json ==
nullptr))
1027 auto* ctx = context;
1030 if (ctx->getWorld()->findTiming(
id) ==
nullptr)
1035 auto j = nlohmann::json::parse(json);
1039 catch (
const std::exception& e)
1050 if ((context ==
nullptr) || (scenario_json ==
nullptr))
1058 auto* ctx = context;
1061 const nlohmann::json j = nlohmann::json::parse(scenario_json);
1067 catch (
const nlohmann::json::exception& e)
1077 catch (
const std::exception& e)
1107 return strdup(warning_json.c_str());
1121 struct SimulationRunRequest
1125 void* progress_user_data;
1127 void* cancel_user_data;
1129 void* vita49_telemetry_user_data;
1130 const char* function_name;
1131 const char* invalid_context_message;
1134 int run_simulation_common(
const SimulationRunRequest& request)
1137 if (request.context ==
nullptr)
1144 auto* ctx = request.context;
1146 std::function<void(
const std::string&,
int,
int)> progress_fn;
1147 if (request.progress_callback !=
nullptr)
1149 progress_fn = [&request](
const std::string& msg,
const int current,
const int total)
1150 { request.progress_callback(msg.c_str(), current, total, request.progress_user_data); };
1153 std::function<bool()> cancel_fn;
1154 if (request.cancel_callback !=
nullptr)
1156 cancel_fn = [&request] {
return request.cancel_callback(request.cancel_user_data) != 0; };
1160 if (request.vita49_telemetry_callback !=
nullptr)
1162 telemetry_fn = [&request](
const std::optional<core::OutputStats>& stats,
1163 std::span<const core::ReceiverOutputPacketTrace> packets)
1165 std::string stats_json;
1166 std::string packet_batch_json;
1167 const char* stats_ptr =
nullptr;
1168 const char* packet_batch_ptr =
nullptr;
1170 if (stats.has_value())
1172 stats_json = output_stats_to_json_string(*stats);
1173 stats_ptr = stats_json.c_str();
1175 if (!packets.empty())
1177 packet_batch_json = packet_trace_batch_to_json_string(packets);
1178 packet_batch_ptr = packet_batch_json.c_str();
1181 request.vita49_telemetry_callback(stats_ptr, packet_batch_ptr, request.vita49_telemetry_user_data);
1187 if (
const auto validation_error = validate_vita49_config_for_run(ctx->getOutputConfig()))
1189 set_api_error(*validation_error);
1195 ctx->clearLastOutputMetadata();
1196 bool cancelled =
false;
1197 auto output_metadata =
1199 std::move(cancel_fn), &cancelled, std::move(telemetry_fn));
1200 ctx->setLastOutputMetadata(output_metadata);
1202 return cancelled ? 2 : 0;
1204 catch (
const std::exception& e)
1214 return run_simulation_common(
1215 SimulationRunRequest{.context = context,
1216 .progress_callback = callback,
1217 .progress_user_data = user_data,
1218 .cancel_callback =
nullptr,
1219 .cancel_user_data =
nullptr,
1220 .vita49_telemetry_callback =
nullptr,
1221 .vita49_telemetry_user_data =
nullptr,
1222 .function_name =
"fers_run_simulation",
1223 .invalid_context_message =
"Invalid context provided to fers_run_simulation."});
1230 return run_simulation_common(
1231 SimulationRunRequest{.context = context,
1232 .progress_callback = progress_callback,
1233 .progress_user_data = progress_user_data,
1234 .cancel_callback = cancel_callback,
1235 .cancel_user_data = cancel_user_data,
1236 .vita49_telemetry_callback = vita49_telemetry_callback,
1237 .vita49_telemetry_user_data = vita49_telemetry_user_data,
1238 .function_name =
"fers_run_simulation_ex",
1239 .invalid_context_message =
"Invalid context provided to fers_run_simulation_ex."});
1245 if ((context ==
nullptr) || (output_kml_filepath ==
nullptr))
1252 const auto* ctx = context;
1266 catch (
const std::exception& e)
1304 const size_t waypoint_count,
1306 const size_t num_points)
1309 if ((waypoints ==
nullptr) || waypoint_count == 0 || num_points == 0)
1311 last_error_message =
"Invalid arguments: waypoints cannot be null and counts must be > 0.";
1327 for (
size_t i = 0; i < waypoint_count; ++i)
1331 c.pos.x = waypoints[i].
x;
1332 c.pos.y = waypoints[i].
y;
1333 c.pos.z = waypoints[i].
z;
1343 result_path->count = num_points;
1345 const double start_time = waypoints[0].
time;
1346 const double end_time = waypoints[waypoint_count - 1].
time;
1347 const double duration = end_time - start_time;
1350 if (waypoint_count < 2 || duration <= 0)
1353 for (
size_t i = 0; i < num_points; ++i)
1355 result_path->points[i] = {pos.
x, pos.
y, pos.
z, 0.0, 0.0, 0.0};
1360 const double time_step =
1361 duration /
static_cast<double>(num_points > 1 ? num_points - 1 :
static_cast<size_t>(1));
1363 for (
size_t i = 0; i < num_points; ++i)
1365 const double t = start_time +
static_cast<double>(i) * time_step;
1368 result_path->points[i] = {pos.
x, pos.y, pos.z, vel.
x, vel.
y, vel.
z};
1373 catch (
const std::exception& e)
1382 if (path !=
nullptr)
1393 const size_t waypoint_count,
1396 const size_t num_points)
1401 if ((waypoints ==
nullptr) || waypoint_count == 0 || num_points == 0)
1403 last_error_message =
"Invalid arguments: waypoints cannot be null and counts must be > 0.";
1421 for (
size_t i = 0; i < waypoint_count; ++i)
1425 std::format(
"rotation waypoint {}", i),
"azimuth");
1428 std::format(
"rotation waypoint {}", i),
"elevation");
1430 waypoints[i].azimuth, waypoints[i].elevation, waypoints[i].time, unit));
1439 result_path->count = num_points;
1441 const double start_time = waypoints[0].
time;
1442 const double end_time = waypoints[waypoint_count - 1].
time;
1443 const double duration = end_time - start_time;
1446 if (waypoint_count < 2 || duration <= 0)
1449 for (
size_t i = 0; i < num_points; ++i)
1458 const double time_step =
1459 duration /
static_cast<double>(num_points > 1 ? num_points - 1 :
static_cast<size_t>(1));
1461 for (
size_t i = 0; i < num_points; ++i)
1463 const double t = start_time +
static_cast<double>(i) * time_step;
1473 catch (
const std::exception& e)
1483 if (path !=
nullptr)
1496 const size_t az_samples,
const size_t el_samples,
1497 const double frequency_hz)
1500 if ((context ==
nullptr) || az_samples < 2 || el_samples < 2)
1502 last_error_message =
"Invalid arguments: context must be non-null and sample counts must be >= 2.";
1509 const auto* ctx = context;
1514 last_error_message =
"Antenna ID '" + std::to_string(antenna_id) +
"' not found in the world.";
1527 if (frequency_hz > 0.0)
1529 wavelength =
params::c() / frequency_hz;
1534 data->az_count = az_samples;
1535 data->el_count = el_samples;
1536 const size_t total_samples = az_samples * el_samples;
1538 data->gains =
new double[total_samples];
1543 double max_gain = 0.0;
1545 const auto az_denominator =
static_cast<RealType>(az_samples - 1);
1546 const auto el_denominator =
static_cast<RealType>(el_samples - 1);
1548 for (
size_t i = 0; i < el_samples; ++i)
1552 for (
size_t j = 0; j < az_samples; ++j)
1556 const math::SVec3 sample_angle(1.0, azimuth, elevation);
1557 const RealType gain = ant->
getGain(sample_angle, ref_angle, wavelength);
1558 data->gains[i * az_samples + j] = gain;
1559 max_gain = std::max(gain, max_gain);
1563 data->max_gain = max_gain;
1568 for (
size_t i = 0; i < total_samples; ++i)
1570 data->gains[i] /= max_gain;
1576 catch (
const std::exception& e)
1585 if (data !=
nullptr)
1588 delete[] data->
gains;
1599 if (context ==
nullptr)
1608 const auto* ctx = context;
1615 result->count = cpp_links.size();
1617 if (!cpp_links.empty())
1621 for (
size_t i = 0; i < result->count; ++i)
1623 const auto& src = cpp_links[i];
1624 auto& dst = result->links[i];
1645 copy_visual_link_label(dst, src.label);
1647 dst.source_id =
static_cast<uint64_t
>(src.source_id);
1648 dst.dest_id =
static_cast<uint64_t
>(src.dest_id);
1649 dst.origin_id =
static_cast<uint64_t
>(src.origin_id);
1651 dst.actual_power_dbm = src.actual_power_dbm;
1652 dst.display_value = src.display_value;
1657 result->links =
nullptr;
1661 catch (
const std::exception& e)
1670 if (list !=
nullptr)
1673 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 samples_emitted
std::uint64_t over_range_count
std::uint64_t late_context_packet_count
std::uint64_t context_packets
std::uint64_t packets_dropped
std::uint64_t late_data_packet_count
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.