19#include <highfive/highfive.hpp>
38 if (value.has_value())
40 file.createAttribute(name, *value);
46 const std::optional<T>& value)
48 if (value.has_value())
50 file.createAttribute(name,
static_cast<unsigned long long>(*value));
56 file.createAttribute(
"fers_metadata_schema_version", 1U);
58 file.createAttribute(
"receiver_id",
static_cast<unsigned long long>(metadata.
receiver_id));
60 file.createAttribute(
"data_mode", metadata.
mode);
65 file.createAttribute(
"total_samples",
static_cast<unsigned long long>(metadata.
total_samples));
66 file.createAttribute(
"sample_start",
static_cast<unsigned long long>(metadata.
sample_start));
67 file.createAttribute(
"sample_end_exclusive",
69 file.createAttribute(
"streaming_segment_count",
71 file.createAttribute(
"fmcw_source_count",
static_cast<unsigned long long>(metadata.
fmcw_sources.size()));
72 file.createAttribute(
"sfcw_source_count",
static_cast<unsigned long long>(metadata.
sfcw_sources.size()));
159 if (
segment.first_chirp_start_time.has_value())
163 if (
segment.emitted_chirp_count.has_value())
166 static_cast<unsigned long long>(*
segment.emitted_chirp_count));
168 if (
segment.first_triangle_start_time.has_value())
172 if (
segment.emitted_triangle_count.has_value())
175 static_cast<unsigned long long>(*
segment.emitted_triangle_count));
180 auto attr =
file.createAttribute<
RealType>(
"streaming_first_chirp_start_time",
186 auto attr =
file.createAttribute<
unsigned long long>(
192 auto attr =
file.createAttribute<
RealType>(
"streaming_first_triangle_start_time",
198 auto attr =
file.createAttribute<
unsigned long long>(
206 if (!metadata.
fmcw.has_value())
216 if (!metadata.
sfcw.has_value())
220 const auto& sfcw = *metadata.
sfcw;
221 file.createAttribute(
"sfcw_carrier_frequency", sfcw.carrier_frequency);
222 file.createAttribute(
"sfcw_start_frequency_offset", sfcw.start_frequency_offset);
223 file.createAttribute(
"sfcw_step_size", sfcw.step_size);
224 file.createAttribute(
"sfcw_step_count",
static_cast<unsigned long long>(sfcw.step_count));
225 file.createAttribute(
"sfcw_dwell_time", sfcw.dwell_time);
226 file.createAttribute(
"sfcw_step_period", sfcw.step_period);
228 file.createAttribute(
"sfcw_first_frequency", sfcw.first_frequency);
229 file.createAttribute(
"sfcw_last_frequency", sfcw.last_frequency);
230 file.createAttribute(
"sfcw_frequency_span", sfcw.frequency_span);
231 file.createAttribute(
"sfcw_effective_bandwidth", sfcw.effective_bandwidth);
232 file.createAttribute(
"sfcw_range_resolution", sfcw.range_resolution);
233 file.createAttribute(
"sfcw_unambiguous_range", sfcw.unambiguous_range);
250 if (!std::filesystem::exists(name))
252 LOG(Level::FATAL,
"File '{}' not found", name);
253 throw std::runtime_error(
"File " + name +
" not found.");
256 LOG(Level::TRACE,
"Opening file '{}'", name);
257 const HighFive::File
file(name, HighFive::File::ReadOnly);
275 LOG(Level::TRACE,
"Reading dataset 'I' from file '{}'", name);
280 LOG(Level::TRACE,
"Reading dataset 'Q' from file '{}'", name);
283 LOG(Level::FATAL,
"Dataset 'Q' is not the same size as dataset 'I' in file '{}'", name);
284 throw std::runtime_error(R
"(Dataset "Q" is not the same size as dataset "I" in file )" + name);
288 for (
size_t i = 0; i <
size; ++i)
292 LOG(Level::TRACE,
"Read dataset successfully");
300 const std::size_t
size =
data.size();
302 const std::string
base_chunk_name =
"chunk_" + std::format(
"{:06}", count);
306 std::vector<RealType> i(
size), q(
size);
307 std::ranges::transform(
data, i.begin(), [](
const ComplexType&
c) { return c.real(); });
308 std::ranges::transform(
data, q.begin(), [](
const ComplexType&
c) { return c.imag(); });
318 catch (
const HighFive::Exception&
err)
320 LOG(Level::FATAL,
"Error while writing data to HDF5 file: {}",
err.what());
321 throw std::runtime_error(
"Error while writing data to HDF5 file: " +
chunkName +
" - " +
err.what());
330 dataset.createAttribute(
"time", time);
332 dataset.createAttribute(
"fullscale", fullscale);
333 if (metadata !=
nullptr)
336 dataset.createAttribute(
"sample_count",
static_cast<unsigned long long>(metadata->
sample_count));
337 dataset.createAttribute(
"sample_start",
static_cast<unsigned long long>(metadata->
sample_start));
338 dataset.createAttribute(
"sample_end_exclusive",
342 catch (
const HighFive::Exception&
err)
344 LOG(Level::FATAL,
"Error while setting attributes on chunk: {}",
err.what());
345 throw std::runtime_error(
"Error while setting attributes on chunk: " +
chunkName +
" - " +
err.what());
361 LOG(Level::TRACE,
"Reading dataset '{}' from file '{}'",
datasetName, name);
362 const HighFive::File
file(name, HighFive::File::ReadOnly);
369 if (
dims.size() != 2)
371 LOG(Level::FATAL,
"Invalid dataset dimensions for '{}' in file '{}'",
datasetName, name);
372 throw std::runtime_error(
373 std::format(R
"(Invalid dataset dimensions for "{}" in file "{}")", datasetName, name));
376 LOG(Level::TRACE, "Reading dataset with dimensions {}x{}",
dims[0],
dims[1]);
378 std::vector
data(
dims[0], std::vector<RealType>(
dims[1]));
381 LOG(Level::TRACE,
"Read dataset successfully");
385 catch (
const HighFive::Exception&
err)
387 LOG(Level::FATAL,
"Error handling HDF5 file: {}",
err.what());
388 throw std::runtime_error(
"Error handling HDF5 file: " + std::string(
err.what()));
double RealType
Type for real numbers.
std::complex< RealType > ComplexType
Type for complex numbers.
Header file for HDF5 data export and import functions.
Header file for the logging system.
std::string outputFileMetadataToJsonString(const OutputFileMetadata &metadata)
Serializes one output-file metadata entry to JSON.
RealType rate() noexcept
Get the rendering sample rate.
std::mutex hdf5_global_mutex
Global mutex to protect all HDF5 C-library calls, which are not thread-safe.
void writeOutputFileMetadataAttributes(HighFive::File &file, const core::OutputFileMetadata &metadata)
Writes additive FERS output metadata attributes to an open HDF5 file.
void addChunkToFile(HighFive::File &file, const std::vector< ComplexType > &data, const RealType time, const RealType fullscale, const unsigned count, const core::PulseChunkMetadata *metadata)
Adds a chunk of data to an HDF5 file.
void readPulseData(const std::string &name, std::vector< ComplexType > &data)
Reads pulse data from an HDF5 file.
std::vector< std::vector< RealType > > readPattern(const std::string &name, const std::string &datasetName)
Reads a 2D antenna gain pattern from the named dataset.
Defines the Parameters struct and provides methods for managing simulation parameters.