FERS 0.1.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
waveform_factory.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright (c) 2006-2008 Marc Brooker and Michael Inggs
4// Copyright (c) 2008-present FERS Contributors (see AUTHORS.md).
5//
6// See the GNU GPLv2 LICENSE file in the FERS project root for more information.
7
8/**
9 * @file waveform_factory.cpp
10 * @brief Implementation for loading waveform data into RadarSignal objects.
11 */
12
13#include "waveform_factory.h"
14
15#include <cmath>
16#include <complex>
17#include <cstddef>
18#include <filesystem>
19#include <fstream>
20#include <limits>
21#include <span>
22#include <stdexcept>
23#include <string_view>
24#include <utility>
25#include <vector>
26
27#include "core/config.h"
28#include "core/parameters.h"
29#include "core/sim_id.h"
30#include "hdf5_handler.h"
31#include "signal/radar_signal.h"
32
35
36namespace
37{
38 /// Converts a sample count to unsigned after validating the supported range.
39 [[nodiscard]] unsigned checked_sample_count(const std::size_t sample_count, const std::string_view source)
40 {
41 if (sample_count > static_cast<std::size_t>(std::numeric_limits<unsigned>::max()))
42 {
43 throw std::runtime_error(std::format("Waveform '{}' has too many samples to load into Signal.", source));
44 }
45 return static_cast<unsigned>(sample_count);
46 }
47
48 /// Parses and validates a CSV waveform sample count header.
49 [[nodiscard]] unsigned parse_csv_sample_count(const RealType sample_count, const std::filesystem::path& filepath)
50 {
51 if (!std::isfinite(sample_count) || sample_count < 0.0 || std::trunc(sample_count) != sample_count)
52 {
53 throw std::runtime_error("Waveform file '" + filepath.string() + "' has an invalid sample count header.");
54 }
55
56 if (sample_count > static_cast<RealType>(std::numeric_limits<unsigned>::max()))
57 {
58 throw std::runtime_error("Waveform file '" + filepath.string() +
59 "' declares more samples than Signal can represent.");
60 }
61
62 return static_cast<unsigned>(sample_count);
63 }
64
65 /**
66 * @brief Loads a radar waveform from an HDF5 file and returns a RadarSignal object.
67 *
68 * @param name The name of the radar signal.
69 * @param filepath The path to the HDF5 file containing the waveform data.
70 * @param power The power of the radar signal in the waveform.
71 * @param carrierFreq The carrier frequency of the radar signal.
72 * @return A unique pointer to a RadarSignal object loaded with the waveform data.
73 * @throws std::runtime_error If the file cannot be opened or the file format is unrecognized.
74 */
75 std::unique_ptr<RadarSignal> loadWaveformFromHdf5File(const std::string& name,
76 const std::filesystem::path& filepath, const RealType power,
77 const RealType carrierFreq, const SimId id)
78 {
79 std::vector<ComplexType> data;
81 const unsigned sample_count = checked_sample_count(data.size(), filepath.string());
82
83 auto signal = std::make_unique<Signal>();
84 signal->load(data, sample_count, params::rate());
85 return std::make_unique<RadarSignal>(
86 name, power, carrierFreq, static_cast<RealType>(sample_count) / params::rate(), std::move(signal), id);
87 }
88
89 /**
90 * @brief Loads a radar waveform from a CSV file and returns a RadarSignal object.
91 *
92 * @param name The name of the radar signal.
93 * @param filepath The path to the CSV file containing the waveform data.
94 * @param power The power of the radar signal in the waveform.
95 * @param carrierFreq The carrier frequency of the radar signal.
96 * @return A unique pointer to a RadarSignal object loaded with the waveform data.
97 * @throws std::runtime_error If the file cannot be opened or the file format is unrecognized.
98 */
99 std::unique_ptr<RadarSignal> loadWaveformFromCsvFile(const std::string& name, const std::filesystem::path& filepath,
100 const RealType power, const RealType carrierFreq,
101 const SimId id)
102 {
103 std::ifstream ifile(filepath);
104 if (!ifile)
105 {
106 LOG(logging::Level::FATAL, "Could not open file '{}' to read waveform", filepath.string());
107 throw std::runtime_error("Could not open file '" + filepath.string() + "' to read waveform");
108 }
109
110 RealType rlength = 0.0;
111 RealType rate = 0.0;
112 if (!(ifile >> rlength >> rate))
113 {
114 LOG(logging::Level::FATAL, "Could not read waveform header from file '{}'", filepath.string());
115 throw std::runtime_error("Could not read waveform header from file '" + filepath.string() + "'");
116 }
117 if (!std::isfinite(rate) || rate <= 0.0)
118 {
119 LOG(logging::Level::FATAL, "Waveform file '{}' has invalid sample rate {}", filepath.string(), rate);
120 throw std::runtime_error("Waveform file '" + filepath.string() + "' has an invalid sample rate");
121 }
122
123 const unsigned length = parse_csv_sample_count(rlength, filepath);
124 std::vector<ComplexType> data(length);
125
126 // Read the file data
127 for (std::size_t done = 0; done < length && ifile >> data[done]; ++done)
128 {
129 }
130
131 if (ifile.fail() || data.size() != length)
132 {
133 LOG(logging::Level::FATAL, "Could not read full waveform from file '{}'", filepath.string());
134 throw std::runtime_error("Could not read full waveform from file '" + filepath.string() + "'");
135 }
136
137 auto signal = std::make_unique<Signal>();
138 signal->load(data, length, rate);
139 return std::make_unique<RadarSignal>(name, power, carrierFreq, rlength / rate, std::move(signal), id);
140 }
141
142 /**
143 * @brief Checks if a filename has a specific extension.
144 *
145 * @param filename The filename to check.
146 * @param ext The extension to check for.
147 * @return True if the filename has the specified extension, false otherwise.
148 */
149 constexpr bool hasExtension(const std::string_view filename, const std::string_view ext) noexcept
150 {
151 return filename.ends_with(ext);
152 }
153}
154
155namespace serial
156{
157 std::unique_ptr<RadarSignal> loadWaveformFromFile(const std::string& name, const std::string& filename,
158 const RealType power, const RealType carrierFreq, const SimId id)
159 {
160 const std::filesystem::path filepath = filename;
161 const auto extension = filepath.extension().string();
162
163 if (hasExtension(extension, ".csv"))
164 {
165 auto wave = loadWaveformFromCsvFile(name, filepath, power, carrierFreq, id);
166 wave->setFilename(filename);
167 return wave;
168 }
169 if (hasExtension(extension, ".h5"))
170 {
171 auto wave = loadWaveformFromHdf5File(name, filepath, power, carrierFreq, id);
172 wave->setFilename(filename);
173 return wave;
174 }
175
176 LOG(logging::Level::FATAL, "Unrecognized file extension '{}' for file: '{}'", extension, filename);
177 throw std::runtime_error("Unrecognized file extension '" + extension + "' for file: " + filename);
178 }
179}
Class representing a radar signal with associated properties.
Class for handling radar waveform signal data.
Global configuration file for the project.
double RealType
Type for real numbers.
Definition config.h:27
Header file for HDF5 data export and import functions.
#define LOG(level,...)
Definition logging.h:19
@ FATAL
Fatal level for severe error events.
RealType rate() noexcept
Get the rendering sample rate.
Definition parameters.h:121
std::unique_ptr< RadarSignal > loadWaveformFromFile(const std::string &name, const std::string &filename, const RealType power, const RealType carrierFreq, const SimId id)
Loads a radar waveform from a file and returns a RadarSignal object.
void readPulseData(const std::string &name, std::vector< ComplexType > &data)
Reads pulse data from an HDF5 file.
Defines the Parameters struct and provides methods for managing simulation parameters.
Classes for handling radar waveforms and signals.
uint64_t SimId
64-bit Unique Simulation ID.
Definition sim_id.h:18
math::Vec3 max
Interface for loading waveform data into RadarSignal objects.