19#include <nlohmann/json.hpp>
41 void to_json(nlohmann::json& j,
const Vec3& v) { j = {{
"x", v.
x}, {
"y", v.
y}, {
"z", v.
z}}; }
45 j.at(
"x").get_to(v.
x);
46 j.at(
"y").get_to(v.
y);
47 j.at(
"z").get_to(v.
z);
52 j = {{
"time", c.t}, {
"x", c.pos.x}, {
"y", c.pos.y}, {
"altitude", c.pos.z}};
57 j.at(
"time").get_to(c.t);
58 j.at(
"x").get_to(c.pos.x);
59 j.at(
"y").get_to(c.pos.y);
60 j.at(
"altitude").get_to(c.pos.z);
71 j = {{
"time", rc.
t}, {
"azimuth", az_deg}, {
"elevation", el_deg}};
76 j.at(
"time").get_to(rc.
t);
83 rc.
azimuth = (90.0 - az_deg) * (
PI / 180.0);
92 void to_json(nlohmann::json& j,
const Path& p)
94 j = {{
"interpolation", p.getType()}, {
"positionwaypoints", p.getCoords()}};
100 for (
const auto waypoints = j.at(
"positionwaypoints").get<std::vector<Coord>>();
const auto& wp : waypoints)
114 void to_json(nlohmann::json& j,
const RotationPath& p)
116 j[
"interpolation"] = p.getType();
125 const RealType start_az_deg = 90.0 - p.getStart().azimuth * 180.0 /
PI;
126 const RealType start_el_deg = p.getStart().elevation * 180.0 /
PI;
127 const RealType rate_az_deg_s = -p.getRate().azimuth * 180.0 /
PI;
128 const RealType rate_el_deg_s = p.getRate().elevation * 180.0 /
PI;
129 j[
"startazimuth"] = start_az_deg;
130 j[
"startelevation"] = start_el_deg;
131 j[
"azimuthrate"] = rate_az_deg_s;
132 j[
"elevationrate"] = rate_el_deg_s;
136 j[
"rotationwaypoints"] = p.getCoords();
143 for (
const auto waypoints = j.at(
"rotationwaypoints").get<std::vector<RotationCoord>>();
144 const auto& wp : waypoints)
177 std::vector<RealType> alphas;
178 std::vector<RealType> weights;
182 nlohmann::json noise_entries = nlohmann::json::array();
183 for (
size_t i = 0; i < alphas.size(); ++i)
185 noise_entries.push_back({{
"alpha", alphas[i]}, {
"weight", weights[i]}});
187 j[
"noise_entries"] = noise_entries;
194 if (j.value(
"synconpulse",
false))
199 if (j.contains(
"freq_offset"))
203 if (j.contains(
"random_freq_offset_stdev"))
207 if (j.contains(
"phase_offset"))
211 if (j.contains(
"random_phase_offset_stdev"))
216 if (j.contains(
"noise_entries"))
218 for (
const auto& entry : j.at(
"noise_entries"))
233 j[
"cw"] = nlohmann::json::object();
237 if (
const auto& filename = rs.
getFilename(); filename.has_value())
239 j[
"pulsed_from_file"] = {{
"filename", *filename}};
243 throw std::logic_error(
"Attempted to serialize a file-based waveform named '" + rs.
getName() +
244 "' without a source filename.");
249 void from_json(
const nlohmann::json& j, std::unique_ptr<RadarSignal>& rs)
251 const auto name = j.at(
"name").get<std::string>();
252 const auto power = j.at(
"power").get<
RealType>();
253 const auto carrier = j.at(
"carrier_frequency").get<
RealType>();
255 if (j.contains(
"cw"))
257 auto cw_signal = std::make_unique<CwSignal>();
259 std::move(cw_signal));
261 else if (j.contains(
"pulsed_from_file"))
263 const auto& pulsed_file = j.at(
"pulsed_from_file");
264 const auto filename = pulsed_file.value(
"filename",
"");
265 if (filename.empty())
274 throw std::runtime_error(
"Unsupported waveform type in from_json for '" + name +
"'");
285 if (
const auto* sinc =
dynamic_cast<const Sinc*
>(&a))
287 j[
"pattern"] =
"sinc";
288 j[
"alpha"] = sinc->getAlpha();
289 j[
"beta"] = sinc->getBeta();
290 j[
"gamma"] = sinc->getGamma();
292 else if (
const auto* gaussian =
dynamic_cast<const Gaussian*
>(&a))
294 j[
"pattern"] =
"gaussian";
295 j[
"azscale"] = gaussian->getAzimuthScale();
296 j[
"elscale"] = gaussian->getElevationScale();
298 else if (
const auto* sh =
dynamic_cast<const SquareHorn*
>(&a))
300 j[
"pattern"] =
"squarehorn";
301 j[
"diameter"] = sh->getDimension();
303 else if (
const auto* parabolic =
dynamic_cast<const Parabolic*
>(&a))
305 j[
"pattern"] =
"parabolic";
306 j[
"diameter"] = parabolic->getDiameter();
308 else if (
const auto* xml =
dynamic_cast<const XmlAntenna*
>(&a))
310 j[
"pattern"] =
"xml";
311 j[
"filename"] = xml->getFilename();
313 else if (
const auto* h5 =
dynamic_cast<const H5Antenna*
>(&a))
315 j[
"pattern"] =
"file";
316 j[
"filename"] = h5->getFilename();
320 j[
"pattern"] =
"isotropic";
324 void from_json(
const nlohmann::json& j, std::unique_ptr<Antenna>& ant)
326 const auto name = j.at(
"name").get<std::string>();
327 const auto pattern = j.value(
"pattern",
"isotropic");
329 if (pattern ==
"isotropic")
331 ant = std::make_unique<Isotropic>(name);
333 else if (pattern ==
"sinc")
335 ant = std::make_unique<Sinc>(name, j.at(
"alpha").get<
RealType>(), j.at(
"beta").get<
RealType>(),
338 else if (pattern ==
"gaussian")
340 ant = std::make_unique<Gaussian>(name, j.at(
"azscale").get<
RealType>(), j.at(
"elscale").get<
RealType>());
342 else if (pattern ==
"squarehorn")
344 ant = std::make_unique<SquareHorn>(name, j.at(
"diameter").get<
RealType>());
346 else if (pattern ==
"parabolic")
348 ant = std::make_unique<Parabolic>(name, j.at(
"diameter").get<
RealType>());
350 else if (pattern ==
"xml")
352 const auto filename = j.value(
"filename",
"");
353 if (filename.empty())
358 ant = std::make_unique<XmlAntenna>(name, filename);
360 else if (pattern ==
"file")
362 const auto filename = j.value(
"filename",
"");
363 if (filename.empty())
368 ant = std::make_unique<H5Antenna>(name, filename);
372 throw std::runtime_error(
"Unsupported antenna pattern in from_json: " + pattern);
375 ant->setEfficiencyFactor(j.value(
"efficiency", 1.0));
385 j.at(
"start").get_to(p.
start);
386 j.at(
"end").get_to(p.
end);
391 j = nlohmann::json{{
"name", t.
getName()},
398 j[
"pulsed_mode"] = {{
"prf", t.
getPrf()}};
402 j[
"cw_mode"] = nlohmann::json::object();
412 j = nlohmann::json{{
"name", r.
getName()},
426 j[
"cw_mode"] = nlohmann::json::object();
437 nlohmann::json rcs_json;
438 if (
const auto* iso =
dynamic_cast<const IsoTarget*
>(&t))
440 rcs_json[
"type"] =
"isotropic";
441 rcs_json[
"value"] = iso->getConstRcs();
443 else if (
const auto* file =
dynamic_cast<const FileTarget*
>(&t))
445 rcs_json[
"type"] =
"file";
446 rcs_json[
"filename"] = file->getFilename();
453 nlohmann::json model_json;
454 if (
const auto* chi_model =
dynamic_cast<const RcsChiSquare*
>(model_base))
456 model_json[
"type"] =
"chisquare";
457 model_json[
"k"] = chi_model->getK();
461 model_json[
"type"] =
"constant";
463 j[
"model"] = model_json;
490 void to_json(nlohmann::json& j,
const Parameters& p)
492 j = nlohmann::json{{
"starttime", p.start},
496 {
"simSamplingRate", p.sim_sampling_rate},
497 {
"adc_bits", p.adc_bits},
498 {
"oversample", p.oversample_ratio}};
500 if (p.random_seed.has_value())
502 j[
"randomseed"] = p.random_seed.value();
506 {
"latitude", p.origin_latitude}, {
"longitude", p.origin_longitude}, {
"altitude", p.origin_altitude}};
508 j[
"coordinatesystem"] = {{
"frame", p.coordinate_frame}};
511 j[
"coordinatesystem"][
"zone"] = p.utm_zone;
512 j[
"coordinatesystem"][
"hemisphere"] = p.utm_north_hemisphere ?
"N" :
"S";
523 p.
adc_bits = j.value(
"adc_bits", 0);
525 p.
random_seed = j.value<std::optional<unsigned>>(
"randomseed", std::nullopt);
527 const auto& origin = j.at(
"origin");
532 const auto& cs = j.at(
"coordinatesystem");
536 p.
utm_zone = cs.at(
"zone").get<
int>();
546 nlohmann::json sim_json;
551 sim_json[
"waveforms"] = nlohmann::json::array();
552 for (
const auto& waveform : world.
getWaveforms() | std::views::values)
554 sim_json[
"waveforms"].push_back(*waveform);
557 sim_json[
"antennas"] = nlohmann::json::array();
560 sim_json[
"antennas"].push_back(*
antenna);
563 sim_json[
"timings"] = nlohmann::json::array();
566 sim_json[
"timings"].push_back(*
timing);
569 sim_json[
"platforms"] = nlohmann::json::array();
572 nlohmann::json plat_json = *p;
575 plat_json[
"components"] = nlohmann::json::array();
580 if (t->getPlatform() == p.get())
582 if (t->getAttached() !=
nullptr)
584 nlohmann::json monostatic_comp;
585 monostatic_comp[
"name"] = t->getName();
586 monostatic_comp[
"waveform"] = t->getSignal() ? t->getSignal()->getName() :
"";
587 monostatic_comp[
"antenna"] = t->getAntenna() ? t->getAntenna()->getName() :
"";
588 monostatic_comp[
"timing"] = t->getTiming() ? t->getTiming()->getName() :
"";
590 if (
const auto* recv =
dynamic_cast<const radar::Receiver*
>(t->getAttached()))
592 monostatic_comp[
"noise_temp"] = recv->getNoiseTemperature();
594 monostatic_comp[
"nopropagationloss"] =
597 if (!t->getSchedule().empty())
599 monostatic_comp[
"schedule"] = t->getSchedule();
604 monostatic_comp[
"pulsed_mode"] = {{
"prf", t->getPrf()},
605 {
"window_skip", recv->getWindowSkip()},
606 {
"window_length", recv->getWindowLength()}};
610 monostatic_comp[
"cw_mode"] = nlohmann::json::object();
613 plat_json[
"components"].push_back({{
"monostatic", monostatic_comp}});
617 plat_json[
"components"].push_back({{
"transmitter", *t}});
625 if (r->getPlatform() == p.get())
628 if (r->getAttached() ==
nullptr)
630 plat_json[
"components"].push_back({{
"receiver", *r}});
638 if (target->getPlatform() == p.get())
640 plat_json[
"components"].push_back({{
"target", *target}});
644 sim_json[
"platforms"].push_back(plat_json);
647 return {{
"simulation", sim_json}};
656 const auto& sim = j.at(
"simulation");
663 if (sim.at(
"parameters").contains(
"randomseed"))
681 if (sim.contains(
"waveforms"))
683 for (
auto waveforms = sim.at(
"waveforms").get<std::vector<std::unique_ptr<fers_signal::RadarSignal>>>();
684 auto& waveform : waveforms)
689 world.
add(std::move(waveform));
694 if (sim.contains(
"antennas"))
696 for (
auto antennas = sim.at(
"antennas").get<std::vector<std::unique_ptr<antenna::Antenna>>>();
707 if (sim.contains(
"timings"))
709 for (
const auto& timing_json : sim.at(
"timings"))
711 auto name = timing_json.at(
"name").get<std::string>();
712 auto timing_obj = std::make_unique<timing::PrototypeTiming>(name);
713 from_json(timing_json, *timing_obj);
714 world.
add(std::move(timing_obj));
719 if (sim.contains(
"platforms"))
721 for (
const auto& plat_json : sim.at(
"platforms"))
723 auto name = plat_json.at(
"name").get<std::string>();
724 auto plat = std::make_unique<radar::Platform>(name);
727 if (plat_json.contains(
"motionpath"))
729 auto path = std::make_unique<math::Path>();
730 from_json(plat_json.at(
"motionpath"), *path);
731 plat->setMotionPath(std::move(path));
733 if (plat_json.contains(
"rotationpath"))
735 auto rot_path = std::make_unique<math::RotationPath>();
736 from_json(plat_json.at(
"rotationpath"), *rot_path);
737 plat->setRotationPath(std::move(rot_path));
739 else if (plat_json.contains(
"fixedrotation"))
743 auto rot_path = std::make_unique<math::RotationPath>();
744 const auto& fixed_json = plat_json.at(
"fixedrotation");
745 const RealType start_az_deg = fixed_json.at(
"startazimuth").get<
RealType>();
746 const RealType start_el_deg = fixed_json.at(
"startelevation").get<
RealType>();
747 const RealType rate_az_deg_s = fixed_json.at(
"azimuthrate").get<
RealType>();
748 const RealType rate_el_deg_s = fixed_json.at(
"elevationrate").get<
RealType>();
751 start.azimuth = (90.0 - start_az_deg) * (
PI / 180.0);
752 start.elevation = start_el_deg * (
PI / 180.0);
753 rate.azimuth = -rate_az_deg_s * (
PI / 180.0);
754 rate.elevation = rate_el_deg_s * (
PI / 180.0);
755 rot_path->setConstantRate(start, rate);
756 rot_path->finalize();
757 plat->setRotationPath(std::move(rot_path));
761 if (plat_json.contains(
"components"))
763 for (
const auto& comp_json_outer : plat_json.at(
"components"))
765 if (comp_json_outer.contains(
"transmitter"))
767 const auto& comp_json = comp_json_outer.at(
"transmitter");
771 const auto wave_name = comp_json.value(
"waveform",
"");
772 const auto timing_name = comp_json.value(
"timing",
"");
773 const auto antenna_name = comp_json.value(
"antenna",
"");
775 if (wave_name.empty() || !world.
findWaveform(wave_name))
778 "Skipping Transmitter '{}': Missing or invalid waveform '{}'.",
779 comp_json.value(
"name",
"Unnamed"), wave_name);
782 if (timing_name.empty() || !world.
findTiming(timing_name))
785 "Skipping Transmitter '{}': Missing or invalid timing source '{}'.",
786 comp_json.value(
"name",
"Unnamed"), timing_name);
789 if (antenna_name.empty() || !world.
findAntenna(antenna_name))
792 "Skipping Transmitter '{}': Missing or invalid antenna '{}'.",
793 comp_json.value(
"name",
"Unnamed"), antenna_name);
798 if (comp_json.contains(
"pulsed_mode"))
802 else if (comp_json.contains(
"cw_mode"))
808 throw std::runtime_error(
"Transmitter component '" +
809 comp_json.value(
"name",
"Unnamed") +
810 "' must have a 'pulsed_mode' or 'cw_mode' block.");
813 auto trans = std::make_unique<radar::Transmitter>(plat.get(),
814 comp_json.value(
"name",
"Unnamed"), mode);
817 trans->setPrf(comp_json.at(
"pulsed_mode").value(
"prf", 0.0));
821 trans->setAntenna(world.
findAntenna(antenna_name));
823 if (
const auto timing_proto = world.
findTiming(timing_name))
825 const auto timing = std::make_shared<timing::Timing>(timing_name, masterSeeder());
826 timing->initializeModel(timing_proto);
830 if (comp_json.contains(
"schedule"))
832 auto raw = comp_json.at(
"schedule").get<std::vector<radar::SchedulePeriod>>();
836 pri = 1.0 / trans->getPrf();
842 world.
add(std::move(trans));
844 else if (comp_json_outer.contains(
"receiver"))
846 const auto& comp_json = comp_json_outer.at(
"receiver");
850 const auto timing_name = comp_json.value(
"timing",
"");
851 const auto antenna_name = comp_json.value(
"antenna",
"");
853 if (timing_name.empty() || !world.
findTiming(timing_name))
856 "Skipping Receiver '{}': Missing or invalid timing source '{}'.",
857 comp_json.value(
"name",
"Unnamed"), timing_name);
861 if (!antenna_name.empty() && !world.
findAntenna(antenna_name))
864 comp_json.value(
"name",
"Unnamed"), antenna_name);
869 if (comp_json.contains(
"pulsed_mode"))
873 else if (comp_json.contains(
"cw_mode"))
879 throw std::runtime_error(
"Receiver component '" + comp_json.value(
"name",
"Unnamed") +
880 "' must have a 'pulsed_mode' or 'cw_mode' block.");
883 auto recv = std::make_unique<radar::Receiver>(
884 plat.get(), comp_json.value(
"name",
"Unnamed"), masterSeeder(), mode);
887 const auto& mode_json = comp_json.at(
"pulsed_mode");
888 recv->setWindowProperties(mode_json.value(
"window_length", 0.0),
889 mode_json.value(
"prf", 0.0),
890 mode_json.value(
"window_skip", 0.0));
893 recv->setNoiseTemperature(comp_json.value(
"noise_temp", 0.0));
897 if (
const auto timing_proto = world.
findTiming(timing_name))
899 const auto timing = std::make_shared<timing::Timing>(timing_name, masterSeeder());
900 timing->initializeModel(timing_proto);
904 if (comp_json.value(
"nodirect",
false))
908 if (comp_json.value(
"nopropagationloss",
false))
913 if (comp_json.contains(
"schedule"))
915 auto raw = comp_json.at(
"schedule").get<std::vector<radar::SchedulePeriod>>();
919 pri = 1.0 / recv->getWindowPrf();
925 world.
add(std::move(recv));
927 if (comp_json_outer.contains(
"target"))
929 const auto& comp_json = comp_json_outer.at(
"target");
930 const auto& rcs_json = comp_json.at(
"rcs");
931 const auto rcs_type = rcs_json.at(
"type").get<std::string>();
932 std::unique_ptr<radar::Target> target_obj;
934 if (rcs_type ==
"isotropic")
938 rcs_json.at(
"value").get<
RealType>(), masterSeeder());
940 else if (rcs_type ==
"file")
942 const auto filename = rcs_json.value(
"filename",
"");
943 if (filename.empty())
946 "Skipping load of file target '{}': RCS filename is empty.",
947 comp_json.value(
"name",
"Unknown"));
951 plat.get(), comp_json.at(
"name").get<std::string>(), filename, masterSeeder());
955 throw std::runtime_error(
"Unsupported target RCS type: " + rcs_type);
957 world.
add(std::move(target_obj));
960 if (comp_json.contains(
"model"))
962 const auto& model_json = comp_json.at(
"model");
963 if (
const auto model_type = model_json.at(
"type").get<std::string>();
964 model_type ==
"chisquare" || model_type ==
"gamma")
966 auto model = std::make_unique<radar::RcsChiSquare>(
968 world.
getTargets().back()->setFluctuationModel(std::move(model));
973 else if (comp_json_outer.contains(
"monostatic"))
978 const auto& comp_json = comp_json_outer.at(
"monostatic");
981 const auto wave_name = comp_json.value(
"waveform",
"");
982 const auto timing_name = comp_json.value(
"timing",
"");
983 const auto antenna_name = comp_json.value(
"antenna",
"");
985 if (wave_name.empty() || !world.
findWaveform(wave_name))
988 "Skipping Monostatic '{}': Missing or invalid waveform '{}'.",
989 comp_json.value(
"name",
"Unnamed"), wave_name);
992 if (timing_name.empty() || !world.
findTiming(timing_name))
995 "Skipping Monostatic '{}': Missing or invalid timing source '{}'.",
996 comp_json.value(
"name",
"Unnamed"), timing_name);
999 if (antenna_name.empty() || !world.
findAntenna(antenna_name))
1002 "Skipping Monostatic '{}': Missing or invalid antenna '{}'.",
1003 comp_json.value(
"name",
"Unnamed"), antenna_name);
1008 if (comp_json.contains(
"pulsed_mode"))
1012 else if (comp_json.contains(
"cw_mode"))
1018 throw std::runtime_error(
"Monostatic component '" + comp_json.value(
"name",
"Unnamed") +
1019 "' must have a 'pulsed_mode' or 'cw_mode' block.");
1023 auto trans = std::make_unique<radar::Transmitter>(plat.get(),
1024 comp_json.value(
"name",
"Unnamed"), mode);
1027 trans->setPrf(comp_json.at(
"pulsed_mode").value(
"prf", 0.0));
1031 trans->setAntenna(world.
findAntenna(antenna_name));
1032 const auto tx_timing_proto = world.
findTiming(timing_name);
1033 if (tx_timing_proto)
1035 const auto tx_timing = std::make_shared<timing::Timing>(timing_name, masterSeeder());
1036 tx_timing->initializeModel(tx_timing_proto);
1037 trans->setTiming(tx_timing);
1041 auto recv = std::make_unique<radar::Receiver>(
1042 plat.get(), comp_json.value(
"name",
"Unnamed"), masterSeeder(), mode);
1045 const auto& mode_json = comp_json.at(
"pulsed_mode");
1046 recv->setWindowProperties(mode_json.value(
"window_length", 0.0),
1048 mode_json.value(
"window_skip", 0.0));
1050 recv->setNoiseTemperature(comp_json.value(
"noise_temp", 0.0));
1052 recv->setAntenna(world.
findAntenna(antenna_name));
1053 const auto rx_timing_proto = world.
findTiming(timing_name);
1054 if (rx_timing_proto)
1056 const auto rx_timing = std::make_shared<timing::Timing>(timing_name, masterSeeder());
1057 rx_timing->initializeModel(rx_timing_proto);
1058 recv->setTiming(rx_timing);
1061 if (comp_json.value(
"nodirect",
false))
1065 if (comp_json.value(
"nopropagationloss",
false))
1069 if (comp_json.contains(
"schedule"))
1071 auto raw = comp_json.at(
"schedule").get<std::vector<radar::SchedulePeriod>>();
1075 pri = 1.0 / trans->getPrf();
1082 trans->setSchedule(processed_schedule);
1083 recv->setSchedule(processed_schedule);
1087 trans->setAttached(recv.get());
1088 recv->setAttached(trans.get());
1089 world.
add(std::move(trans));
1090 world.
add(std::move(recv));
1095 world.
add(std::move(plat));
1105 const auto num_samples =
static_cast<size_t>(std::ceil((end_time - start_time) / dt_sim));
1111 receiver->prepareCwData(num_samples);
Header file defining various types of antennas and their gain patterns.
Abstract base class representing an antenna.
RealType getEfficiencyFactor() const noexcept
Retrieves the efficiency factor of the antenna.
std::string getName() const noexcept
Retrieves the name of the antenna.
Represents a Gaussian-shaped antenna gain pattern.
Represents an antenna whose gain pattern is loaded from a HDF5 file.
Represents a parabolic reflector antenna.
Represents a sinc function-based antenna gain pattern.
Represents a square horn antenna.
Represents an antenna whose gain pattern is defined by an XML file.
The World class manages the simulator environment.
void scheduleInitialEvents()
Populates the event queue with the initial events for the simulation.
timing::PrototypeTiming * findTiming(const std::string &name)
Finds a timing source by name.
void add(std::unique_ptr< radar::Platform > plat) noexcept
Adds a radar platform to the simulation world.
antenna::Antenna * findAntenna(const std::string &name)
Finds an antenna by name.
const std::vector< std::unique_ptr< radar::Target > > & getTargets() const noexcept
Retrieves the list of radar targets.
const std::unordered_map< std::string, std::unique_ptr< fers_signal::RadarSignal > > & getWaveforms() const noexcept
Retrieves the map of radar signals (waveforms).
fers_signal::RadarSignal * findWaveform(const std::string &name)
Finds a radar signal by name.
void clear() noexcept
Clears all objects and assets from the simulation world.
const std::vector< std::unique_ptr< radar::Platform > > & getPlatforms() const noexcept
Retrieves the list of platforms.
const std::vector< std::unique_ptr< radar::Transmitter > > & getTransmitters() const noexcept
Retrieves the list of radar transmitters.
const std::unordered_map< std::string, std::unique_ptr< timing::PrototypeTiming > > & getTimings() const noexcept
Retrieves the map of timing prototypes.
const std::vector< std::unique_ptr< radar::Receiver > > & getReceivers() const noexcept
Retrieves the list of radar receivers.
const std::unordered_map< std::string, std::unique_ptr< antenna::Antenna > > & getAntennas() const noexcept
Retrieves the map of antennas.
Class representing a radar signal with associated properties.
const std::optional< std::string > & getFilename() const noexcept
Gets the filename associated with this signal.
const std::string & getName() const noexcept
Gets the name of the radar signal.
RealType getCarrier() const noexcept
Gets the carrier frequency of the radar signal.
const Signal * getSignal() const noexcept
Gets the underlying signal object.
RealType getPower() const noexcept
Gets the power of the radar signal.
Represents a path with coordinates and allows for various interpolation methods.
void setInterp(InterpType settype) noexcept
Changes the interpolation type.
InterpType
Types of interpolation supported by the Path class.
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.
InterpType getType() const noexcept
Gets the interpolation type of the path.
void setInterp(InterpType setinterp) noexcept
Sets the interpolation type for the path.
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 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.
const std::string & getName() const noexcept
Retrieves the name of the object.
const antenna::Antenna * getAntenna() const noexcept
Gets the antenna associated with this radar.
std::shared_ptr< timing::Timing > getTiming() const
Retrieves the timing source for the radar.
Chi-square distributed RCS model.
Manages radar signal reception and response processing.
bool checkFlag(RecvFlag flag) const noexcept
Checks if a specific flag is set.
const std::vector< SchedulePeriod > & getSchedule() const noexcept
Retrieves the list of active reception periods.
RealType getNoiseTemperature() const noexcept
Retrieves the noise temperature of the receiver.
OperationMode getMode() const noexcept
Gets the operational mode of the receiver.
RealType getWindowPrf() const noexcept
Retrieves the pulse repetition frequency (PRF) of the radar window.
RealType getWindowSkip() const noexcept
Retrieves the window skip time.
RealType getWindowLength() const noexcept
Retrieves the radar window length.
Base class for radar targets.
const RcsModel * getFluctuationModel() const
Gets the RCS fluctuation model.
Represents a radar transmitter system.
RealType getPrf() const noexcept
Retrieves the pulse repetition frequency (PRF).
fers_signal::RadarSignal * getSignal() const noexcept
Retrieves the radar signal currently being transmitted.
const std::vector< SchedulePeriod > & getSchedule() const noexcept
Retrieves the list of active transmission periods.
OperationMode getMode() const noexcept
Gets the operational mode of the transmitter.
Manages timing properties such as frequency, offsets, and synchronization.
void setSyncOnPulse() noexcept
Enables synchronization on pulse.
void setAlpha(RealType alpha, RealType weight) noexcept
Sets an alpha and weight value.
void setRandomPhaseOffsetStdev(RealType stdev) noexcept
Sets a random phase offset standard deviation.
std::optional< RealType > getRandomPhaseOffsetStdev() const noexcept
bool getSyncOnPulse() const noexcept
Checks if synchronization on pulse is enabled.
std::string getName() const
Gets the name of the timing source.
void setFreqOffset(RealType offset) noexcept
Sets the frequency offset.
std::optional< RealType > getRandomFreqOffsetStdev() const noexcept
void setPhaseOffset(RealType offset) noexcept
Sets the phase offset.
void setRandomFreqOffsetStdev(RealType stdev) noexcept
Sets a random frequency offset standard deviation.
RealType getFrequency() const noexcept
Gets the current frequency.
void setFrequency(const RealType freq) noexcept
Sets the frequency value.
std::optional< RealType > getPhaseOffset() const noexcept
Gets the phase offset.
void copyAlphas(std::vector< RealType > &alphas, std::vector< RealType > &weights) const noexcept
Copies the alphas and weights vectors.
std::optional< RealType > getFreqOffset() const noexcept
Gets the frequency offset.
double RealType
Type for real numbers.
constexpr RealType PI
Mathematical constant π (pi).
Coordinate and rotation structure operations.
Provides functions to serialize and deserialize the simulation world to/from JSON.
void to_json(nlohmann::json &j, const Antenna &a)
void from_json(const nlohmann::json &j, std::unique_ptr< Antenna > &ant)
void from_json(const nlohmann::json &j, std::unique_ptr< RadarSignal > &rs)
void to_json(nlohmann::json &j, const RadarSignal &rs)
@ WARNING
Warning level for potentially harmful situations.
@ INFO
Info level for informational messages.
NLOHMANN_JSON_SERIALIZE_ENUM(Path::InterpType, {{Path::InterpType::INTERP_STATIC, "static"}, {Path::InterpType::INTERP_LINEAR, "linear"}, {Path::InterpType::INTERP_CUBIC, "cubic"}}) void to_json(nlohmann
void to_json(nlohmann::json &j, const Vec3 &v)
void from_json(const nlohmann::json &j, Vec3 &v)
RealType endTime() noexcept
Get the end time for the simulation.
RealType rate() noexcept
Get the rendering sample rate.
RealType startTime() noexcept
Get the start time for the simulation.
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
CoordinateFrame
Defines the coordinate systems supported for scenario definition.
@ UTM
Universal Transverse Mercator.
@ ENU
East-North-Up local tangent plane (default)
@ ECEF
Earth-Centered, Earth-Fixed.
NLOHMANN_JSON_SERIALIZE_ENUM(CoordinateFrame, {{CoordinateFrame::ENU, "ENU"}, {CoordinateFrame::UTM, "UTM"}, {CoordinateFrame::ECEF, "ECEF"}}) void to_json(nlohmann
void from_json(const nlohmann::json &j, Parameters &p)
void to_json(nlohmann::json &j, const SchedulePeriod &p)
std::unique_ptr< Target > createIsoTarget(Platform *platform, std::string name, RealType rcs, unsigned seed)
Creates an isotropic target.
void from_json(const nlohmann::json &j, SchedulePeriod &p)
std::vector< SchedulePeriod > processRawSchedule(std::vector< SchedulePeriod > periods, const std::string &ownerName, const bool isPulsed, const RealType pri)
Processes a raw list of schedule periods.
std::unique_ptr< Target > createFileTarget(Platform *platform, std::string name, const std::string &filename, unsigned seed)
Creates a file-based target.
OperationMode
Defines the operational mode of a radar component.
@ PULSED_MODE
The component operates in a pulsed mode.
@ CW_MODE
The component operates in a continuous-wave mode.
void json_to_world(const nlohmann::json &j, core::World &world, std::mt19937 &masterSeeder)
Deserializes a nlohmann::json object and reconstructs the simulation world.
std::unique_ptr< RadarSignal > loadWaveformFromFile(const std::string &name, const std::string &filename, const RealType power, const RealType carrierFreq)
Loads a radar waveform from a file and returns a RadarSignal object.
nlohmann::json world_to_json(const core::World &world)
Serializes the entire simulation world into a nlohmann::json object.
void from_json(const nlohmann::json &j, PrototypeTiming &pt)
void to_json(nlohmann::json &j, const PrototypeTiming &pt)
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 ...
Header file for the PrototypeTiming class.
Classes for handling radar waveforms and signals.
Radar Receiver class for managing signal reception and response handling.
Defines the RotationPath class for handling rotational paths with different interpolation types.
Represents a position in 3D space with an associated time.
Represents a rotation in terms of azimuth, elevation, and time.
RealType elevation
Elevation angle.
RealType azimuth
Azimuth angle.
Struct to hold simulation parameters.
RealType rate
Rendering sample rate.
double origin_longitude
Geodetic origin longitude.
RealType start
Start time for the simulation.
double origin_altitude
Geodetic origin altitude (in meters)
CoordinateFrame coordinate_frame
Scenario coordinate frame.
RealType end
End time for the simulation.
int utm_zone
UTM zone (1-60), if applicable.
unsigned oversample_ratio
Oversampling ratio.
std::optional< unsigned > random_seed
Random seed for simulation.
RealType sim_sampling_rate
Temporal sampling rate (Hz) that determines time-step resolution for radar pulse simulation.
std::string simulation_name
The name of the simulation, from the XML.
RealType c
Speed of light (modifiable)
static constexpr RealType DEFAULT_C
Speed of light (m/s)
unsigned adc_bits
ADC quantization bits.
bool utm_north_hemisphere
UTM hemisphere, if applicable.
double origin_latitude
Geodetic origin latitude.
Represents a time period during which the transmitter is active.
Defines classes for radar targets and their Radar Cross-Section (RCS) models.
Timing source for simulation objects.
Header file for the Transmitter class in the radar namespace.
Header file for the World class in the simulator.