FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
api.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
3
4/**
5 * @file api.cpp
6 * @brief Implementation of the C-style FFI for the libfers core library.
7 *
8 * This file provides the C implementations for the functions declared in `api.h`.
9 * It acts as the bridge between the C ABI and the C++ core, handling object
10 * creation/destruction, exception catching, error reporting, and type casting.
11 */
12
13#include <core/logging.h>
14#include <core/parameters.h>
15#include <cstring>
16#include <functional>
17#include <libfers/api.h>
18#include <math/path.h>
19#include <math/rotation_path.h>
20#include <nlohmann/json.hpp>
21#include <string>
22
23#include "core/fers_context.h"
24#include "core/sim_threading.h"
25#include "core/thread_pool.h"
28#include "serial/xml_parser.h"
31
32// The fers_context struct is defined here as an alias for our C++ class.
33// This allows the C-API to return an opaque pointer, hiding the C++ implementation.
35{
36};
37
38// A thread-local error message string ensures that error details from one
39// thread's API call do not interfere with another's. This is crucial for a
40// thread-safe FFI layer.
41thread_local std::string last_error_message;
42
43/**
44 * @brief Centralized exception handler for the C-API boundary.
45 *
46 * This function catches standard C++ exceptions, records their `what()` message
47 * into the thread-local error storage, and logs the error. This prevents C++
48 * exceptions from propagating across the FFI boundary, which would be undefined behavior.
49 * @param e The exception that was caught.
50 * @param function_name The name of the API function where the error occurred.
51 */
52static void handle_api_exception(const std::exception& e, const std::string& function_name)
53{
54 last_error_message = e.what();
55 LOG(logging::Level::ERROR, "API Error in {}: {}", function_name, last_error_message);
56}
57
58extern "C" {
59
61{
62 last_error_message.clear();
63 try
64 {
65 return new fers_context_t();
66 }
67 catch (const std::bad_alloc& e)
68 {
69 handle_api_exception(e, "fers_context_create");
70 return nullptr;
71 }
72 catch (const std::exception& e)
73 {
74 handle_api_exception(e, "fers_context_create");
75 return nullptr;
76 }
77}
78
80{
81 if (!context)
82 {
83 last_error_message = "Invalid context provided to fers_context_destroy.";
85 return;
86 }
87 delete context;
88}
89
90// Helper to map C enum to internal C++ enum
92{
93 switch (level)
94 {
95 case FERS_LOG_TRACE:
97 case FERS_LOG_DEBUG:
99 case FERS_LOG_INFO:
101 case FERS_LOG_WARNING:
103 case FERS_LOG_ERROR:
105 case FERS_LOG_FATAL:
107 default:
109 }
110}
111
112int fers_configure_logging(fers_log_level_t level, const char* log_file_path)
113{
114 last_error_message.clear();
115 try
116 {
118 if (log_file_path && *log_file_path)
119 {
120 auto result = logging::logger.logToFile(log_file_path);
121 if (!result)
122 {
123 last_error_message = result.error();
124 return 1;
125 }
126 }
127 return 0;
128 }
129 catch (const std::exception& e)
130 {
131 handle_api_exception(e, "fers_configure_logging");
132 return 1;
133 }
134}
135
136void fers_log(fers_log_level_t level, const char* message)
137{
138 if (!message)
139 return;
140 // We pass a default source_location because C-API calls don't provide C++ source info
141 logging::logger.log(map_level(level), message, std::source_location::current());
142}
143
144int fers_set_thread_count(unsigned num_threads)
145{
146 try
147 {
148 if (auto res = params::setThreads(num_threads); !res)
149 {
150 last_error_message = res.error();
151 return 1;
152 }
153 return 0;
154 }
155 catch (const std::exception& e)
156 {
157 handle_api_exception(e, "fers_set_thread_count");
158 return 1;
159 }
160}
161
162int fers_load_scenario_from_xml_file(fers_context_t* context, const char* xml_filepath, const int validate)
163{
164 last_error_message.clear();
165 if (!context || !xml_filepath)
166 {
167 last_error_message = "Invalid arguments: context or xml_filepath is NULL.";
169 return -1;
170 }
171
172 auto* ctx = reinterpret_cast<FersContext*>(context);
173 try
174 {
175 serial::parseSimulation(xml_filepath, ctx->getWorld(), static_cast<bool>(validate), ctx->getMasterSeeder());
176
177 // After parsing, seed the master random number generator. This is done
178 // to ensure simulation reproducibility. If the scenario specifies a seed,
179 // it is used; otherwise, a non-deterministic seed is generated so that
180 // subsequent runs are unique by default.
181 if (params::params.random_seed)
182 {
183 LOG(logging::Level::INFO, "Using master seed from scenario file: {}", *params::params.random_seed);
184 ctx->getMasterSeeder().seed(*params::params.random_seed);
185 }
186 else
187 {
188 const auto seed = std::random_device{}();
189 LOG(logging::Level::INFO, "No master seed provided in scenario. Using random_device seed: {}", seed);
191 ctx->getMasterSeeder().seed(seed);
192 }
193 return 0; // Success
194 }
195 catch (const std::exception& e)
196 {
197 handle_api_exception(e, "fers_load_scenario_from_xml_file");
198 return 1; // Error
199 }
200}
201
202int fers_load_scenario_from_xml_string(fers_context_t* context, const char* xml_content, const int validate)
203{
204 last_error_message.clear();
205 if (!context || !xml_content)
206 {
207 last_error_message = "Invalid arguments: context or xml_content is NULL.";
209 return -1;
210 }
211
212 auto* ctx = reinterpret_cast<FersContext*>(context);
213 try
214 {
215 serial::parseSimulationFromString(xml_content, ctx->getWorld(), static_cast<bool>(validate),
216 ctx->getMasterSeeder());
217
218 // After parsing, seed the master random number generator. This ensures
219 // that if the scenario provides a seed, the simulation will be
220 // reproducible. If not, a random seed is used to ensure unique runs.
221 if (params::params.random_seed)
222 {
223 LOG(logging::Level::INFO, "Using master seed from scenario string: {}", *params::params.random_seed);
224 ctx->getMasterSeeder().seed(*params::params.random_seed);
225 }
226 else
227 {
228 const auto seed = std::random_device{}();
229 LOG(logging::Level::INFO, "No master seed provided in scenario. Using random_device seed: {}", seed);
231 ctx->getMasterSeeder().seed(seed);
232 }
233
234 return 0; // Success
235 }
236 catch (const std::exception& e)
237 {
238 handle_api_exception(e, "fers_load_scenario_from_xml_string");
239 return 1; // Parsing or logic error
240 }
241}
242
244{
245 last_error_message.clear();
246 if (!context)
247 {
248 last_error_message = "Invalid context provided to fers_get_scenario_as_json.";
250 return nullptr;
251 }
252
253 const auto* ctx = reinterpret_cast<FersContext*>(context);
254 try
255 {
256 const nlohmann::json j = serial::world_to_json(*ctx->getWorld());
257 const std::string json_str = j.dump(2);
258 // A heap-allocated copy of the string is returned. This is necessary
259 // to transfer ownership of the memory across the FFI boundary to a
260 // client that will free it using `fers_free_string`.
261 return strdup(json_str.c_str());
262 }
263 catch (const std::exception& e)
264 {
265 handle_api_exception(e, "fers_get_scenario_as_json");
266 return nullptr;
267 }
268}
269
271{
272 last_error_message.clear();
273 if (!context)
274 {
275 last_error_message = "Invalid context provided to fers_get_scenario_as_xml.";
277 return nullptr;
278 }
279
280 const auto* ctx = reinterpret_cast<FersContext*>(context);
281 try
282 {
283 const std::string xml_str = serial::world_to_xml_string(*ctx->getWorld());
284 if (xml_str.empty())
285 {
286 throw std::runtime_error("XML serialization resulted in an empty string.");
287 }
288 // `strdup` is used to create a heap-allocated string that can be safely
289 // passed across the FFI boundary. The client is responsible for freeing
290 // this memory with `fers_free_string`.
291 return strdup(xml_str.c_str());
292 }
293 catch (const std::exception& e)
294 {
295 handle_api_exception(e, "fers_get_scenario_as_xml");
296 return nullptr;
297 }
298}
299
300int fers_update_scenario_from_json(fers_context_t* context, const char* scenario_json)
301{
302 last_error_message.clear();
303 if (!context || !scenario_json)
304 {
305 last_error_message = "Invalid arguments: context or scenario_json is NULL.";
307 return -1;
308 }
309
310 auto* ctx = reinterpret_cast<FersContext*>(context);
311 try
312 {
313 const nlohmann::json j = nlohmann::json::parse(scenario_json);
314 serial::json_to_world(j, *ctx->getWorld(), ctx->getMasterSeeder());
315
316 return 0; // Success
317 }
318 catch (const nlohmann::json::exception& e)
319 {
320 // A specific catch block for JSON errors is used to provide more
321 // detailed feedback to the client (e.g., the UI), which can help
322 // developers diagnose schema or data format issues more easily.
323 last_error_message = "JSON parsing/deserialization error: " + std::string(e.what());
324 LOG(logging::Level::ERROR, "API Error in {}: {}", "fers_update_scenario_from_json", last_error_message);
325 return 2; // JSON error
326 }
327 catch (const std::exception& e)
328 {
329 handle_api_exception(e, "fers_update_scenario_from_json");
330 return 1; // Generic error
331 }
332}
333
335{
336 if (last_error_message.empty())
337 {
338 return nullptr; // No error to report
339 }
340 // `strdup` allocates with `malloc`, which is part of the C standard ABI,
341 // making it safe to transfer ownership across the FFI boundary. The caller
342 // must then free this memory using `fers_free_string`.
343 return strdup(last_error_message.c_str());
344}
345
346void fers_free_string(char* str)
347{
348 if (str)
349 {
350 free(str);
351 }
352}
353
354int fers_run_simulation(fers_context_t* context, fers_progress_callback_t callback, void* user_data)
355{
356 last_error_message.clear();
357 if (!context)
358 {
359 last_error_message = "Invalid context provided to fers_run_simulation.";
361 return -1;
362 }
363
364 auto* ctx = reinterpret_cast<FersContext*>(context);
365
366 // Wrap the C-style callback in a std::function for easier use in C++.
367 // This also handles the case where the callback is null.
368 std::function<void(const std::string&, int, int)> progress_fn;
369 if (callback)
370 {
371 progress_fn = [callback, user_data](const std::string& msg, const int current, const int total)
372 { callback(msg.c_str(), current, total, user_data); };
373 }
374
375 try
376 {
378
379 core::runEventDrivenSim(ctx->getWorld(), pool, progress_fn);
380
381 return 0;
382 }
383 catch (const std::exception& e)
384 {
385 handle_api_exception(e, "fers_run_simulation");
386 return 1;
387 }
388}
389
390int fers_generate_kml(const fers_context_t* context, const char* output_kml_filepath)
391{
392 last_error_message.clear();
393 if (!context || !output_kml_filepath)
394 {
395 last_error_message = "Invalid arguments: context or output_kml_filepath is NULL.";
397 return -1;
398 }
399
400 auto* ctx = reinterpret_cast<const FersContext*>(context);
401
402 try
403 {
404 if (serial::KmlGenerator::generateKml(*ctx->getWorld(), output_kml_filepath))
405 {
406 return 0; // Success
407 }
408
409 last_error_message = "KML generation failed for an unknown reason.";
411 return 2; // Generation failed
412 }
413 catch (const std::exception& e)
414 {
415 handle_api_exception(e, "fers_generate_kml");
416 return 1; // Exception thrown
417 }
418}
419
420// --- Helper to convert C-API enum to C++ enum ---
434
448
449
451 const size_t waypoint_count,
452 const fers_interp_type_t interp_type,
453 const size_t num_points)
454{
455 last_error_message.clear();
456 if (!waypoints || waypoint_count == 0 || num_points == 0)
457 {
458 last_error_message = "Invalid arguments: waypoints cannot be null and counts must be > 0.";
460 return nullptr;
461 }
462 if (interp_type == FERS_INTERP_CUBIC && waypoint_count < 2)
463 {
464 last_error_message = "Cubic interpolation requires at least 2 waypoints.";
466 return nullptr;
467 }
468
469 try
470 {
471 math::Path path;
472 path.setInterp(to_cpp_interp_type(interp_type));
473
474 for (size_t i = 0; i < waypoint_count; ++i)
475 {
476 math::Coord c;
477 c.t = waypoints[i].time;
478 c.pos.x = waypoints[i].x;
479 c.pos.y = waypoints[i].y;
480 c.pos.z = waypoints[i].z;
481 path.addCoord(c);
482 }
483
484 path.finalize();
485
486 auto* result_path = new fers_interpolated_path_t();
487 result_path->points = new fers_interpolated_point_t[num_points];
488 result_path->count = num_points;
489
490 const double start_time = waypoints[0].time;
491 const double end_time = waypoints[waypoint_count - 1].time;
492 const double duration = end_time - start_time;
493
494 // Handle static case separately
495 if (waypoint_count < 2 || duration <= 0)
496 {
497 const math::Vec3 pos = path.getPosition(start_time);
498 for (size_t i = 0; i < num_points; ++i)
499 {
500 result_path->points[i] = {pos.x, pos.y, pos.z};
501 }
502 return result_path;
503 }
504
505 const double time_step = duration / (num_points > 1 ? num_points - 1 : 1);
506
507 for (size_t i = 0; i < num_points; ++i)
508 {
509 const double t = start_time + i * time_step;
510 const math::Vec3 pos = path.getPosition(t);
511 const math::Vec3 vel = path.getVelocity(t);
512 result_path->points[i] = {pos.x, pos.y, pos.z, vel.x, vel.y, vel.z};
513 }
514
515 return result_path;
516 }
517 catch (const std::exception& e)
518 {
519 handle_api_exception(e, "fers_get_interpolated_motion_path");
520 return nullptr;
521 }
522}
523
525{
526 if (path)
527 {
528 delete[] path->points;
529 delete path;
530 }
531}
532
534 const size_t waypoint_count,
535 const fers_interp_type_t interp_type,
536 const size_t num_points)
537{
538 last_error_message.clear();
539 if (!waypoints || waypoint_count == 0 || num_points == 0)
540 {
541 last_error_message = "Invalid arguments: waypoints cannot be null and counts must be > 0.";
543 return nullptr;
544 }
545 if (interp_type == FERS_INTERP_CUBIC && waypoint_count < 2)
546 {
547 last_error_message = "Cubic interpolation requires at least 2 waypoints.";
549 return nullptr;
550 }
551
552 try
553 {
555 path.setInterp(to_cpp_rot_interp_type(interp_type));
556
557 for (size_t i = 0; i < waypoint_count; ++i)
558 {
559 const RealType az_deg = waypoints[i].azimuth_deg;
560 const RealType el_deg = waypoints[i].elevation_deg;
561
562 // Convert from compass degrees (from C-API) to internal mathematical radians
563 const RealType az_rad = (90.0 - az_deg) * (PI / 180.0);
564 const RealType el_rad = el_deg * (PI / 180.0);
565
566 path.addCoord({az_rad, el_rad, waypoints[i].time});
567 }
568
569 path.finalize();
570
571 auto* result_path = new fers_interpolated_rotation_path_t();
572 result_path->points = new fers_interpolated_rotation_point_t[num_points];
573 result_path->count = num_points;
574
575 const double start_time = waypoints[0].time;
576 const double end_time = waypoints[waypoint_count - 1].time;
577 const double duration = end_time - start_time;
578
579 // Handle static case separately
580 if (waypoint_count < 2 || duration <= 0)
581 {
582 const math::SVec3 rot = path.getPosition(start_time);
583 // Convert back to compass degrees for output without normalization
584 const RealType az_deg = 90.0 - rot.azimuth * 180.0 / PI;
585 const RealType el_deg = rot.elevation * 180.0 / PI;
586 for (size_t i = 0; i < num_points; ++i)
587 {
588 result_path->points[i] = {az_deg, el_deg};
589 }
590 return result_path;
591 }
592
593 const double time_step = duration / (num_points > 1 ? num_points - 1 : 1);
594
595 for (size_t i = 0; i < num_points; ++i)
596 {
597 const double t = start_time + i * time_step;
598 const math::SVec3 rot = path.getPosition(t);
599
600 // Convert from internal mathematical radians back to compass degrees for C-API output
601 // We do NOT normalize to [0, 360) to preserve winding/negative angles for the UI.
602 const RealType az_deg = 90.0 - rot.azimuth * 180.0 / PI;
603 const RealType el_deg = rot.elevation * 180.0 / PI;
604
605 result_path->points[i] = {az_deg, el_deg};
606 }
607
608 return result_path;
609 }
610 catch (const std::exception& e)
611 {
612 handle_api_exception(e, "fers_get_interpolated_rotation_path");
613 return nullptr;
614 }
615}
616
618{
619 if (path)
620 {
621 delete[] path->points;
622 delete path;
623 }
624}
625
626// --- Antenna Pattern Implementation ---
627
629 const size_t az_samples, const size_t el_samples,
630 const double frequency_hz)
631{
632 last_error_message.clear();
633 if (!context || !antenna_name || az_samples == 0 || el_samples == 0)
634 {
635 last_error_message = "Invalid arguments: context, antenna_name, or sample counts are invalid.";
637 return nullptr;
638 }
639
640 try
641 {
642 const auto* ctx = reinterpret_cast<const FersContext*>(context);
643 antenna::Antenna* ant = ctx->getWorld()->findAntenna(antenna_name);
644
645 if (!ant)
646 {
647 last_error_message = "Antenna '" + std::string(antenna_name) + "' not found in the world.";
649 return nullptr;
650 }
651
652 // TODO: Currently only using the first-found waveform. This is incorrect but also difficult to represent
653 // correctly in scenarios with multiple waveforms as the gain for squarehorn and parabolic antennas
654 // depends on the wavelength. Hence a decision needs to be made about whether to return multiple patterns
655 // per waveform or have the user specify a representative wavelength in the UI per antenna.
656 // Calculate wavelength from the provided frequency.
657 // Default to 1GHz (0.3m) if frequency is invalid/zero, though the UI should prevent this
658 // for antennas that strictly require it (Horn/Parabolic).
659 RealType wavelength = 0.3;
660 if (frequency_hz > 0.0)
661 {
662 wavelength = params::c() / frequency_hz;
663 }
664
665 auto* data = new fers_antenna_pattern_data_t();
666 data->az_count = az_samples;
667 data->el_count = el_samples;
668 const size_t total_samples = az_samples * el_samples;
669 data->gains = new double[total_samples];
670
671 // The reference angle (boresight) is implicitly the local X-axis in the FERS engine.
672 // We pass a zero rotation to get the gain relative to this boresight.
673 const math::SVec3 ref_angle(1.0, 0.0, 0.0);
674 double max_gain = 0.0;
675
676 for (size_t i = 0; i < el_samples; ++i)
677 {
678 // Elevation from -PI/2 to PI/2
679 const RealType elevation = (static_cast<RealType>(i) / (el_samples - 1)) * PI - (PI / 2.0);
680 for (size_t j = 0; j < az_samples; ++j)
681 {
682 // Azimuth from -PI to PI
683 const RealType azimuth = (static_cast<RealType>(j) / (az_samples - 1)) * 2.0 * PI - PI;
684 const math::SVec3 sample_angle(1.0, azimuth, elevation);
685 const RealType gain = ant->getGain(sample_angle, ref_angle, wavelength);
686 data->gains[i * az_samples + j] = gain;
687 if (gain > max_gain)
688 {
689 max_gain = gain;
690 }
691 }
692 }
693
694 data->max_gain = max_gain;
695
696 // Normalize the gains
697 if (max_gain > 0)
698 {
699 for (size_t i = 0; i < total_samples; ++i)
700 {
701 data->gains[i] /= max_gain;
702 }
703 }
704
705 return data;
706 }
707 catch (const std::exception& e)
708 {
709 handle_api_exception(e, "fers_get_antenna_pattern");
710 return nullptr;
711 }
712}
713
715{
716 if (data)
717 {
718 delete[] data->gains;
719 delete data;
720 }
721}
722
723// --- Preview Link Calculation Implementation ---
724
726{
727 last_error_message.clear();
728 if (!context)
729 {
730 last_error_message = "Invalid context passed to fers_calculate_preview_links";
732 return nullptr;
733 }
734
735 try
736 {
737 const auto* ctx = reinterpret_cast<const FersContext*>(context);
738 // Call the core physics logic in channel_model.cpp
739 const auto cpp_links = simulation::calculatePreviewLinks(*ctx->getWorld(), time);
740
741 // Convert C++ vector to C-API struct
742 auto* result = new fers_visual_link_list_t();
743 result->count = cpp_links.size();
744
745 if (!cpp_links.empty())
746 {
747 result->links = new fers_visual_link_t[result->count];
748 for (size_t i = 0; i < result->count; ++i)
749 {
750 const auto& src = cpp_links[i];
751 auto& dst = result->links[i];
752
753 // Map enums
754 switch (src.type)
755 {
758 break;
760 dst.type = FERS_LINK_BISTATIC_TX_TGT;
761 break;
763 dst.type = FERS_LINK_BISTATIC_TGT_RX;
764 break;
766 dst.type = FERS_LINK_DIRECT_TX_RX;
767 break;
768 }
769
770 dst.quality = (src.quality == simulation::LinkQuality::Strong) ? FERS_LINK_STRONG : FERS_LINK_WEAK;
771
772 // Safe string copy
773 std::strncpy(dst.label, src.label.c_str(), sizeof(dst.label) - 1);
774 dst.label[sizeof(dst.label) - 1] = '\0';
775
776 std::strncpy(dst.source_name, src.source_name.c_str(), sizeof(dst.source_name) - 1);
777 dst.source_name[sizeof(dst.source_name) - 1] = '\0';
778
779 std::strncpy(dst.dest_name, src.dest_name.c_str(), sizeof(dst.dest_name) - 1);
780 dst.dest_name[sizeof(dst.dest_name) - 1] = '\0';
781
782 std::strncpy(dst.origin_name, src.origin_name.c_str(), sizeof(dst.origin_name) - 1);
783 dst.origin_name[sizeof(dst.origin_name) - 1] = '\0';
784 }
785 }
786 else
787 {
788 result->links = nullptr;
789 }
790 return result;
791 }
792 catch (const std::exception& e)
793 {
794 handle_api_exception(e, "fers_calculate_preview_links");
795 return nullptr;
796 }
797}
798
800{
801 if (list)
802 {
803 delete[] list->links;
804 delete list;
805 }
806}
807}
char * fers_get_scenario_as_xml(fers_context_t *context)
Serializes the current simulation scenario into a FERS XML string.
Definition api.cpp:270
void fers_free_antenna_pattern_data(fers_antenna_pattern_data_t *data)
Frees the memory allocated for an antenna pattern data structure.
Definition api.cpp:714
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.
Definition api.cpp:202
void fers_log(fers_log_level_t level, const char *message)
Submits a log message to the library's unified logging system.
Definition api.cpp:136
static logging::Level map_level(fers_log_level_t level)
Definition api.cpp:91
char * fers_get_last_error_message()
Retrieves the last error message that occurred on the current thread.
Definition api.cpp:334
void fers_free_interpolated_motion_path(fers_interpolated_path_t *path)
Frees the memory allocated for an interpolated motion path.
Definition api.cpp:524
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.
Definition api.cpp:390
void fers_context_destroy(fers_context_t *context)
Destroys a FERS simulation context and releases all associated memory.
Definition api.cpp:79
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:52
fers_context_t * fers_context_create()
Creates a new FERS simulation context.
Definition api.cpp:60
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 size_t num_points)
Calculates an interpolated rotation path from a set of waypoints.
Definition api.cpp:533
void fers_free_preview_links(fers_visual_link_list_t *list)
Frees the memory allocated for a preview link list.
Definition api.cpp:799
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.
Definition api.cpp:450
int fers_run_simulation(fers_context_t *context, fers_progress_callback_t callback, void *user_data)
Runs the simulation defined in the provided context.
Definition api.cpp:354
void fers_free_string(char *str)
Frees a string that was allocated and returned by the libfers API.
Definition api.cpp:346
math::RotationPath::InterpType to_cpp_rot_interp_type(const fers_interp_type_t type)
Definition api.cpp:435
fers_antenna_pattern_data_t * fers_get_antenna_pattern(const fers_context_t *context, const char *antenna_name, 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.
Definition api.cpp:628
int fers_configure_logging(fers_log_level_t level, const char *log_file_path)
Configures the internal logger.
Definition api.cpp:112
void fers_free_interpolated_rotation_path(fers_interpolated_rotation_path_t *path)
Frees the memory allocated for an interpolated rotation path.
Definition api.cpp:617
math::Path::InterpType to_cpp_interp_type(const fers_interp_type_t type)
Definition api.cpp:421
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.
Definition api.cpp:162
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.
Definition api.cpp:725
char * fers_get_scenario_as_json(fers_context_t *context)
Serializes the current simulation scenario into a JSON string.
Definition api.cpp:243
int fers_update_scenario_from_json(fers_context_t *context, const char *scenario_json)
Updates the simulation scenario from a JSON string.
Definition api.cpp:300
int fers_set_thread_count(unsigned num_threads)
Sets the number of worker threads for the simulation.
Definition api.cpp:144
thread_local std::string last_error_message
Definition api.cpp:41
void(* fers_progress_callback_t)(const char *message, int current, int total, void *user_data)
A function pointer type for progress reporting callbacks.
Definition api.h:39
fers_log_level_t
Log levels for the FERS library.
Definition api.h:76
@ FERS_LOG_FATAL
Definition api.h:82
@ FERS_LOG_DEBUG
Definition api.h:78
@ FERS_LOG_ERROR
Definition api.h:81
@ FERS_LOG_INFO
Definition api.h:79
@ FERS_LOG_TRACE
Definition api.h:77
@ FERS_LOG_WARNING
Definition api.h:80
fers_interp_type_t
Defines the interpolation methods available for path generation.
Definition api.h:313
@ FERS_INTERP_CUBIC
Definition api.h:316
@ FERS_INTERP_STATIC
Definition api.h:314
@ FERS_INTERP_LINEAR
Definition api.h:315
struct fers_context fers_context_t
Definition api.h:25
@ FERS_LINK_BISTATIC_TX_TGT
Definition api.h:451
@ FERS_LINK_MONOSTATIC
Definition api.h:450
@ FERS_LINK_BISTATIC_TGT_RX
Definition api.h:452
@ FERS_LINK_DIRECT_TX_RX
Definition api.h:453
@ FERS_LINK_WEAK
Definition api.h:442
@ FERS_LINK_STRONG
Definition api.h:441
Header for radar channel propagation and interaction models.
Manages the lifetime and state of a single FERS simulation scenario.
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.
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:37
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:59
void setLevel(const Level level) noexcept
Sets the logging level.
Definition logging.h:57
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:35
void setInterp(InterpType settype) noexcept
Changes the interpolation type.
Definition path.cpp:158
InterpType
Types of interpolation supported by the Path class.
Definition path.h:36
Vec3 getVelocity(RealType t) const
Retrieves the velocity at a given time along the path.
Definition path.cpp:59
void addCoord(const Coord &coord) noexcept
Adds a coordinate to the path.
Definition path.cpp:26
void finalize()
Finalizes the path, preparing it for interpolation.
Definition path.cpp:141
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.
InterpType
Enumeration for types of interpolation.
void addCoord(const RotationCoord &coord) noexcept
Adds a rotation coordinate to the path.
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.
Definition thread_pool.h:29
static bool 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.
Definition config.h:27
constexpr RealType PI
Mathematical constant π (pi).
Definition config.h:43
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.
#define LOG(level,...)
Definition logging.h:19
void runEventDrivenSim(World *world, pool::ThreadPool &pool, const std::function< void(const std::string &, int, int)> &progress_callback)
Runs the unified, event-driven radar simulation.
@ 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.
@ ERROR
Error level for error events.
@ DEBUG
Debug level for general debugging information.
Logger logger
Externally available logger object.
Definition logging.cpp:23
unsigned renderThreads() noexcept
Get the number of worker threads.
Definition parameters.h:133
std::expected< void, std::string > setThreads(const unsigned threads) noexcept
Set the number of worker threads.
Definition parameters.h:248
Parameters params
Definition parameters.h:73
RealType c() noexcept
Get the speed of light.
Definition parameters.h:79
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 parseSimulationFromString(const std::string &xmlContent, World *world, const bool validate, std::mt19937 &masterSeeder)
void parseSimulation(const std::string &filename, World *world, const bool validate, std::mt19937 &masterSeeder)
Parses a simulation configuration from an XML file.
nlohmann::json world_to_json(const core::World &world)
Serializes the entire simulation world into a nlohmann::json object.
std::string world_to_xml_string(const core::World &world)
Serializes the entire simulation world into an XML formatted string.
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.
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 ...
Defines the RotationPath class for handling rotational paths with different interpolation types.
Header file for the main simulation runner.
Represents a sampled 2D antenna gain pattern.
Definition api.h:270
A container for an array of interpolated motion path points.
Definition api.h:372
fers_interpolated_point_t * points
Definition api.h:373
Represents a single interpolated point on a motion path.
Definition api.h:347
A container for an array of interpolated rotation path points.
Definition api.h:382
fers_interpolated_rotation_point_t * points
Definition api.h:383
Represents a single interpolated point on a rotation path.
Definition api.h:361
Represents a single waypoint for a motion path.
Definition api.h:324
double x
X coordinate in meters (East in ENU).
Definition api.h:326
double time
Time in seconds.
Definition api.h:325
double y
Y coordinate in meters (North in ENU).
Definition api.h:327
double z
Z coordinate in meters (Up/Altitude in ENU).
Definition api.h:328
Represents a single waypoint for a rotation path.
Definition api.h:336
double time
Time in seconds.
Definition api.h:337
double elevation_deg
Elevation angle in degrees (positive up).
Definition api.h:339
double azimuth_deg
Azimuth angle in compass degrees (0=North, 90=East).
Definition api.h:338
Represents a position in 3D space with an associated time.
Definition coord.h:24
std::optional< unsigned > random_seed
Random seed for simulation.
Definition parameters.h:58
A simple thread pool implementation.
Header file for parsing XML configuration files for simulation.
Provides functions to serialize the simulation world back into the FERS XML format.