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));
59 file.createAttribute(
"receiver_name", metadata.
receiver_name);
60 file.createAttribute(
"data_mode", metadata.
mode);
63 file.createAttribute(
"output_sampling_rate", metadata.
sampling_rate);
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()));
164 if (
segment.first_chirp_start_time.has_value())
168 if (
segment.emitted_chirp_count.has_value())
171 static_cast<unsigned long long>(*
segment.emitted_chirp_count));
173 if (
segment.first_triangle_start_time.has_value())
177 if (
segment.emitted_triangle_count.has_value())
180 static_cast<unsigned long long>(*
segment.emitted_triangle_count));
185 auto attr = file.createAttribute<
RealType>(
"streaming_first_chirp_start_time",
191 auto attr = file.createAttribute<
unsigned long long>(
197 auto attr = file.createAttribute<
RealType>(
"streaming_first_triangle_start_time",
203 auto attr = file.createAttribute<
unsigned long long>(
211 if (!metadata.
fmcw.has_value())
221 if (!metadata.
sfcw.has_value())
225 const auto& sfcw = *metadata.
sfcw;
226 file.createAttribute(
"sfcw_carrier_frequency", sfcw.carrier_frequency);
227 file.createAttribute(
"sfcw_start_frequency_offset", sfcw.start_frequency_offset);
228 file.createAttribute(
"sfcw_step_size", sfcw.step_size);
229 file.createAttribute(
"sfcw_step_count",
static_cast<unsigned long long>(sfcw.step_count));
230 file.createAttribute(
"sfcw_dwell_time", sfcw.dwell_time);
231 file.createAttribute(
"sfcw_step_period", sfcw.step_period);
233 file.createAttribute(
"sfcw_first_frequency", sfcw.first_frequency);
234 file.createAttribute(
"sfcw_last_frequency", sfcw.last_frequency);
235 file.createAttribute(
"sfcw_frequency_span", sfcw.frequency_span);
236 file.createAttribute(
"sfcw_effective_bandwidth", sfcw.effective_bandwidth);
237 file.createAttribute(
"sfcw_range_resolution", sfcw.range_resolution);
238 file.createAttribute(
"sfcw_unambiguous_range", sfcw.unambiguous_range);
239 file.createAttribute(
"sfcw_flat_attributes_are_summary",
true);
247 group.createAttribute(
"step_count",
static_cast<unsigned long long>(waveform.
step_count));
252 group.createAttribute(
"sweep_count",
static_cast<unsigned long long>(*waveform.
sweep_count));
271 sfcw_group.createAttribute(
"schema_version", 1U);
272 sfcw_group.createAttribute(
"source_count",
static_cast<unsigned long long>(metadata.
sfcw_sources.size()));
278 source_group.createAttribute(
"transmitter_id",
static_cast<unsigned long long>(source.transmitter_id));
279 source_group.createAttribute(
"transmitter_name", source.transmitter_name);
280 source_group.createAttribute(
"waveform_id",
static_cast<unsigned long long>(source.waveform_id));
281 source_group.createAttribute(
"waveform_name", source.waveform_name);
287 segments_group.createAttribute(
"count",
static_cast<unsigned long long>(source.segments.size()));
294 if (
segment.first_step_start_time.has_value())
298 if (
segment.emitted_step_count.has_value())
301 static_cast<unsigned long long>(*
segment.emitted_step_count));
305 file.createAttribute(
"sfcw_metadata_path", std::string(
"/metadata/sfcw"));
323 if (!std::filesystem::exists(name))
325 LOG(Level::FATAL,
"File '{}' not found", name);
326 throw std::runtime_error(
"File " + name +
" not found.");
329 LOG(Level::TRACE,
"Opening file '{}'", name);
330 const HighFive::File file(name, HighFive::File::ReadOnly);
348 LOG(Level::TRACE,
"Reading dataset 'I' from file '{}'", name);
353 LOG(Level::TRACE,
"Reading dataset 'Q' from file '{}'", name);
356 LOG(Level::FATAL,
"Dataset 'Q' is not the same size as dataset 'I' in file '{}'", name);
357 throw std::runtime_error(R
"(Dataset "Q" is not the same size as dataset "I" in file )" + name);
361 for (
size_t i = 0; i <
size; ++i)
365 LOG(Level::TRACE,
"Read dataset successfully");
373 const std::size_t
size =
data.size();
375 const std::string
base_chunk_name =
"chunk_" + std::format(
"{:06}", count);
379 std::vector<RealType> i(
size), q(
size);
380 std::ranges::transform(
data, i.begin(), [](
const ComplexType&
c) { return c.real(); });
381 std::ranges::transform(
data, q.begin(), [](
const ComplexType&
c) { return c.imag(); });
391 catch (
const HighFive::Exception&
err)
393 LOG(Level::FATAL,
"Error while writing data to HDF5 file: {}",
err.what());
394 throw std::runtime_error(
"Error while writing data to HDF5 file: " +
chunkName +
" - " +
err.what());
403 dataset.createAttribute(
"time", time);
405 dataset.createAttribute(
"fullscale", fullscale);
406 if (metadata !=
nullptr)
409 dataset.createAttribute(
"sample_count",
static_cast<unsigned long long>(metadata->
sample_count));
410 dataset.createAttribute(
"sample_start",
static_cast<unsigned long long>(metadata->
sample_start));
411 dataset.createAttribute(
"sample_end_exclusive",
415 catch (
const HighFive::Exception&
err)
417 LOG(Level::FATAL,
"Error while setting attributes on chunk: {}",
err.what());
418 throw std::runtime_error(
"Error while setting attributes on chunk: " +
chunkName +
" - " +
err.what());
434 LOG(Level::TRACE,
"Reading dataset '{}' from file '{}'",
datasetName, name);
435 const HighFive::File file(name, HighFive::File::ReadOnly);
442 if (
dims.size() != 2)
444 LOG(Level::FATAL,
"Invalid dataset dimensions for '{}' in file '{}'",
datasetName, name);
445 throw std::runtime_error(
446 std::format(R
"(Invalid dataset dimensions for "{}" in file "{}")", datasetName, name));
449 LOG(Level::TRACE, "Reading dataset with dimensions {}x{}",
dims[0],
dims[1]);
451 std::vector
data(
dims[0], std::vector<RealType>(
dims[1]));
454 LOG(Level::TRACE,
"Read dataset successfully");
458 catch (
const HighFive::Exception&
err)
460 LOG(Level::FATAL,
"Error handling HDF5 file: {}",
err.what());
461 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.