FERS 1.0.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 [[nodiscard]] unsigned checked_sample_count(const std::size_t sample_count, const std::string_view source)
39 {
40 if (sample_count > static_cast<std::size_t>(std::numeric_limits<unsigned>::max()))
41 {
42 throw std::runtime_error(std::format("Waveform '{}' has too many samples to load into Signal.", source));
43 }
44 return static_cast<unsigned>(sample_count);
45 }
46
47 [[nodiscard]] unsigned parse_csv_sample_count(const RealType sample_count, const std::filesystem::path& filepath)
48 {
49 if (!std::isfinite(sample_count) || sample_count < 0.0 || std::trunc(sample_count) != sample_count)
50 {
51 throw std::runtime_error("Waveform file '" + filepath.string() + "' has an invalid sample count header.");
52 }
53
54 if (sample_count > static_cast<RealType>(std::numeric_limits<unsigned>::max()))
55 {
56 throw std::runtime_error("Waveform file '" + filepath.string() +
57 "' declares more samples than Signal can represent.");
58 }
59
60 return static_cast<unsigned>(sample_count);
61 }
62
63 /**
64 * @brief Loads a radar waveform from an HDF5 file and returns a RadarSignal object.
65 *
66 * @param name The name of the radar signal.
67 * @param filepath The path to the HDF5 file containing the waveform data.
68 * @param power The power of the radar signal in the waveform.
69 * @param carrierFreq The carrier frequency of the radar signal.
70 * @return A unique pointer to a RadarSignal object loaded with the waveform data.
71 * @throws std::runtime_error If the file cannot be opened or the file format is unrecognized.
72 */
73 std::unique_ptr<RadarSignal> loadWaveformFromHdf5File(const std::string& name,
74 const std::filesystem::path& filepath, const RealType power,
75 const RealType carrierFreq, const SimId id)
76 {
77 std::vector<ComplexType> data;
78 serial::readPulseData(filepath.string(), data);
79 const unsigned sample_count = checked_sample_count(data.size(), filepath.string());
80
81 auto signal = std::make_unique<Signal>();
82 signal->load(data, sample_count, params::rate());
83 return std::make_unique<RadarSignal>(
84 name, power, carrierFreq, static_cast<RealType>(sample_count) / params::rate(), std::move(signal), id);
85 }
86
87 /**
88 * @brief Loads a radar waveform from a CSV file and returns a RadarSignal object.
89 *
90 * @param name The name of the radar signal.
91 * @param filepath The path to the CSV file containing the waveform data.
92 * @param power The power of the radar signal in the waveform.
93 * @param carrierFreq The carrier frequency of the radar signal.
94 * @return A unique pointer to a RadarSignal object loaded with the waveform data.
95 * @throws std::runtime_error If the file cannot be opened or the file format is unrecognized.
96 */
97 std::unique_ptr<RadarSignal> loadWaveformFromCsvFile(const std::string& name, const std::filesystem::path& filepath,
98 const RealType power, const RealType carrierFreq,
99 const SimId id)
100 {
101 std::ifstream ifile(filepath);
102 if (!ifile)
103 {
104 LOG(logging::Level::FATAL, "Could not open file '{}' to read waveform", filepath.string());
105 throw std::runtime_error("Could not open file '" + filepath.string() + "' to read waveform");
106 }
107
108 RealType rlength = 0.0;
109 RealType rate = 0.0;
110 if (!(ifile >> rlength >> rate))
111 {
112 LOG(logging::Level::FATAL, "Could not read waveform header from file '{}'", filepath.string());
113 throw std::runtime_error("Could not read waveform header from file '" + filepath.string() + "'");
114 }
115 if (!std::isfinite(rate) || rate <= 0.0)
116 {
117 LOG(logging::Level::FATAL, "Waveform file '{}' has invalid sample rate {}", filepath.string(), rate);
118 throw std::runtime_error("Waveform file '" + filepath.string() + "' has an invalid sample rate");
119 }
120
121 const unsigned length = parse_csv_sample_count(rlength, filepath);
122 std::vector<ComplexType> data(length);
123
124 // Read the file data
125 for (std::size_t done = 0; done < length && ifile >> data[done]; ++done)
126 {
127 }
128
129 if (ifile.fail() || data.size() != length)
130 {
131 LOG(logging::Level::FATAL, "Could not read full waveform from file '{}'", filepath.string());
132 throw std::runtime_error("Could not read full waveform from file '" + filepath.string() + "'");
133 }
134
135 auto signal = std::make_unique<Signal>();
136 signal->load(data, length, rate);
137 return std::make_unique<RadarSignal>(name, power, carrierFreq, rlength / rate, std::move(signal), id);
138 }
139
140 /**
141 * @brief Checks if a filename has a specific extension.
142 *
143 * @param filename The filename to check.
144 * @param ext The extension to check for.
145 * @return True if the filename has the specified extension, false otherwise.
146 */
147 constexpr bool hasExtension(const std::string_view filename, const std::string_view ext) noexcept
148 {
149 return filename.ends_with(ext);
150 }
151}
152
153namespace serial
154{
155 std::unique_ptr<RadarSignal> loadWaveformFromFile(const std::string& name, const std::string& filename,
156 const RealType power, const RealType carrierFreq, const SimId id)
157 {
158 const std::filesystem::path filepath = filename;
159 const auto extension = filepath.extension().string();
160
161 if (hasExtension(extension, ".csv"))
162 {
163 auto wave = loadWaveformFromCsvFile(name, filepath, power, carrierFreq, id);
164 wave->setFilename(filename);
165 return wave;
166 }
167 if (hasExtension(extension, ".h5"))
168 {
169 auto wave = loadWaveformFromHdf5File(name, filepath, power, carrierFreq, id);
170 wave->setFilename(filename);
171 return wave;
172 }
173
174 LOG(logging::Level::FATAL, "Unrecognized file extension '{}' for file: '{}'", extension, filename);
175 throw std::runtime_error("Unrecognized file extension '" + extension + "' for file: " + filename);
176 }
177}
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
Interface for loading waveform data into RadarSignal objects.