C++ API Documentation for Quadriga-Lib v0.11.0
Overview
Array antenna class
Array antenna functions
Channel class
Channel functions
Channel generation functions
Math Functions
| fast_acos | Fast, approximate arc-cosine |
| fast_asin | Fast, approximate arc-sine |
| fast_atan2 | Fast, approximate two-argument arc-tangent |
| fast_cart2geo | Fast, approximate Cartesian-to-geographic conversion |
| fast_geo2cart | Fast, approximate geographic-to-Cartesian conversion |
| fast_sincos | Fast, approximate sine/cosine |
| fast_slerp | Fast, approximate spherical interpolation (SLERP) for complex value pairs |
Miscellaneous / Tools
Site-Specific Simulation Tools
Array antenna class
arrayant - Class for storing and manipulating array antenna models
-
Description:
- An array antenna consists of multiple individual elements.
- Each element occupies a specific position relative to the array's phase-center, its local origin.
- Elements can also be inter-coupled, represented by a coupling matrix.
-
Attributes:
arma::Cube<dtype> e_theta_re |
Vertical component of the electric field, real part |
arma::Cube<dtype> e_theta_im |
Vertical component of the electric field, imaginary part |
arma::Cube<dtype> e_phi_re |
Horizontal component of the electric field, real part |
arma::Cube<dtype> e_phi_im |
Horizontal component of the electric field, imaginary part |
arma::Col<dtype> azimuth_grid |
Azimuth angles in pattern (theta) in [rad], between -pi and pi, sorted |
arma::Col<dtype> elevation_grid |
Elevation angles in pattern (phi) in [rad], between -pi/2 and pi/2, sorted |
arma::Mat<dtype> element_pos |
Element positions (optional), Size: Empty or [3, n_elements] |
arma::Mat<dtype> coupling_re |
Coupling matrix, real part (optional), Size: [n_elements, n_ports] |
arma::Mat<dtype> coupling_im |
Coupling matrix, imaginary part (optional), Size: [n_elements, n_ports] |
dtype center_frequency |
Center frequency in [Hz] |
- Allowed datatypes (
dtype): float and double
e_theta_re, e_theta_im, e_phi_re, e_phi_im must have size [n_elevation, n_azimuth, n_elements]
-
Example:
float pi = arma::datum::pi;
quadriga_lib::arrayant<float> ant;
ant.azimuth_grid = {-0.75f * pi, 0.0f, 0.75f * pi, pi};
ant.elevation_grid = {-0.45f * pi, 0.0f, 0.45f * pi};
arma::mat A = arma::linspace(1.0, 12.0, 12);
A.reshape(3, 4);
arma::fcube B;
B.zeros(3, 4, 1);
B.slice(0) = arma::conv_to<arma::fmat>::from(A);
ant.e_theta_re = B * 0.5f;
ant.e_theta_im = B * 0.002f;
ant.e_phi_re = -B;
ant.e_phi_im = -B * 0.001f;
arma::fmat C = {1.0f, 2.0f, 4.0f};
ant.element_pos = C.t();
ant.coupling_re = {1.0f};
ant.coupling_im = {0.1f};
ant.center_frequency = 2.0e9f;
ant.name = "name";
-
Simple member functions:
.n_elevation() |
Returns number of elevation angles as 64bit integer |
.n_azimuth() |
Returns number of azimuth angles as 64bit integer |
.n_elements() |
Returns number of antenna elements as 64bit integer |
.n_ports() |
Returns number of ports (after coupling) as 64bit integer |
.copy() |
Creates a copy of the array antenna object |
.reset() |
Reset the size to zero (the arrayant object will contain no data) |
.is_valid() |
Returns an empty string if arrayant object is valid or an error message otherwise |
-
Complex member fuctions:
.append - Append elements of another antenna array
-
Description:
- Combines elements of another antenna array (
new_arrayant) with the current antenna array object.
- Returns a new
arrayant object containing elements from both antenna arrays.
- Throws an error if the sampling grids of the two antenna arrays do not match.
- Member function of arrayant
- Allowed datatypes (
dtype): float or double
-
Declaration:
quadriga_lib::arrayant<dtype> quadriga_lib::arrayant<dtype>::append(const arrayant<dtype> *new_arrayant) const;
-
Arguments:
const arrayant<dtype> *new_arrayant (input)
Pointer to an antenna array object whose elements will be added to the current object. Sampling grids must match exactly.
-
Returns:
quadriga_lib::arrayant<dtype>
A new antenna array object combining the current and new antenna elements.
-
Example:
quadriga_lib::arrayant<double> ant1 = quadriga_lib::generate_arrayant_custom<double>(90.0, 90.0, 0.0);
quadriga_lib::arrayant<double> ant2 = quadriga_lib::generate_arrayant_custom<double>(120.0, 60.0, 0.0);
quadriga_lib::arrayant<double> combined_ant = ant1.append(&ant2);
.calc_directivity_dbi - Calculate the directivity (in dBi) of array antenna elements
.combine_pattern - Calculate effective radiation patterns for array antennas
-
Description:
- Member function of arrayant
- By integrating element radiation patterns, element positions, and the coupling weights, one can
determine an effective radiation pattern observable by a receiver in the antenna's far field.
- Leveraging these effective patterns is especially beneficial in antenna design, beamforming
applications such as in 5G systems, and in planning wireless communication networks in complex
environments like urban areas. This streamlined approach offers a significant boost in computation
speed when calculating MIMO channel coefficients, as it reduces the number of necessary operations.
- Allowed datatypes (
dtype): float and double
-
Declaration:
quadriga_lib::arrayant<dtype> quadriga_lib::arrayant<dtype>::combine_pattern(
const arma::Col<dtype> *azimuth_grid_new = nullptr,
const arma::Col<dtype> *elevation_grid_new = nullptr) const;
-
Arguments:
arma::Col<dtype> *azimuth_grid_new (optional)
Azimuth angle grid of the output array antenna in [rad], between -pi and pi, sorted
arma::Col<dtype> *elevation_grid_new (optional)
Elevation angle grid of the output array antenna in [rad], between -pi/2 and pi/2, sorted
-
Example:
auto ant = quadriga_lib::generate_arrayant_omni<double>(); // Generate omni antenna
ant.copy_element(0, 1); // Duplicate the first element
ant.element_pos.row(1) = {-0.25, 0.25}; // Set element positions (in lambda)
ant.coupling_re.ones(2, 1); // Set coupling matrix (real part)
ant.coupling_im.reset(); // Remove imaginary part
ant = ant.combine_pattern(); // Calculate the combined pattern
.copy_element - Creates a copy of a single array antenna element
-
Description:
- Member function of arrayant
- Allowed datatypes (
dtype): float and double
-
Declaration:
void quadriga_lib::arrayant<dtype>::copy_element(arma::uword source, arma::uvec destination);
void quadriga_lib::arrayant<dtype>::copy_element(arma::uword source, arma::uword destination);
-
Arguments:
arma::uword source (optional)
Index of the source element (0-based)
arma::uvec destination or arma::uword destination
Index of the destinations element (0-based), either as a vector or as a scalar.
-
Example:
auto ant = quadriga_lib::generate_arrayant_omni<double>(); // Generate omni antenna
ant.copy_element(0, 1); // Duplicate the first element
ant.copy_element(1, {2,3}); // Duplicate multiple times
.export_obj_file - Export antenna pattern geometry to Wavefront OBJ file
-
Description:
- This function exports the antenna pattern geometry to a Wavefront OBJ file, useful for visualization in 3D software such as Blender.
- Member function of arrayant
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant<dtype>::export_obj_file(
std::string fn,
dtype directivity_range = 30.0,
std::string colormap = "jet",
dtype object_radius = 1.0,
arma::uword icosphere_n_div = 4,
arma::uvec i_element = {}) const;
-
Arguments:
std::string fn (input)
Filename of the OBJ file to which the antenna pattern will be exported. Cannot be empty.
dtype directivity_range = 30.0 (optional input)
Directivity range in decibels (dB) for visualizing the antenna pattern. This value defines the
dynamic range of the visualized directivity pattern. Default: 30.0
std::string colormap = "jet" (optional input)
Colormap used for visualizing the antenna directivity. Supported colormaps are: jet,
parula, winter, hot, turbo, copper, spring, cool, gray, autumn, summer.
Default: "jet"
dtype object_radius = 1.0 (optional input)
Radius of the exported antenna pattern geometry object, specified in meters. Default: 1.0
arma::uword icosphere_n_div = 4 (optional input)
Number of subdivisions used to map the antenna pattern onto an icosphere. Higher values yield finer
mesh resolution. Default: 4
arma::uvec i_element = {} (optional input)
Antenna element indices for which the pattern geometry is exported. Indices are 0-based. Providing
an empty vector {} (default) exports the geometry for all elements of the antenna array.
-
Example:
auto ant = quadriga_lib::generate_arrayant_custom<double>(90.0, 90.0, 0.0);
ant.export_obj_file("antenna_pattern.obj", 40.0, "turbo", 1.5, 5);
.interpolate - Interpolate array antenna field patterns
-
Description:
- This function interpolates polarimetric antenna field patterns for a given set of azimuth and
elevation angles.
- Member function of arrayant
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant<dtype>::interpolate(
const arma::Mat<dtype> *azimuth,
const arma::Mat<dtype> *elevation,
arma::Mat<dtype> *V_re, arma::Mat<dtype> *V_im,
arma::Mat<dtype> *H_re, arma::Mat<dtype> *H_im,
arma::uvec i_element,
const arma::Cube<dtype> *orientation,
const arma::Mat<dtype> *element_pos_i,
arma::Mat<dtype> *dist,
arma::Mat<dtype> *azimuth_loc, arma::Mat<dtype> *elevation_loc,
arma::Mat<dtype> *gamma) const;
-
Arguments:
const arma::Mat<dtype> *azimuth (input)
Azimuth angles in [rad] for which the field pattern should be interpolated. Values must be
between -pi and pi, cannot be NULL
| Option 1: |
Use the same angles for all antenna elements (planar wave approximation) |
|
Size: [1, n_ang] |
| Option 2: |
Provide different angles for each array element (e.g. for spherical waves) |
|
Size: [n_out, n_ang] |
const arma::Mat<dtype> *elevation (input)
Elevation angles in [rad] for which the field pattern should be interpolated. Values must be
between -pi/2 and pi/2, cannot be NULL
| Option 1: |
Use the same angles for all antenna elements (planar wave approximation) |
|
Size: [1, n_ang] |
| Option 2: |
Provide different angles for each array element (e.g. for spherical waves) |
|
Size: [n_out, n_ang] |
arma::Mat<dtype> *V_re (output)
Real part of the interpolated e-theta (vertical) field component, Size [n_out, n_ang],
will be resized if it does not match the required size (invalidates data pointers), cannot be NULL
arma::Mat<dtype> *V_im (output)
Imaginary part of the interpolated e-theta (vertical) field component, Size [n_out, n_ang]
will be resized if it does not match the required size (invalidates data pointers), cannot be NULL
arma::Mat<dtype> *H_re (output)
Real part of the interpolated e-phi (horizontal) field component, Size [n_out, n_ang]
will be resized if it does not match the required size (invalidates data pointers), cannot be NULL
arma::Mat<dtype> *H_im (output)
Imaginary part of the interpolated e-phi (horizontal) field component, Size [n_out, n_ang]
will be resized if it does not match the required size (invalidates data pointers), cannot be NULL
arma::uvec i_element = {} (optional input)
The element indices for which the interpolation should be done, optional argument,
values must be between 1 and n_elements. It is possible to duplicate elements, i.e. by passing
{1,1,2}. If this parameter is not provided (or an empty array is passed), i_element is initialized
to include all elements of the array antenna. In this case, n_out = n_elements,
Length: n_out or empty {}
const arma::Cube<dtype> *orientation = nullptr (optional input)
This (optional) 3-element vector allows for setting orientation of the array antenna or
of individual elements using Euler angles (bank, tilt, heading); values must be given in [rad];
By default, the orientation is {0,0,0}, i.e. the broadside of the antenna points at the horizon
towards the East. Sizes: nullptr (use default), [3, 1] (set orientation for entire array),
[3, n_out] (set orientation for individual elements), or [3, 1, n_ang] (set orientation for
individual angles) or [3, n_out, n_ang] (set orientation for individual elements and angles)
const arma::Mat<dtype> *element_pos_i = nullptr (optional input)
Positions of the array antenna elements in local cartesian coordinates (using units
of [m]). If this parameter is not given, the element positions from the arrayant object are used.
Sizes: nullptr (use arrayant.element_pos), [3, n_out] (set alternative positions)
arma::Mat<dtype> *dist = nullptr (optional output)
The effective distances between the antenna elements when seen from the direction
of the incident path. The distance is calculated by an projection of the array positions on the normal
plane of the incident path. This is needed for calculating the phase of the antenna response.
Size: nullptr (do not calculate this) or [n_out, n_ang] (argument be resized if it does not already
match this size)
arma::Mat<dtype> *azimuth_loc = nullptr (optional output)
The azimuth angles in [rad] for the local antenna coordinate system, i.e., after
applying the orientation. If no orientation vector is given, these angles are identical to the input
azimuth angles. Size: nullptr or [n_out, n_ang]
arma::Mat<dtype> *elevation_loc = nullptr (optional output)
The elevation angles in [rad] for the local antenna coordinate system, i.e., after
applying the orientation. If no orientation vector is given, these angles are identical to the input
elevation angles. Size: nullptr or [n_out, n_ang]
arma::Mat<dtype> *gamma = nullptr (optional output)
Polarization rotation angles in [rad]. Size: nullptr or [n_out, n_ang]
-
Example:
double pi = arma::datum::pi;
// Directional antenna, pointing east
auto ant = quadriga_lib::generate_arrayant_custom<double>(90.0, 90.0, 0.0);
arma::mat azimuth = {0.0, 0.5 * pi, -0.5 * pi, pi}; // Azimuth angles: East, North, South, West
arma::mat elevation(1, azimuth.n_elem); // Initialize to 0
arma::mat V_re, V_im, H_re, H_im; // Output variables (uninitialized)
ant.interpolate(&azimuth, &elevation, &V_re, &V_im, &H_re, &H_im);
V_re.print();
.is_valid - Validate integrity of antenna array object
-
Description:
- Checks the integrity of an antenna array (
arrayant) object.
- Returns an empty string if the antenna object is valid.
- Provides an error message describing any issue if the object is invalid.
- A quick integrity check can be performed for efficiency.
- Member function of arrayant
- Allowed datatypes (
dtype): float or double
-
Declaration:
std::string quadriga_lib::arrayant<dtype>::is_valid(bool quick_check = true) const;
-
Arguments:
bool quick_check = true (optional input)
If set to true (default), performs a quick validation check. Setting it to false performs a more thorough validation.
-
Returns:
std::string
Returns an empty string ("") if the antenna object passes the integrity check; otherwise, returns an error message detailing the issue.
-
Example:
quadriga_lib::arrayant<double> ant;
std::string result = ant.is_valid();
if(result.empty()) {
std::cout << "Antenna array is valid." << std::endl;
} else {
std::cout << "Error: " << result << std::endl;
}
.qdant_write - Write array antenna object and layout to QDANT file
-
Description:
- This function writes array antenna patterns and their layout into the QuaDRiGa array antenna exchange
format (QDANT), an XML-based file format
- Multiple array antennas can be stored in the same file using the
id parameter.
- If writing to an exisiting file without specifying an
id, the data gests appended at the end.
The output id_in_file identifies the location inside the file.
- An optional storage
layout can be provided to organize data inside the file.
- Member function of arrayant
- Allowed datatypes (
dtype): float or double
-
Declaration:
unsigned quadriga_lib::arrayant<dtype>::qdant_write(
std::string fn,
unsigned id = 0,
arma::u32_mat layout = {}) const;
-
Arguments:
std::string fn (input)
Filename of the QDANT file to write the antenna pattern data. Cannot be empty.
unsigned id = 0 (optional input)
ID of the antenna to write into the file. If not provided or set to 0, the antenna pattern is appended with a new ID equal to the maximum existing ID in the file plus one.
arma::u32_mat layout = {} (optional input)
Layout specifying the organization of multiple antenna elements inside the file. This matrix must only contain element IDs present within the file. Default: empty matrix {}.
-
Returns:
unsigned
Returns the ID assigned to the antenna pattern within the file after writing.
-
Example:
quadriga_lib::arrayant<double> ant = quadriga_lib::generate_arrayant_custom<double>(90.0, 90.0, 0.0);
unsigned ant_id = ant.qdant_write("antenna_data.qdant");
-
See also:
.remove_zeros - Remove zeros from antenna pattern data
-
Description:
- This function removes zeros from the antenna pattern data, altering its size accordingly.
- If called without an argument, the function modifies the antenna array properties in place.
- Member function of arrayant
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant<dtype>::remove_zeros(arrayant<dtype> *output = nullptr);
-
Arguments:
arrayant<dtype> *output = nullptr (optional output)
Pointer to an antenna array object where the modified pattern data is should be written to. If set
to nullptr (default), the modifications are applied directly to the calling antenna object.
-
Example:
auto ant = quadriga_lib::generate_arrayant_custom<double>(90.0, 90.0, 0.0);
ant.remove_zeros(); // Modifies ant in-place
.rotate_pattern - Adjust orientation of antenna patterns
-
Description:
- Adjusts the orientation of antenna radiation patterns by performing precise rotations around the
three principal axes (x, y, z) of the local Cartesian coordinate system (Euler rotations)
- Transforms both uniformly and non-uniformly sampled antenna patterns, useful for precise adjustments
in antennas like parabolic antennas with small apertures.
- Member function of arrayant
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant<dtype>::rotate_pattern(
dtype x_deg = 0.0,
dtype y_deg = 0.0,
dtype z_deg = 0.0,
unsigned usage = 0,
unsigned element = -1,
arrayant<dtype> *output = nullptr);
-
Arguments:
dtype x_deg = 0.0 (optional input)
Rotation angle around the x-axis (bank angle), specified in degrees. Default: 0.0
dtype y_deg = 0.0 (optional input)
Rotation angle around the y-axis (tilt angle), specified in degrees. Default: 0.0
dtype z_deg = 0.0 (optional input)
Rotation angle around the z-axis (heading angle), specified in degrees. Default: 0.0
unsigned usage = 0 (optional input)
Rotation usage model, specifying which components to rotate:
0 |
Rotate both pattern and polarization (with grid adjustment for non-uniform grids), |
1 |
Rotate only pattern (with grid adjustment for non-uniform grids), |
2 |
Rotate only polarization, |
3 |
Rotate both pattern and polarization without adjusting the grid, |
4 |
Rotate only pattern without adjusting the grid |
unsigned element = -1 (optional input)
Index of the antenna element (0-based) to rotate. Default (-1) applies rotation to all elements.
arrayant<dtype> output = nullptr (optional output)
Pointer to an antenna array object to store the modified pattern data. If nullptr (default), modifications are applied directly to the calling antenna object.
-
Usage Modes:
| Mode |
Pattern |
Polarization |
Grid Adjustment |
| 0 |
Yes |
Yes |
Yes |
| 1 |
Yes |
No |
Yes |
| 2 |
No |
Yes |
No |
| 3 |
Yes |
Yes |
No |
| 4 |
Yes |
No |
No |
- Modes 0 and 1 adjust the angular sampling grid when the input grid is non-uniform (e.g. for parabolic
antennas with small apertures). This ensures the rotated pattern is properly sampled.
- Modes 3 and 4 skip grid adjustment, which is faster and preserves the original grid structure. This
is appropriate for uniformly sampled patterns such as loudspeaker models generated by
generate_speaker.
- Mode 4 is particularly useful for rotating loudspeaker directivity patterns where the acoustic
pressure field is scalar (stored only in
e_theta_re) and polarization rotation has no physical meaning.
-
Example:
auto ant = quadriga_lib::generate_arrayant_custom<double>(90.0, 90.0, 0.0);
ant.rotate_pattern(0.0, 0.0, 45.0);
.set_size - Change size of antenna array object
-
Description:
- Changes the size of an antenna array (
arrayant) without explicitly preserving existing data.
- Resets
element_pos to zero and sets coupling_re and coupling_im to identity matrices.
- Other properties may contain undefined or garbage data after resizing
- Size update is performed only if the existing size differs from the specified new size
- Function returns an error if the antenna object is marked as read-only
- Member function of arrayant
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant<dtype>::set_size(
arma::uword n_elevation,
arma::uword n_azimuth,
arma::uword n_elements,
arma::uword n_ports);
-
Arguments:
arma::uword n_elevation (input)
Number of elevation angles to resize to.
arma::uword n_azimuth (input)
Number of azimuth angles to resize to.
arma::uword n_elements (input)
Number of antenna elements in the array after resizing.
arma::uword n_ports (input)
Number of ports (after coupling of elements) in the resized antenna array.
-
Example:
quadriga_lib::arrayant<double> ant;
ant.set_size(180, 360, 4, 2);
Array antenna functions
arrayant_concat_multi - Concatenate two multi-frequency arrayant vectors into a single multi-element model
-
Description:
- Combines two multi-frequency
arrayant vectors into a single vector representing a multi-driver
loudspeaker (or any multi-element antenna array).
- Both input vectors must have the same number of frequency entries, identical angular grids (azimuth and elevation),
and matching center frequencies at each index.
- For each frequency entry, the function performs:
- Pattern concatenation: The 3D pattern cubes (
e_theta_re, e_theta_im, e_phi_re, e_phi_im)
are joined along the element (slice) dimension, producing n_elem1 + n_elem2 elements.
- Element position concatenation: The
element_pos matrices are horizontally concatenated.
If either input has empty positions, zeros are substituted.
- Block-diagonal coupling assembly: The coupling matrices are assembled in block-diagonal form,
preserving independent port sets from each input. If vec1 has
p1 ports and vec2 has p2 ports, the
output has p1 + p2 ports with no cross-coupling between the two driver groups.
- Both inputs are validated with
is_valid_multi before processing, and each output entry is
validated with validate before returning.
- The output inherits the name, azimuth grid, elevation grid, and center frequency from the first
input vector.
- Allowed datatypes (
dtype): float or double
-
Declaration:
std::vector<arrayant<dtype>> quadriga_lib::arrayant_concat_multi(
const std::vector<arrayant<dtype>> &arrayant_vec1,
const std::vector<arrayant<dtype>> &arrayant_vec2)
-
Arguments:
const std::vector<arrayant<dtype>> &arrayant_vec1 (input)
First arrayant vector. All entries must be valid and mutually consistent (same grids, element counts, coupling shape).
const std::vector<arrayant<dtype>> &arrayant_vec2 (input)
Second arrayant vector. Must have the same number of entries as arrayant_vec1, with matching angular grids and center frequencies.
-
Returns:
std::vector<arrayant<dtype>>
Combined arrayant vector with n_elem1 + n_elem2 elements and n_ports1 + n_ports2 ports per frequency entry. The coupling matrix has block-diagonal structure.
-
Coupling Matrix Structure:
For a woofer with 1 element / 1 port and a tweeter with 1 element / 1 port, the combined coupling matrix is:
Port 1 (woofer) Port 2 (tweeter)
Elem 1 (woofer) 1.0 0.0
Elem 2 (tweeter) 0.0 1.0
For more complex arrays (e.g. a line array with 4 elements / 2 ports combined with a tweeter of 1 element / 1 port):
P1 P2 P3
E1 c11 c12 0
E2 c21 c22 0
E3 c31 c32 0
E4 c41 c42 0
E5 0 0 1
-
Example:
// Build a 2-way loudspeaker: woofer + tweeter at matching frequencies
arma::vec freqs = {100.0, 200.0, 500.0, 1000.0, 2000.0, 5000.0, 10000.0};
// Generate woofer: 6.5" piston, sealed box, 50-3000 Hz passband
auto woofer = quadriga_lib::generate_speaker<double>(
"piston", 0.083, 50.0, 3000.0, 12.0, 24.0, 87.0, "hemisphere",
0.0, 0.0, 0.0, 0.20, 0.30, freqs, 10.0);
// Generate tweeter: 1" dome, sealed box, 1500-20000 Hz passband
auto tweeter = quadriga_lib::generate_speaker<double>(
"piston", 0.013, 1500.0, 20000.0, 24.0, 12.0, 90.0, "hemisphere",
0.0, 0.0, 0.0, 0.20, 0.30, freqs, 10.0);
// Position the drivers: woofer centered, tweeter 12 cm above
arma::mat woofer_pos = arma::mat({0.0, 0.0, 0.0}).t();
arma::mat tweeter_pos = arma::mat({0.0, 0.0, 0.12}).t();
quadriga_lib::arrayant_set_element_pos_multi(woofer, woofer_pos);
quadriga_lib::arrayant_set_element_pos_multi(tweeter, tweeter_pos);
// Combine into a single 2-way speaker model
auto speaker_2way = quadriga_lib::arrayant_concat_multi(woofer, tweeter);
// Result: 2 elements, 2 ports per frequency entry
// Port 1 drives the woofer, Port 2 drives the tweeter
// Crossover behavior emerges from overlapping bandpass responses
// Write to file
quadriga_lib::qdant_write_multi("speaker_2way.qdant", speaker_2way);
-
See also:
arrayant_copy_element_multi - Copy antenna elements across all entries in a multi-frequency arrayant vector
-
Description:
- Copies the pattern data, element position, and coupling weights from a source element to one or more
destination elements, applied identically to every frequency entry in the vector.
- Calls the
arrayant::copy_element member function for each entry in the vector. This member function handles
all internal details including pattern data copying, element position copying, and coupling matrix management.
- If any destination index exceeds the current number of elements, the arrayant is automatically enlarged.
New elements receive an identity coupling entry (one additional port per added element), consistent with
the behavior of the underlying
arrayant::copy_element member function.
- Source and destination indices are 0-based.
- Two overloads are provided: one accepting a vector of destination indices (
arma::uvec) for copying
to multiple targets, and one accepting a single destination index (arma::uword).
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant_copy_element_multi(
std::vector<arrayant<dtype>> &arrayant_vec,
arma::uword source,
arma::uvec destination)
void quadriga_lib::arrayant_copy_element_multi(
std::vector<arrayant<dtype>> &arrayant_vec,
arma::uword source,
arma::uword destination)
-
Arguments:
std::vector<arrayant<dtype>> &arrayant_vec (input/output)
Vector of arrayant objects to update. Modified in-place. Must not be empty. All entries must be valid arrayant objects.
arma::uword source (input)
0-based index of the source element to copy from. Must be within the current element count.
arma::uvec destination (input)
0-based indices of the destination elements to copy to. If any index exceeds the current element count, the arrayant is enlarged accordingly. Must not be empty.
arma::uword destination (input)
Single 0-based destination element index (convenience overload).
-
Returns:
-
Example:
// Build a 4-element line array from a single driver template
arma::vec freqs = {500.0, 1000.0, 2000.0, 5000.0};
auto driver = quadriga_lib::generate_speaker<double>(
"piston", 0.05, 80.0, 12000.0, 12.0, 12.0, 85.0, "hemisphere",
0.0, 0.0, 0.0, 0.15, 0.25, freqs, 10.0);
// Replicate element 0 to create elements 1, 2, 3
arma::uvec dest = {1, 2, 3};
quadriga_lib::arrayant_copy_element_multi(driver, 0, dest);
// Now set the individual positions (vertical line array, 15 cm spacing)
arma::mat positions(3, 4, arma::fill::zeros);
positions(2, 0) = -0.225; // z = -22.5 cm
positions(2, 1) = -0.075; // z = -7.5 cm
positions(2, 2) = 0.075; // z = +7.5 cm
positions(2, 3) = 0.225; // z = +22.5 cm
quadriga_lib::arrayant_set_element_pos_multi(driver, positions);
// Result: 4 identical elements, 4 independent ports, per frequency entry
-
See also:
arrayant_interpolate_multi - Interpolate multi-frequency array antenna patterns at arbitrary frequencies
-
Description:
- Computes interpolated complex-valued antenna field patterns at arbitrary spatial angles and
frequencies from a multi-frequency
arrayant vector (e.g. a loudspeaker model generated by
generate_speaker).
- For each requested frequency, the function identifies the two arrayant entries with the closest
center frequencies that bracket the target frequency, performs spatial pattern interpolation on
both using
qd_arrayant_interpolate, and then blends the two results in the frequency dimension.
- Frequency interpolation uses spherical interpolation (SLERP) of the complex field values with
automatic fallback to linear interpolation when the phase difference between the two bracketing
values exceeds a threshold. This is the same mixed-mode algorithm used by
qd_arrayant_interpolate
for spatial grid interpolation, ensuring smooth amplitude and phase transitions.
- If the requested frequency is below the lowest or above the highest
center_frequency in the
vector, the nearest entry is used without extrapolation (clamping). This is intentional: a
subwoofer model queried at 12 kHz returns its 12 kHz response if available, or its highest
frequency sample otherwise, reflecting the physical roll-off already encoded in the pattern data.
- The function calls
qd_arrayant_interpolate directly (bypassing the member function wrapper) for
maximum performance. This avoids redundant validation on each call since the arrayant vector is
validated once upfront (when validate_input is true).
- A simple caching mechanism avoids redundant spatial interpolation when consecutive frequency
requests share the same bracketing entry.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant_interpolate_multi(
const std::vector<arrayant<dtype>> &arrayant_vec,
const arma::Mat<dtype> *azimuth,
const arma::Mat<dtype> *elevation,
const arma::Col<dtype> *frequency,
arma::Cube<dtype> *V_re,
arma::Cube<dtype> *V_im,
arma::Cube<dtype> *H_re,
arma::Cube<dtype> *H_im,
arma::uvec i_element = {},
const arma::Cube<dtype> *orientation = nullptr,
const arma::Mat<dtype> *element_pos_i = nullptr,
bool validate_input = true)
-
Arguments:
const std::vector<arrayant<dtype>> &arrayant_vec (input)
Multi-frequency arrayant vector. Each entry must have a valid center_frequency. Entries need not
be sorted by frequency (sorting is handled internally). All entries must share the same angular
grids and element structure.
const arma::Mat<dtype> *azimuth (input)
Azimuth angles in [rad] for spatial interpolation. Cannot be NULL.
Size [1, n_ang] (same angles for all elements) or [n_out, n_ang] (per-element angles).
const arma::Mat<dtype> *elevation (input)
Elevation angles in [rad] for spatial interpolation. Cannot be NULL.
Size must match azimuth.
const arma::Col<dtype> *frequency (input)
Target frequencies in [Hz] at which to evaluate the pattern. Cannot be NULL or empty.
Size [n_freq]. Values need not be sorted.
arma::Cube<dtype> *V_re (output)
Real part of the interpolated e-theta (vertical) field component.
Size [n_out, n_ang, n_freq]. Resized automatically if needed. Cannot be NULL.
arma::Cube<dtype> *V_im (output)
Imaginary part of the interpolated e-theta (vertical) field component.
Size [n_out, n_ang, n_freq]. Cannot be NULL.
arma::Cube<dtype> *H_re (output)
Real part of the interpolated e-phi (horizontal) field component.
Size [n_out, n_ang, n_freq]. Cannot be NULL.
arma::Cube<dtype> *H_im (output)
Imaginary part of the interpolated e-phi (horizontal) field component.
Size [n_out, n_ang, n_freq]. Cannot be NULL.
arma::uvec i_element = {} (optional input)
0-based element indices to interpolate. If empty, all elements are included (n_out = n_elements).
const arma::Cube<dtype> *orientation = nullptr (optional input)
Array antenna orientation (bank, tilt, heading) in [rad]. Applied identically at all frequencies.
Sizes: nullptr, [3,1,1], [3,n_out,1], [3,1,n_ang], or [3,n_out,n_ang].
const arma::Mat<dtype> *element_pos_i = nullptr (optional input)
Alternative element positions in [m], Size [3, n_out]. If nullptr, positions from the first
arrayant entry are used (positions are identical across all frequency entries by construction).
bool validate_input = true (optional input)
If true, the arrayant vector is validated with arrayant_is_valid_multi before processing. Set
to false to skip validation in performance-critical loops where the vector is known to be valid.
-
Performance Notes:
- The function caches spatial interpolation results to avoid redundant
qd_arrayant_interpolate calls
when consecutive frequency requests share the same bracketing entry. For best cache utilization,
provide the frequency vector sorted in ascending or descending order.
- For single-frequency queries that exactly match a
center_frequency, no frequency interpolation is
performed (direct copy from the spatial interpolation result).
- For maximum throughput in RT simulations, set
validate_input = false after initial validation.
-
Example:
// Build a 2-way speaker model
arma::vec freqs_model = {100.0, 200.0, 500.0, 1000.0, 2000.0, 5000.0, 10000.0};
auto woofer = quadriga_lib::generate_speaker<double>("piston", 0.083, 50.0, 3000.0,
12.0, 24.0, 87.0, "hemisphere", 0.0, 0.0, 0.0, 0.20, 0.30, freqs_model, 10.0);
auto tweeter = quadriga_lib::generate_speaker<double>("piston", 0.013, 1500.0, 20000.0,
24.0, 12.0, 90.0, "hemisphere", 0.0, 0.0, 0.0, 0.20, 0.30, freqs_model, 10.0);
auto speaker = quadriga_lib::arrayant_concat_multi(woofer, tweeter);
// Query at 4 specific directions, 3 arbitrary frequencies
arma::mat azimuth = {0.0, 1.5708, -1.5708, 3.14159};
arma::mat elevation(1, 4, arma::fill::zeros);
arma::vec query_freqs = {250.0, 1500.0, 8000.0};
arma::cube V_re, V_im, H_re, H_im;
quadriga_lib::arrayant_interpolate_multi(speaker, &azimuth, &elevation, &query_freqs,
&V_re, &V_im, &H_re, &H_im);
// V_re has size [2, 4, 3]: 2 elements × 4 angles × 3 frequencies
// V_re.slice(0) = response at 250 Hz (interpolated between 200 and 500 Hz entries)
// V_re.slice(1) = response at 1500 Hz (interpolated between 1000 and 2000 Hz entries)
// V_re.slice(2) = response at 8000 Hz (interpolated between 5000 and 10000 Hz entries)
-
See also:
arrayant_is_valid_multi - Validate a vector of array antenna objects for multi-frequency consistency
-
Description:
- Validates that a vector of
arrayant objects forms a consistent multi-frequency model
- Each individual entry is validated using its
is_valid member function. The quick_check parameter
is passed through to control whether pointer-based fast validation or full deep validation is performed per entry.
- Beyond individual validity, the function checks that all entries in the vector are structurally compatible by verifying:
- Azimuth and elevation grids have identical sizes and values across all entries.
- The number of antenna elements is the same for all entries.
- Element positions are identical across all entries (physical antenna/driver layout does not change with frequency).
- Coupling matrices have the same shape (same number of elements and ports) across all entries.
- The function does not check pattern data values, center frequencies, or coupling matrix values, as these are expected to vary with frequency.
- Validation stops at the first error found and returns a descriptive message indicating which entry and what property failed.
- Allowed datatypes (
dtype): float or double
-
Declaration:
std::string quadriga_lib::arrayant_is_valid_multi(
const std::vector<arrayant<dtype>> &arrayant_vec,
bool quick_check = true)
-
Arguments:
const std::vector<arrayant<dtype>> &arrayant_vec (input)
Vector of arrayant objects to validate. Must not be empty.
bool quick_check = true (optional input)
If true, individual entry validation uses the fast pointer-based check (skips deep validation when check_ptr matches). If false, performs a full deep validation of every entry. Default: true.
-
Returns:
std::string
Empty string if the vector is valid and all entries are consistent. Otherwise, a descriptive error message indicating which entry (by index) and what property caused the validation failure. For example: "Entry 3: Azimuth grid values do not match entry 0.".
-
Example:
// Generate a speaker model and validate
auto spk = quadriga_lib::generate_speaker<double>("piston");
std::string err = quadriga_lib::is_valid_multi(spk, false);
if (!err.empty())
std::cerr << "Validation error: " << err << std::endl;
// Detect tampered element positions
spk[2].element_pos(0, 0) = 999.0;
err = quadriga_lib::is_valid_multi(spk, false);
// Returns: "Entry 2: Element positions do not match entry 0."
-
See also:
arrayant_rotate_pattern_multi - Rotate antenna patterns across all entries in a multi-frequency arrayant vector
-
Description:
- Applies Euler rotations to every frequency entry in a multi-frequency
arrayant vector,
modifying the patterns in-place.
- Grid adjustment is always disabled because multi-frequency loudspeaker models use uniform angular
grids that must remain consistent across all frequency entries. Non-uniform grid resampling would break this consistency.
- If
i_element is empty (default), all elements are rotated. If i_element is provided, only the specified
elements (0-based indices) are rotated, leaving others unchanged. This is useful for rotating individual drivers in a multi-driver speaker model.
- For acoustic speaker models where the pressure field is scalar (stored only in
e_theta_re), usage mode 1
(pattern only) is recommended to avoid spurious polarization effects.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant_rotate_pattern_multi(
std::vector<arrayant<dtype>> &arrayant_vec,
dtype x_deg = 0.0,
dtype y_deg = 0.0,
dtype z_deg = 0.0,
unsigned usage = 0,
arma::uvec i_element = arma::uvec())
-
Arguments:
std::vector<arrayant<dtype>> &arrayant_vec (input/output)
Vector of arrayant objects to update. Modified in-place. Must not be empty.
dtype x_deg = 0.0 (optional input)
Rotation angle around the x-axis (bank angle) in degrees. Default: 0.0.
dtype y_deg = 0.0 (optional input)
Rotation angle around the y-axis (tilt angle) in degrees. Default: 0.0.
dtype z_deg = 0.0 (optional input)
Rotation angle around the z-axis (heading angle) in degrees. Default: 0.0.
unsigned usage = 0 (optional input)
Rotation mode: 0 = rotate both pattern and polarization, 1 = rotate only pattern, 2 = rotate only polarization. Grid adjustment is always disabled.
arma::uvec i_element = arma::uvec() (optional input)
0-based indices of the elements to rotate. If empty, all elements are rotated. If provided, only the specified elements are affected.
-
Usage Mode Mapping:
| Input Mode |
Behavior |
Internal rotate_pattern Mode |
| 0 |
Pattern + polarization |
3 (no grid adjustment) |
| 1 |
Pattern only |
4 (no grid adjustment) |
| 2 |
Polarization only |
2 (no grid adjustment) |
-
Example:
// Generate a 2-way speaker model
arma::vec freqs = {100.0, 500.0, 1000.0, 5000.0, 10000.0};
auto woofer = quadriga_lib::generate_speaker<double>(
"piston", 0.083, 50.0, 3000.0, 12.0, 24.0, 87.0, "hemisphere",
0.0, 0.0, 0.0, 0.20, 0.30, freqs, 10.0);
auto tweeter = quadriga_lib::generate_speaker<double>(
"piston", 0.013, 1500.0, 20000.0, 24.0, 12.0, 90.0, "hemisphere",
0.0, 0.0, 0.0, 0.20, 0.30, freqs, 10.0);
auto speaker = quadriga_lib::arrayant_concat_multi(woofer, tweeter);
// Rotate entire speaker 30 degrees around the z-axis (heading)
quadriga_lib::arrayant_rotate_pattern_multi(speaker, 0.0, 0.0, 30.0, 1);
// Tilt only the tweeter (element 1) upward by 10 degrees
arma::uvec tweeter_idx = {1};
quadriga_lib::arrayant_rotate_pattern_multi(speaker, 0.0, 10.0, 0.0, 1, tweeter_idx);
-
See also:
arrayant_set_element_pos_multi - Set element positions for all entries in a multi-frequency arrayant vector
-
Description:
- Updates the
element_pos field of every arrayant in a multi-frequency vector in-place.
- If
i_element is empty (default), all element positions are replaced. In this case, element_pos
must have exactly n_elements columns matching the number of elements in the arrayant.
- If
i_element is provided, only the specified elements (0-based indices) are updated. The number
of columns in element_pos must match the number of indices in i_element.
- All entries in the vector must have the same number of elements. If any entry has a different
element count, an exception is thrown.
- If an entry's
element_pos is empty (uninitialized), it is initialized to zeros before
applying the update.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::arrayant_set_element_pos_multi(
std::vector<arrayant<dtype>> &arrayant_vec,
const arma::Mat<dtype> &element_pos,
arma::uvec i_element = arma::uvec())
-
Arguments:
std::vector<arrayant<dtype>> &arrayant_vec (input/output)
Vector of arrayant objects to update. Modified in-place. Must not be empty.
const arma::Mat<dtype> &element_pos (input)
New element positions with size [3, n_update], where each column contains the (x, y, z) coordinates of one element in meters.
arma::uvec i_element = arma::uvec() (optional input)
0-based indices of the elements to update. If empty, all elements are updated and element_pos must have n_elements columns. If provided, element_pos must have the same number of columns as i_element has entries.
-
Example:
// Generate a two-driver speaker (woofer + tweeter) at multiple frequencies
arma::vec freqs = {100.0, 500.0, 1000.0, 5000.0};
auto woofer = quadriga_lib::generate_speaker<double>("piston", 0.08, 50.0, 2000.0);
auto tweeter = quadriga_lib::generate_speaker<double>("horn", 0.025, 1500.0, 20000.0);
// Set the woofer position: centered, 10 cm below the tweeter
arma::mat pos = arma::mat({0.0, 0.0, -0.10}).t();
quadriga_lib::arrayant_set_element_pos_multi(woofer, pos);
// Or update a specific element in a multi-element arrayant
arma::mat tweeter_pos = arma::mat({0.0, 0.0, 0.10}).t();
arma::uvec idx = {0};
quadriga_lib::arrayant_set_element_pos_multi(tweeter, tweeter_pos, idx);
-
See also:
generate_arrayant_3gpp - Generate 3GPP-NR compliant antenna model
-
Description:
- Generates an antenna array based on the 3GPP-NR channel model specification.
- Supports vertical and horizontal stacking of elements and panels with a range of polarization configurations.
- Optionally, a custom per-element pattern can be provided (element positions, coupling, and center frequency are ignored).
- Allowed datatypes (
dtype): float or double
-
Declaration:
arrayant<dtype> quadriga_lib::generate_arrayant_3GPP(
arma::uword M = 1, arma::uword N = 1, dtype center_freq = 299792458.0,
unsigned pol = 1, dtype tilt = 0.0, dtype spacing = 0.5, arma::uword Mg = 1,
arma::uword Ng = 1, dtype dgv = 0.5, dtype dgh = 0.5,
const arrayant<dtype> *pattern = nullptr, dtype res = 1.0)
-
Arguments:
arma::uword M = 1 (optional input)
Number of vertical elements in the array. Default: 1
arma::uword N = 1 (optional input)
Number of horizontal elements in the array. Default: 1
dtype center_freq = 299792458.0 (optional input)
Center frequency of the antenna array in Hz. Default: 299792458.0
unsigned pol = 1 (optional input)
Polarization configuration:
pol = 1 |
vertical polarization (default value) |
pol = 2 |
H/V polarized elements, results in 2NM elements |
pol = 3 |
+/-45° polarized elements, results in 2NM elements |
pol = 4 |
vertical polarization, combines elements in vertical direction, results in N elements |
pol = 5 |
H/V polarization, combines elements in vertical direction, results in 2N elements |
pol = 6 |
+/-45° polarization, combines elements in vertical direction, results in 2N elements |
dtype tilt = 0.0 (optional input)
Electrical downtilt angle in degrees. Used for pol values 4, 5, and 6. Default: 0.0
dtype spacing = 0.5 (optional input)
Spacing between elements in wavelengths (λ). Default: 0.5
arma::uword Mg = 1 (optional input)
Number of vertically stacked panels (columns). Default: 1
arma::uword Ng = 1 (optional input)
Number of horizontally stacked panels (rows). Default: 1
dtype dgv = 0.5 (optional input)
Panel spacing in vertical direction in wavelengths (λ). Default: 0.5
dtype dgh = 0.5 (optional input)
Panel spacing in horizontal direction in wavelengths (λ). Default: 0.5
const arrayant<dtype> pattern = nullptr (optional input)
Optional pointer to a custom per-element antenna pattern. If provided, it overrides default generation. Only the shape of the pattern is used; element positions, coupling, and frequency are ignored.
dtype res = 1.0 (optional input)
Pattern resolution in degrees. Ignored if a custom pattern is provided. Default: 1.0
-
Returns:
arrayant<dtype>
Generated antenna array object according to 3GPP-NR specifications.
-
Example:
// Generate 3GPP antenna array with 4x4 elements, H/V polarization
auto ant = quadriga_lib::generate_arrayant_3GPP<double>(4, 4, 3e9, 2);
generate_arrayant_custom - Generate antenna with custom 3dB beamwidth
-
Description:
- Generates an antenna array object with a custom-defined 3dB beamwidth (FWHM) in azimuth and elevation.
- Allows control over the rear-side attenuation using a front-to-back gain ratio.
- Supports specification of the pattern sampling resolution.
- Allowed datatypes (
dtype): float or double
-
Declaration:
arrayant<dtype> quadriga_lib::generate_arrayant_custom(dtype az_3dB = 90.0,
dtype el_3db = 90.0, dtype rear_gain_lin = 0.0, dtype res = 1.0)
-
Arguments:
dtype az_3dB = 90.0 (optional input)
3dB beamwidth in azimuth, specified in degrees. Default: 90.0
dtype el_3db = 90.0 (optional input)
3dB beamwidth in elevation, specified in degrees. Default: 90.0
dtype rear_gain_lin = 0.0 (optional input)
Front-to-back gain ratio as a linear value. Default: 0.0 (no rear gain)
dtype res = 1.0 (optional input)
Resolution of the antenna pattern sampling grid, specified in degrees. Default: 1.0
-
Returns:
arrayant<dtype>
Antenna array object with the specified 3dB beamwidth and rear gain.
-
Example:
auto ant = quadriga_lib::generate_arrayant_custom<double>(60.0, 45.0, 0.01, 1.0);
generate_arrayant_dipole - Generate short dipole with vertical polarization
generate_arrayant_half_wave_dipole - Generate half-wave dipole with vertical polarization
generate_arrayant_multibeam - Generate a planar multi-element antenna with support for multiple beam directions.
-
Description:
This function generates a planar array antenna with M rows and N columns.
It allows customization of the per-element radiation pattern, polarization, and spacing.
Multiple beam directions can be specified via azimuth and elevation angles.
Beamforming uses maximum-ratio transmission (MRT), which is optimal for a single
beam and approximate when multiple beams are specified.
Supported data types for dtype: float or double.
-
Declaration:
arrayant<dtype> quadriga_lib::generate_arrayant_multibeam(
arma::uword M = 1,
arma::uword N = 1,
arma::Col<dtype> az = {0.0},
arma::Col<dtype> el = {0.0},
arma::Col<dtype> weight = {1.0},
dtype center_freq = 299792458.0,
unsigned pol = 1,
dtype spacing = 0.5,
dtype az_3dB = 120.0,
dtype el_3dB = 120.0,
dtype rear_gain_lin = 0.0,
dtype res = 1.0,
bool separate_beams = false,
bool apply_weights = false );
-
Arguments:
arma::uword M = 1 (optional input)
Number of vertical (rows) elements in the array. Default: 1
arma::uword N = 1 (optional input)
Number of horizontal (columns) elements in the array. Default: 1
arma::Col<dtype> az = {0.0} (optional input)
Azimuth beam angles (degrees). Vector of length n_beams. Default: {0.0}
arma::Col<dtype> el = {0.0} (optional input)
Elevation beam angles (degrees). Vector of length n_beams. Default: {0.0}
arma::Col<dtype> weight = {1.0} (optional input)
Scaling factors for each beam. The vector must have the same length as az and el.
Values are normalized so that their sum equals 1. Can be used to prioritize beams.
Default: {1.0}
dtype center_freq = 299792458.0 (optional input)
Center frequency of the antenna array in Hz. Default: 299792458.0
unsigned pol = 1 (optional input)
Polarization configuration:
pol = 1 |
vertical polarization (default value) |
pol = 2 |
H/V polarized elements, results in 2NM elements |
pol = 3 |
+/-45° polarized elements, results in 2NM elements |
dtype spacing = 0.5 (optional input)
Spacing between elements in wavelengths (λ). Default: 0.5
dtype az_3dB = 120.0 (optional input)
3dB beamwidth in azimuth, specified in degrees. Default: 120.0
dtype el_3db = 120.0 (optional input)
3dB beamwidth in elevation, specified in degrees. Default: 120.0
dtype rear_gain_lin = 0.0 (optional input)
Front-to-back gain ratio as a linear value. Default: 0.0 (no rear gain)
dtype res = 1.0 (optional input)
Resolution of the antenna pattern sampling grid, specified in degrees. Default: 1.0
bool separate_beams = false (optional input)
If set to true, create a separate beam for each angle pair (ignores weights)
bool apply_weights = false (optional input)
Switch to apply the beam-forming weights
-
Returns:
arrayant<dtype>
Array antenna object containing the generated multibeam pattern.
-
Example:
// Generate a pattern with 2 weighted beams
double freq = 3.75e9;
arma::vec az = {20.0, 0.0};
arma::vec el = {-7.0, 30.0};
arma::vec weight = {2.0, 1.0};
auto ant = quadriga_lib::generate_arrayant_multibeam<double>(6, 6, az, el, weight, freq);
generate_arrayant_omni - Generate isotropic radiator with vertical polarization
generate_arrayant_ula - Generate an unified linear array
-
Description:
- Supports horizontal stacking of elements
- Default pattern: Isotropic omnidirectional radiator, v-polarized
- Optionally, a custom per-element pattern can be provided (element positions, coupling, and center frequency are ignored).
- Allowed datatypes (
dtype): float or double
-
Declaration:
arrayant<dtype> quadriga_lib::generate_arrayant_ula(
arma::uword N = 1, dtype center_freq = 299792458.0, dtype spacing = 0.5,
const arrayant<dtype> *pattern = nullptr, dtype res = 1.0)
-
Arguments:
arma::uword N = 1 (optional input)
Number of horizontal elements in the array. Default: 1
dtype center_freq = 299792458.0 (optional input)
Center frequency of the antenna array in Hz. Default: 299792458.0
dtype spacing = 0.5 (optional input)
Spacing between elements in wavelengths (λ). Default: 0.5
const arrayant<dtype> pattern = nullptr (optional input)
Optional pointer to a custom per-element antenna pattern. If provided, it overrides default generation. Only the shape of the pattern is used; element positions, coupling, and frequency are ignored.
dtype res = 1.0 (optional input)
Pattern resolution in degrees. Ignored if a custom pattern is provided. Default: 1.0
-
Returns:
arrayant<dtype>
Generated antenna array object according to 3GPP-NR specifications.
generate_arrayant_xpol - Generate cross-polarized isotropic radiator
generate_speaker - Generate a parametric loudspeaker directivity model
-
Description:
- Generates frequency-dependent loudspeaker radiation patterns by combining a driver directivity model
with an enclosure radiation modifier and a Butterworth-style bandpass frequency response.
- Returns a vector of
arrayant objects, one per frequency sample, where each contains the complex-valued
directivity balloon at that frequency stored in e_theta_re. The center_frequency field of each
arrayant is set to the corresponding frequency in Hz.
- Multi-driver speakers (e.g. two-way systems) are modelled by generating each driver separately and
combining them via
append and element_pos. Crossover behavior emerges naturally from overlapping
bandpass responses.
- The frequency response follows a Butterworth-style bandpass filter:
H(f) = 1/sqrt(1 + (f_low/f)^(2n_low)) * 1/sqrt(1 + (f/f_high)^(2n_high)), where the filter order
n = slope_dB_per_octave / 6. The response is -3 dB at the specified cutoff frequencies.
- Sensitivity is applied as a linear amplitude scaling relative to 85 dB SPL:
sens_lin = 10^((sensitivity - 85) / 20).
- If no frequency vector is provided, third-octave band center frequencies are auto-generated
covering the range from one band below the lower cutoff to one band above the upper cutoff,
clipped to 20-20000 Hz.
- The speed of sound is assumed to be 344 m/s.
- Allowed datatypes (
dtype): float or double
Three driver directivity models are supported:
- Piston: Circular piston in a baffle, using the classical formula
D(theta) = 2J1(kasin(theta)) / (kasin(theta)), where k = 2pi*f/c is the wavenumber and a is
the driver radius. Rotationally symmetric around the forward axis (+x). Transitions from omnidirectional
at low ka to progressively narrower beaming at high ka. Uses C++17
std::cyl_bessel_j for the Bessel f
unction J1.
- Horn: Separable cosine-power directivity with frequency-dependent pattern control. Horizontal
and vertical coverage are modelled independently as cos^n(angle), where the exponent n is derived
from the coverage angle: n = log(0.5) / log(cos(theta_3dB/2)). Below the horn control frequency,
the pattern blends toward omnidirectional using a second-order transition:
blend = (f/f_ctrl)^2 / (1 + (f/f_ctrl)^2). Horn parameters can be auto-derived from the mouth radius.
- Omni: Frequency-independent omnidirectional pattern (D = 1 at all angles). Suitable for
subwoofers at frequencies where the driver is small relative to the wavelength.
Four enclosure radiation types modify the base driver pattern:
- Monopole: No modification (4*pi radiation, R = 1 everywhere). Appropriate for subwoofers in free space.
- Hemisphere: Sealed box on a finite baffle with frequency-dependent transition from omnidirectional
to forward-weighted radiation. Uses a baffle step model: alpha = (f/f_baffle)^2 / (1 + (f/f_baffle)^2),
where f_baffle = c / (pi sqrt(WH)) is derived from the baffle dimensions. The radiation modifier is
R = (1-alpha) + alpha 0.5 (1 + cos(theta_off)). At low frequencies the pattern is nearly
omnidirectional; at high frequencies it approaches a cardioid-like front weighting with approximately
6 dB front-to-back ratio.
- Dipole: Open baffle / planar speaker with figure-8 pattern. R = abs(cos(theta_off)) with a 180-degree
phase inversion (negative sign in
e_theta_re) in the rear hemisphere. Nulls occur at 90 degrees off-axis.
- Cardioid: Monopole + dipole combination, R = 0.5 * (1 + cos(theta_off)). Null at the rear,
half-amplitude at the sides. No phase inversion.
-
Declaration:
std::vector<arrayant<dtype>> quadriga_lib::generate_speaker(
std::string driver_type = "piston",
dtype radius = 0.05,
dtype lower_cutoff = 80.0,
dtype upper_cutoff = 12000.0,
dtype lower_rolloff_slope = 12.0,
dtype upper_rolloff_slope = 12.0,
dtype sensitivity = 85.0,
std::string radiation_type = "hemisphere",
dtype hor_coverage = 0.0,
dtype ver_coverage = 0.0,
dtype horn_control_freq = 0.0,
dtype baffle_width = 0.15,
dtype baffle_height = 0.25,
arma::Col<dtype> frequencies = arma::Col<dtype>(),
dtype angular_resolution = 5.0)
-
Arguments:
std::string driver_type = "piston" (optional input)
Driver directivity model. Supported values: "piston" (cone/dome via Bessel function), "horn"
(cosine-power with frequency-dependent pattern control), "omni" (omnidirectional subwoofer).
dtype radius = 0.05 (optional input)
Effective radiating radius in meters. For "piston": the cone or dome radius. For "horn": the horn
mouth radius, from which the pattern control frequency is derived if not specified. Default: 0.05 m
(approximately a 4-inch driver).
dtype lower_cutoff = 80.0 (optional input)
Lower -3 dB frequency of the bandpass response in Hz. Default: 80 Hz.
dtype upper_cutoff = 12000.0 (optional input)
Upper -3 dB frequency of the bandpass response in Hz. Default: 12000 Hz.
dtype lower_rolloff_slope = 12.0 (optional input)
Low-frequency rolloff slope in dB per octave. Corresponds to a Butterworth filter of order
n = slope/6 (e.g. 12 dB/oct = 2nd order). Default: 12.0.
dtype upper_rolloff_slope = 12.0 (optional input)
High-frequency rolloff slope in dB per octave. Default: 12.0.
dtype sensitivity = 85.0 (optional input)
On-axis sensitivity in dB SPL at 1W/1m. Scales the pattern amplitude linearly relative to 85 dB as
reference (i.e. 85 dB gives unity amplitude). Default: 85.0.
std::string radiation_type = "hemisphere" (optional input)
Enclosure radiation model. Supported values: "monopole" (4*pi, no modification), "hemisphere"
(sealed box with frequency-dependent baffle step), "dipole" (open baffle, figure-8), "cardioid"
(monopole + dipole combination).
dtype hor_coverage = 0.0 (optional input)
Horizontal coverage angle in degrees. Horn driver only. A value of 0 auto-defaults to 90 degrees. Default: 0.0.
dtype ver_coverage = 0.0 (optional input)
Vertical coverage angle in degrees. Horn driver only. A value of 0 auto-defaults to 60 degrees. Default: 0.0.
dtype horn_control_freq = 0.0 (optional input)
Frequency in Hz above which the horn maintains pattern control. A value of 0 auto-derives from the
mouth radius as f_ctrl = c / (2piradius). Default: 0.0.
dtype baffle_width = 0.15 (optional input)
Enclosure baffle width in meters. Used by the "hemisphere" radiation model to compute the baffle
step frequency. Default: 0.15 m.
dtype baffle_height = 0.25 (optional input)
Enclosure baffle height in meters. Used by the "hemisphere" radiation model to compute the baffle
step frequency. Default: 0.25 m.
arma::Col<dtype> frequencies = arma::Col<dtype>() (optional input)
Frequency sample points in Hz. If empty, third-octave band center frequencies are auto-generated
overing one band beyond each cutoff frequency, clipped to the 20-20000 Hz audible range.
dtype angular_resolution = 5.0 (optional input)
Resolution of the azimuth and elevation sampling grids in degrees. Default: 5.0.
-
Returns:
std::vector<arrayant<dtype>>
Vector of arrayant objects, one per frequency sample. Each arrayant has one element with the directivity
pattern stored in e_theta_re (real-valued except for dipole rear hemisphere where negative values encode
180-degree phase inversion). The center_frequency field is set to the corresponding frequency in Hz.
-
Example:
// Generate a default cheap piston speaker at specific frequencies
arma::vec freqs = {100.0, 500.0, 1000.0, 5000.0, 10000.0};
auto spk = quadriga_lib::generate_speaker<double>("piston", 0.05, 80.0, 12000.0,
12.0, 12.0, 85.0, "hemisphere", 0.0, 0.0, 0.0, 0.15, 0.25, freqs, 5.0);
// Generate a horn driver with auto-derived parameters at third-octave bands
auto horn = quadriga_lib::generate_speaker<double>("horn");
// Generate a subwoofer
arma::vec sub_freqs = {30.0, 50.0, 80.0, 120.0, 200.0};
auto sub = quadriga_lib::generate_speaker<double>("omni", 0.13, 30.0, 200.0,
12.0, 24.0, 92.0, "monopole", 0.0, 0.0, 0.0, 0.15, 0.25, sub_freqs, 10.0);
qdant_read - Reads array antenna data from QDANT files
-
Description:
- Reads antenna pattern data and layout from a QuaDRiGa array antenna exchange format (QDANT) file,
which stores antenna pattern data in XML format.
- Returns an antenna array (
arrayant) object constructed from the specified data in the QDANT file.
- Allowed datatypes (
dtype): float or double
-
Declaration:
arrayant<dtype> quadriga_lib::qdant_read(std::string fn, unsigned id = 1, arma::u32_mat *layout = nullptr);
-
Arguments:
std::string fn (input)
Filename of the QDANT file from which antenna pattern data will be read. Cannot be empty.
unsigned id = 1 (optional input)
ID of the antenna within the QDANT file to read. Default is 1.
arma::u32_mat layout = nullptr (optional output)
Pointer to a matrix that will store the layout information of multiple antenna elements from the file. The layout contains element IDs present in the QDANT file.
-
Returns:
arrayant<dtype>
Antenna array object containing data from the specified antenna ID within the QDANT file.
-
Example:
arma::u32_mat layout;
auto ant = quadriga_lib::qdant_read<double>("antenna_data.qdant", 2, &layout);
-
See also:
qdant_read_multi - Read all array antenna objects from a QDANT file
-
Description:
- Reads all
arrayant objects stored in a QuaDRiGa array antenna exchange format (QDANT) file and
returns them as a vector.
- The file layout is obtained by probing the file with ID 1. Even if ID 1 does not exist as an entry,
the internal reader still returns the layout describing the file contents. This layout is then used
to determine which IDs to read.
- Unique non-zero IDs are extracted from the layout in the order of their first appearance (scanning rows
within each column). Each unique ID is read exactly once, regardless of how many times it appears in the layout.
- All read arrayant objects are validated. An exception is thrown if any entry cannot be read or fails validation.
- This function is the counterpart to
qdant_write_multi and the primary mechanism for loading frequency-dependent
models. The frequency corresponding to each entry is stored in the center_frequency field of the returned arrayant objects.
- Allowed datatypes (
dtype): float or double
-
Declaration:
std::vector<arrayant<dtype>> quadriga_lib::qdant_read_multi(
const std::string &fn,
arma::u32_mat *layout = nullptr)
-
Arguments:
const std::string &fn (input)
Filename of the QDANT file to read. Cannot be empty.
arma::u32_mat *layout = nullptr (optional output)
Pointer to a matrix that will receive the layout information from the file. The layout describes the organization of array antenna entries inside the file, with non-zero values corresponding to entry IDs. If nullptr, the layout is not returned.
-
Returns:
std::vector<arrayant<dtype>>
Vector of arrayant objects, one per unique ID found in the layout. Entries are ordered by their first appearance in the layout. Each arrayant contains the full pattern data including center_frequency.
-
Example:
// Read all entries from a file
auto spk = quadriga_lib::qdant_read_multi<double>("speaker.qdant");
// Read with layout information
arma::u32_mat layout;
auto spk2 = quadriga_lib::qdant_read_multi<double>("speaker.qdant", &layout);
std::cout << "File contains " << spk2.size() << " entries" << std::endl;
std::cout << "Layout: " << std::endl;
layout.print();
-
See also:
qdant_write_multi - Write a vector of array antenna objects to a single QDANT file
-
Description:
- Writes multiple
arrayant objects to a single QuaDRiGa array antenna exchange format (QDANT) file,
one per sequential ID.
- Each entry in the input vector is assigned a 1-based ID (1, 2, ..., n_entries) and written to the file
using the
arrayant::qdant_write member function.
- A layout matrix of size
[n_entries, 1] is created automatically with entries 1, 2, ..., n_entries
to describe the organization inside the file.
- If the output file already exists, it is deleted before writing to ensure a clean file.
- All arrayant objects in the vector are validated before writing. An exception is thrown if any entry is invalid.
- This function is the primary mechanism for storing frequency-dependent models, where each arrayant
in the vector represents the directivity pattern at a specific frequency. The corresponding frequency
is stored in the
center_frequency field of each arrayant.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::qdant_write_multi(
const std::string &fn,
const std::vector<arrayant<dtype>> &arrayant_vec)
-
Arguments:
const std::string &fn (input)
Filename of the QDANT file to write. If the file already exists, it is overwritten. Cannot be empty.
const std::vector<arrayant<dtype>> &arrayant_vec (input)
Vector of arrayant objects to write. Each entry is stored with a sequential 1-based ID. The vector must not be empty and all entries must be valid arrayant objects.
-
Returns:
-
Example:
// Generate a loudspeaker model and write to file
arma::vec freqs = {100.0, 500.0, 1000.0, 5000.0, 10000.0};
auto spk = quadriga_lib::generate_speaker<double>("piston", 0.05, 80.0, 12000.0,
12.0, 12.0, 85.0, "hemisphere", 0.0, 0.0, 0.0, 0.15, 0.25, freqs, 5.0);
quadriga_lib::qdant_write_multi("speaker.qdant", spk);
// Read back a specific frequency entry (e.g. the 3rd one, ID = 3)
auto ant = quadriga_lib::qdant_read<double>("speaker.qdant", 3);
-
See also:
Channel class
channel - Class for storing and managing MIMO channel data and associated metadata
-
Description:
- A channel object represents MIMO path-level channel data between antenna arrays over multiple time snapshots.
- Each snapshot may have a different number of propagation paths.
- Contains structured fields for positions, delays, gains, angles, coefficients, and more.
- Supports optional metadata via
par_names and par_data.
- Allowed datatypes (
dtype): float and double
-
Attributes:
std::string name |
Name of the channel object |
arma::Col<dtype> center_frequency |
Center frequency in [Hz], size [1] or [n_snap] or [] |
arma::Mat<dtype> tx_pos |
Transmitter positions, size [3, n_snap] or [3, 1] |
arma::Mat<dtype> rx_pos |
Receiver positions, size [3, n_snap] or [3, 1] |
arma::Mat<dtype> tx_orientation |
Transmitter orientation (Euler), size [3, n_snap], [3, 1], or [] |
arma::Mat<dtype> rx_orientation |
Receiver orientation (Euler), size [3, n_snap], [3, 1], or [] |
std::vector<arma::Cube<dtype>> coeff_re |
Channel coefficients, real part, size [n_rx, n_tx, n_path] per snapshot |
std::vector<arma::Cube<dtype>> coeff_im |
Channel coefficients, imaginary part, same size as coeff_re |
std::vector<arma::Cube<dtype>> delay |
Path delays [s], size [n_rx, n_tx, n_path] or [1,1,n_path] per snapshot |
std::vector<arma::Col<dtype>> path_gain |
Path gains (pre-pattern), length [n_path] per snapshot |
std::vector<arma::Col<dtype>> path_length |
Path lengths TX→RX [m], length [n_path] per snapshot |
std::vector<arma::Mat<dtype>> path_polarization |
Polarization matrix, size [8, n_path] per snapshot |
std::vector<arma::Mat<dtype>> path_angles |
Departure/arrival angles, size [n_path, 4], columns: AOD, EOD, AOA, EOA |
std::vector<arma::Mat<dtype>> path_fbs_pos |
First-bounce scatterer positions, size [3, n_path] |
std::vector<arma::Mat<dtype>> path_lbs_pos |
Last-bounce scatterer positions, size [3, n_path] |
std::vector<arma::Col<unsigned>> no_interact |
Number of interactions per path, length [n_path] per snapshot |
std::vector<arma::Mat<dtype>> interact_coord |
Coordinates of all interactions, size [3, sum(no_interact)] per snapshot |
std::vector<std::string> par_names |
Names of unstructured parameters |
std::vector<std::any> par_data |
Unstructured metadata fields (e.g., string, scalar, matrix) |
int initial_position |
Index of reference snapshot (0-based) |
-
Simple member functions:
.n_snap() |
Returns the number of snapshots |
.n_rx() |
Returns the number of receive antennas (0 if coeffs not present) |
.n_tx() |
Returns the number of transmit antennas (0 if coeffs not present) |
.n_path() |
Returns the number of paths per snapshot as vector |
.empty() |
Returns true if the object has no channel data |
.is_valid() |
Returns an empty string if object is valid, else an error message |
-
Complex member functions:
.add_paths - Append new propagation paths to an existing channel snapshot
-
Description:
- Adds path-level channel data to a specific snapshot (
i_snap) in a channel object.
- All fields provided must be consistent in length (
n_path_add) and structure.
- The number of antennas must match existing entries for the snapshot.
- Existing fields in the channel object must also be provided to this method if relevant.
- Member function of channel
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::channel<dtype>::add_paths(
arma::uword i_snap,
const arma::Cube<dtype> *coeff_re_add = nullptr,
const arma::Cube<dtype> *coeff_im_add = nullptr,
const arma::Cube<dtype> *delay_add = nullptr,
const arma::u32_vec *no_interact_add = nullptr,
const arma::Mat<dtype> *interact_coord_add = nullptr,
const arma::Col<dtype> *path_gain_add = nullptr,
const arma::Col<dtype> *path_length_add = nullptr,
const arma::Mat<dtype> *path_polarization_add = nullptr,
const arma::Mat<dtype> *path_angles_add = nullptr,
const arma::Mat<dtype> *path_fbs_pos_add = nullptr,
const arma::Mat<dtype> *path_lbs_pos_add = nullptr);
-
Arguments:
arma::uword i_snap (input)
Index of the snapshot to which the new paths should be added (0-based).
const arma::Cube<dtype> *coeff_re_add (optional input)
Real part of channel coefficients. Size: [n_rx, n_tx, n_path_add].
const arma::Cube<dtype> *coeff_im_add (optional input)
Imaginary part of channel coefficients. Size: [n_rx, n_tx, n_path_add].
const arma::Cube<dtype> *delay_add (optional input)
Propagation delay in seconds. Size: [n_rx, n_tx, n_path_add] or [1, 1, n_path_add].
const arma::u32_vec *no_interact_add (optional input)
Number of interaction points per path. Length: [n_path_add].
const arma::Mat<dtype> *interact_coord_add (optional input)
Coordinates of interaction points. Size: [3, sum(no_interact)].
const arma::Col<dtype> *path_gain_add (optional input)
Path gains before antenna effects. Length: [n_path_add].
const arma::Col<dtype> *path_length_add (optional input)
Path lengths from TX to RX phase center. Length: [n_path_add].
const arma::Mat<dtype> *path_polarization_add (optional input)
Polarization transfer matrices (interleaved). Size: [8, n_path_add].
const arma::Mat<dtype> *path_angles_add (optional input)
Departure and arrival angles. Size: [n_path_add, 4], format: {AOD, EOD, AOA, EOA}.
const arma::Mat<dtype> *path_fbs_pos_add (optional input)
First-bounce scatterer positions. Size: [3, n_path_add].
const arma::Mat<dtype> *path_lbs_pos_add (optional input)
Last-bounce scatterer positions. Size: [3, n_path_add].
-
Notes:
- Any provided input must match the snapshot structure and existing fields of the
channel object.
- This method does not update
tx_pos, rx_pos, or orientation fields.
.calc_effective_path_gain - Calculate the effective path gain for each snapshot (in linear scale)
.write_paths_to_obj_file - Export propagation paths to a Wavefront OBJ file
-
Description:
- Writes ray-traced propagation paths to a
.obj file for 3D visualization (e.g., in Blender).
- Each path is represented as a tube, optionally colored by path gain using a selected colormap.
- The function supports filtering by path gain, maximum number of paths, and snapshot index.
- Tube radius and detail can be customized.
- Member function of channel
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::channel<dtype>::write_paths_to_obj_file(
std::string fn,
arma::uword max_no_paths = 0,
dtype gain_max = -60.0,
dtype gain_min = -140.0,
std::string colormap = "jet",
arma::uvec i_snap = {},
dtype radius_max = 0.05,
dtype radius_min = 0.01,
arma::uword n_edges = 5) const;
-
Arguments:
std::string fn (input)
Path to the output .obj file.
arma::uword max_no_paths = 0 (optional input)
Maximum number of paths to be visualized. A value of 0 includes all available paths above gain_min.
Default: 0.
dtype gain_max = -60.0 (optional input)
Maximum path gain (in dB) used for color-coding. Paths with higher gain are clipped. Default: -60.0.
dtype gain_min = -140.0 (optional input)
Minimum path gain (in dB) for color-coding and optional path filtering. Default: -140.0.
std::string colormap = "jet" (optional input)
Name of the colormap to be used for path coloring.
Supported maps: jet, parula, winter, hot, turbo, copper, spring, cool, gray,
autumn, summer. Default: "jet".
arma::uvec i_snap = {} (optional input)
Indices of the snapshots to be included (0-based). Empty vector exports all snapshots. Default: {}.
dtype radius_max = 0.05 (optional input)
Maximum radius (in meters) of the visualized tube geometry. Default: 0.05.
dtype radius_min = 0.01 (optional input)
Minimum radius (in meters) of the visualized tube geometry. Default: 0.01.
arma::uword n_edges = 5 (optional input)
Number of vertices used to create each tube cross-section. Must be ≥ 3. Default: 5.
-
See also:
Channel functions
any_type_id - Get type ID and raw access from a 'std::any' object
baseband_freq_response - Compute the baseband frequency response of a MIMO channel
-
Description:
- Computes the frequency-domain response of a time-domain MIMO channel using a discrete Fourier transform (DFT).
- Input consists of real and imaginary channel coefficients and corresponding delays for each MIMO sub-link.
- Outputs the complex channel response matrix
H at given sub-carrier frequency positions.
- Internally uses AVX2 instructions for fast parallel computation of 8 carriers at once.
- Can be efficiently called in a loop (e.g., over snapshots) and parallelized with OpenMP.
- Internal arithmetic is performed in single precision for speed.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::baseband_freq_response(
const arma::Cube<dtype> *coeff_re,
const arma::Cube<dtype> *coeff_im,
const arma::Cube<dtype> *delay,
const arma::Col<dtype> *pilot_grid,
const double bandwidth,
arma::Cube<dtype> *hmat_re,
arma::Cube<dtype> *hmat_im);
-
Arguments:
const arma::Cube<dtype> *coeff_re (input)
Real part of channel coefficients in time domain, Size [n_rx, n_tx, n_path].
const arma::Cube<dtype> *coeff_im (input)
Imaginary part of channel coefficients in time domain, Size [n_rx, n_tx, n_path].
const arma::Cube<dtype> *delay (input)
Path delays in seconds. Size [n_rx, n_tx, n_path] or broadcastable shape [1, 1, n_path].
const arma::Col<dtype> *pilot_grid (input)
Normalized sub-carrier positions relative to bandwidth. Range: 0.0 (center freq) to 1.0 (center + bandwidth). Length: n_carriers.
const double bandwidth (input)
Total baseband bandwidth in Hz (defines absolute frequency spacing of the pilot grid).
arma::Cube<dtype> *hmat_re (output)
Output: Real part of the frequency-domain channel matrix, Size [n_rx, n_tx, n_carriers].
arma::Cube<dtype> *hmat_im (output)
Output: Imaginary part of the frequency-domain channel matrix, Size [n_rx, n_tx, n_carriers].
baseband_freq_response_multi - Compute the wideband frequency response of a MIMO channel with frequency-dependent coefficients
-
Description:
- Computes the frequency-domain channel transfer function H(f) from multi-frequency channel coefficients
obtained by sampling the channel at a coarse set of carrier frequencies (e.g., every 500 MHz).
- Designed for ultra-wideband (UWB) channels where antenna patterns, path gains, and polarization transfer
matrices vary across a large bandwidth (e.g., 500 MHz to 20 GHz).
- For each multipath component (MPC), the complex coefficient envelope is interpolated from the coarse
input frequency grid
freq_in to the dense output frequency grid freq_out using SLERP (Spherical
Linear Interpolation): magnitude is interpolated linearly, phase is unwrapped and interpolated linearly
along the shortest arc.
- The delay-induced phase rotation
exp(-j 2 pi freq_out delay) is applied per output carrier.
Since the output frequencies are absolute (not baseband offsets), the phase computation uses double
precision internally to avoid loss of accuracy at high carrier frequencies.
- Delay phase removal (
remove_delay_phase = true, default): Channel generation functions such as
get_channels_multifreq bake the delay-induced phase exp(-j 2 pi freq_in[f] delay) into
the output coefficients at each input frequency. This means the coefficient phase rotates rapidly across
the input frequency grid (e.g., 50 full cycles per 500 MHz step for a 100 ns excess delay), which
prevents meaningful SLERP interpolation. When remove_delay_phase is enabled, this function undoes the
baked-in delay phase by multiplying each coefficient by exp(+j 2 pi freq_in[f] delay) before
extracting the slowly-varying envelope for interpolation. The full delay phase at the output frequency
is then re-applied analytically. This must be enabled when consuming output from get_channels_multifreq
or get_channels_spherical. Set to false only if the input coefficients are already pure envelopes
without baked-in delay phase (e.g., from manual construction or external sources).
- Delays are taken from
delay[0] only, since path geometry (and thus propagation delay) is
frequency-independent. All entries in the delay vector should contain identical values.
- The delay cube supports broadcastable shape
[1, 1, n_path] (planar wave, same delay for all antenna
pairs) or full shape [n_rx, n_tx, n_path] (spherical wave, per-antenna-pair delays).
- For output frequencies outside the range of
freq_in, the nearest endpoint coefficient is used
(constant extrapolation in magnitude/phase domain).
- The Nyquist condition for the coarse sampling grid applies to the coefficient envelope only (magnitude
and phase of antenna patterns, path gains, and Jones matrix elements). The fast-rotating delay phase is
handled analytically and does not require dense sampling. Adjacent envelope phase differences should stay
well below pi to avoid interpolation artifacts.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::baseband_freq_response_multi(
const std::vector<arma::Cube<dtype>> &coeff_re,
const std::vector<arma::Cube<dtype>> &coeff_im,
const std::vector<arma::Cube<dtype>> &delay,
const arma::Col<dtype> &freq_in,
const arma::Col<dtype> &freq_out,
arma::Cube<dtype> *hmat_re = nullptr,
arma::Cube<dtype> *hmat_im = nullptr,
arma::Cube<std::complex<dtype>> *hmat = nullptr,
bool remove_delay_phase = true);
-
Arguments:
const std::vector<arma::Cube<dtype>> &coeff_re (input)
Real part of channel coefficients at each input frequency. Vector of length n_freq_in, each cube
has shape [n_rx, n_tx, n_path].
const std::vector<arma::Cube<dtype>> &coeff_im (input)
Imaginary part of channel coefficients at each input frequency. Same structure as coeff_re.
const std::vector<arma::Cube<dtype>> &delay (input)
Path delays in seconds. Vector of length n_freq_in. Only delay[0] is used (delays are
frequency-independent). Shape [n_rx, n_tx, n_path] or [1, 1, n_path].
const arma::Col<dtype> &freq_in (input)
Input sample frequencies in Hz, sorted in ascending order. Length [n_freq_in].
const arma::Col<dtype> &freq_out (input)
Output carrier frequencies in Hz (absolute, not baseband offsets). Length [n_carrier].
arma::Cube<dtype> *hmat_re = nullptr (optional output)
Real part of the frequency-domain channel matrix. Size [n_rx, n_tx, n_carrier].
arma::Cube<dtype> *hmat_im = nullptr (optional output)
Imaginary part of the frequency-domain channel matrix. Size [n_rx, n_tx, n_carrier].
arma::Cube<std::complex<dtype>> *hmat = nullptr (optional output)
Complex-valued frequency-domain channel matrix. Size [n_rx, n_tx, n_carrier].
bool remove_delay_phase = true (optional input)
If true (default), the delay-induced phase exp(-j 2 pi freq_in[f] delay) that is baked
into the input coefficients by channel generation functions (e.g., get_channels_multifreq) is removed
before SLERP interpolation and re-applied at each output frequency. Must be enabled when the input
comes from get_channels_multifreq or get_channels_spherical. Set to false only when the input
coefficients contain pure slowly-varying envelopes without delay phase.
-
Example:
// Using output from get_channels_multifreq (delay phase is baked in)
std::vector<arma::Cube<double>> coeff_re, coeff_im, delays;
arma::Col<double> freq_in = {0.5e9, 1.0e9, 1.5e9, 2.0e9};
arma::Col<double> freq_out = arma::linspace<arma::Col<double>>(0.5e9, 2.0e9, 2048);
// ... call get_channels_multifreq to populate coeff_re, coeff_im, delays ...
arma::Cube<double> Hr, Hi;
quadriga_lib::baseband_freq_response_multi(coeff_re, coeff_im, delays,
freq_in, freq_out, &Hr, &Hi, nullptr, true); // remove_delay_phase = true (default)
-
See also:
baseband_freq_response_vec - Compute the baseband frequency response of multiple MIMO channels
-
Description:
- Computes the frequency-domain response of a batch of time-domain MIMO channels using a discrete Fourier transform (DFT).
- This function wraps
quadriga_lib::baseband_freq_response and applies it across multiple snapshots in parallel using OpenMP.
- Input consists of vectors of real/imaginary coefficients and delay Cubes for each snapshot.
- Output is a vector of frequency-domain channel matrices
H (one per snapshot).
- Can optionally compute a selected subset of snapshots using
i_snap.
- Internal arithmetic is performed in single precision for performance.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::baseband_freq_response_vec(
const std::vector<arma::Cube<dtype>> *coeff_re,
const std::vector<arma::Cube<dtype>> *coeff_im,
const std::vector<arma::Cube<dtype>> *delay,
const arma::Col<dtype> *pilot_grid,
const double bandwidth,
std::vector<arma::Cube<dtype>> *hmat_re,
std::vector<arma::Cube<dtype>> *hmat_im,
const arma::u32_vec *i_snap = nullptr);
-
Arguments:
const std::vector<arma::Cube<dtype>> *coeff_re (input)
Real part of channel coefficients, vector of length n_snap. Each cube has shape [n_rx, n_tx, n_path].
const std::vector<arma::Cube<dtype>> *coeff_im (input)
Imaginary part of channel coefficients, same structure as coeff_re.
const std::vector<arma::Cube<dtype>> *delay (input)
Path delays in seconds, same structure as coeff_re, shape can be broadcasted [1, 1, n_path].
const arma::Col<dtype> *pilot_grid (input)
Normalized sub-carrier positions relative to bandwidth. Range: 0.0 (center freq) to 1.0 (center + bandwidth). Length: n_carriers.
const double bandwidth (input)
Total baseband bandwidth in Hz, used to compute sub-carrier frequencies.
std::vector<arma::Cube<dtype>> *hmat_re (output)
Output: Real part of the frequency-domain channel matrices. Vector of length n_out. Each cube is [n_rx, n_tx, n_carriers].
std::vector<arma::Cube<dtype>> *hmat_im (output)
Output: Imaginary part of the frequency-domain channel matrices. Same structure as hmat_re.
const arma::u32_vec *i_snap = nullptr (optional input)
Optional subset of snapshot indices to process. If omitted, all n_snap snapshots are processed. Length: n_out.
-
See also:
get_hdf5_version - Get the version of the linked HDF5 library
hdf5_create - Create a new HDF5 channel file with a defined storage layout
-
Description:
- Quadriga-Lib offers an HDF5-based method for storing and managing channel data. A key feature of this
library is its ability to organize multiple channels within a single HDF5 file while enabling access
to individual data sets without the need to read the entire file.
- This function initializes a new HDF5 file for storing wireless channel data.
- Defines a multi-dimensional array layout for organizing channels (up to 4 dimensions).
- Typical usage: map base stations (BS), user equipment (UE), and frequencies to dimensions.
- The layout can later be reshaped using
hdf5_reshape_layout, provided the total number of entries remains constant.
- Each combination of indices corresponds to a storage slot that can hold a channel object.
-
Declaration:
void quadriga_lib::hdf5_create(
std::string fn,
unsigned nx = 65536,
unsigned ny = 1,
unsigned nz = 1,
unsigned nw = 1);
-
Arguments:
std::string fn (input)
Filename (including path) of the HDF5 file to be created. If the file exists, it will be overwritten.
unsigned nx = 65536 (input)
Number of entries in the x-dimension, e.g., for base stations (BSs). Default: 65536.
unsigned ny = 1 (input)
Number of entries in the y-dimension, e.g., for user equipment (UEs). Default: 1.
unsigned nz = 1 (input)
Number of entries in the z-dimension, e.g., for frequency points. Default: 1.
unsigned nw = 1 (input)
Number of entries in the w-dimension, e.g., for repetitions, scenarios, or configurations. Default: 1.
-
Example:
// Create a file with layout: [10 BSs, 4 UEs, 2 frequencies]
quadriga_lib::hdf5_create("channels.hdf5", 10, 4, 2);
hdf5_read_channel - Read a channel object from an HDF5 file
-
Description:
- Loads a
quadriga_lib::channel<dtype> object from the specified index in a previously created HDF5 file.
- If the selected index does not contain a valid channel, an empty channel object is returned (with
no_snapshots == 0).
- Allowed datatypes (
dtype): float or double
- All structured data (e.g., channel coefficients, delays, positions) is stored in single precision in the
file but will be converted to the appropriate precision (
float or double) depending on the provided template parameter.
- Unstructured or user-defined fields stored in
std::any containers are not converted and retain their original type
-
Declaration:
quadriga_lib::channel<dtype> quadriga_lib::hdf5_read_channel(
std::string fn,
unsigned ix = 0,
unsigned iy = 0,
unsigned iz = 0,
unsigned iw = 0);
-
Arguments:
std::string fn (input)
Filename of the HDF5 file containing the channel data.
unsigned ix = 0 (input)
x-Index in the file's 4D storage layout. Default: 0.
unsigned iy = 0 (input)
y-Index in the file's 4D storage layout. Default: 0.
unsigned iz = 0 (input)
z-Index in the file's 4D storage layout. Default: 0.
unsigned iw = 0 (input)
w-Index in the file's 4D storage layout. Default: 0.
-
Returns:
quadriga_lib::channel<dtype>
A channel object containing the channel data at the specified index. If no data is found, an empty channel object is returned.
hdf5_read_dset - Read an unstructured dataset from an HDF5 file
-
Description:
- Reads a user-defined, unstructured dataset stored in an HDF5 file at the specified index.
- Unstructured datasets are typically used to store additional parameters or metadata and are stored under a name prefix (e.g.
"par_") followed by the dataset name.
- Returns the dataset as a
std::any object, which can hold any supported type (e.g., scalar values, vectors, matrices, cubes).
- Use
quadriga_lib::any_type_id to determine the contained type and obtain a raw pointer for direct access.
- If the dataset does not exist at the specified index or name, an empty
std::any object is returned.
-
Declaration:
std::any quadriga_lib::hdf5_read_dset(
std::string fn,
std::string par_name,
unsigned ix = 0,
unsigned iy = 0,
unsigned iz = 0,
unsigned iw = 0,
std::string prefix = "par_");
-
Arguments:
std::string fn (input)
Filename of the HDF5 file containing the dataset.
std::string par_name (input)
Name of the dataset, without the prefix (e.g., "carrier_frequency").
unsigned ix = 0 (input)
x-Index in the HDF5 file’s 4D storage layout. Default: 0.
unsigned iy = 0 (input)
y-Index in the HDF5 file’s 4D storage layout. Default: 0.
unsigned iz = 0 (input)
z-Index in the HDF5 file’s 4D storage layout. Default: 0.
unsigned iw = 0 (input)
w-Index in the HDF5 file’s 4D storage layout. Default: 0.
std::string prefix = "par_" (input)
Optional dataset name prefix. Default is "par_".
-
Returns:
- A
std::any object containing the dataset. If the dataset is not present, the return value is an empty std::any.
hdf5_read_dset_names - Read names of unstructured datasets from an HDF5 file
-
Description:
- Retrieves the names of all unstructured datasets stored at a specified 4D index in an HDF5 file.
- Dataset names are identified by a common prefix (default:
"par_") and the actual parameter name follows the prefix.
- The returned names in
par_names exclude the prefix for convenience.
- Returns the number of datasets found at the given index.
-
Declaration:
arma::uword quadriga_lib::hdf5_read_dset_names(
std::string fn,
std::vector<std::string> *par_names,
unsigned ix = 0,
unsigned iy = 0,
unsigned iz = 0,
unsigned iw = 0,
std::string prefix = "par_");
-
Arguments:
std::string fn (input)
Filename of the HDF5 file containing the datasets.
std::vector<std::string> *par_names (output)
Pointer to a vector that will be filled with all dataset names found at the specified index (excluding the prefix).
unsigned ix = 0 (input)
x-Index in the HDF5 file’s 4D layout. Default: 0.
unsigned iy = 0 (input)
y-Index in the HDF5 file’s 4D layout. Default: 0.
unsigned iz = 0 (input)
z-Index in the HDF5 file’s 4D layout. Default: 0.
unsigned iw = 0 (input)
w-Index in the HDF5 file’s 4D layout. Default: 0.
std::string prefix = "par_" (input)
Optional prefix string used to identify unstructured datasets. Default: "par_".
-
Returns:
- The number of unstructured datasets found at the specified location with the given prefix.
hdf5_read_layout - Read the HDF5 channel storage layout
hdf5_reshape_layout - Reshape the storage layout of an HDF5 channel file
-
Description:
- Updates the multi-dimensional storage layout of an existing HDF5 file that contains channel data.
- The layout consists of up to four dimensions:
{nx, ny, nz, nw}.
- This is useful for reorganizing data after initial creation (e.g., grouping entries by BS/UE/frequency).
- The total number of entries must remain unchanged, i.e.,
nx × ny × nz × nw must equal the original layout.
- Throws an error if the reshaped layout violates this constraint.
-
Declaration:
void quadriga_lib::hdf5_reshape_layout(
std::string fn,
unsigned nx,
unsigned ny = 1,
unsigned nz = 1,
unsigned nw = 1);
-
Arguments:
std::string fn (input)
Filename of the HDF5 file to reshape.
unsigned nx (input)
Number of entries in the first dimension (e.g., base stations).
unsigned ny = 1 (input)
Number of entries in the second dimension (e.g., user equipment). Default: 1.
unsigned nz = 1 (input)
Number of entries in the third dimension (e.g., carrier frequency). Default: 1.
unsigned nw = 1 (input)
Number of entries in the fourth dimension. Default: 1.
hdf5_write - Write channel data to HDF5 file
-
Description:
- Quadriga-Lib provides an HDF5-based solution for storing and organizing channel data.
- This function rites a
channel object to a specified HDF5 file at the given 4D index location.
- If the file does not exist, a new file is created with default layout
(65535 × 1 × 1 × 1).
-
Declaration:
int quadriga_lib::hdf5_write(
const quadriga_lib::channel<dtype> *ch,
std::string fn,
unsigned ix = 0,
unsigned iy = 0,
unsigned iz = 0,
unsigned iw = 0,
bool assume_valid = false);
-
Arguments:
const channel<dtype> *ch (input)
Pointer to the channel object to be stored.
std::string fn (input)
Path to the HDF5 file.
unsigned ix = 0 (input)
Index in the x-dimension (e.g., Base Station ID). Default: 0.
unsigned iy = 0 (input)
Index in the y-dimension (e.g., User Equipment ID). Default: 0.
unsigned iz = 0 (input)
Index in the z-dimension (e.g., Frequency point). Default: 0.
unsigned iw = 0 (input)
Index in the w-dimension (e.g., Repetition/Scenario). Default: 0.
bool assume_valid = false (input)
If true, skips channel integrity validation before writing. Default: false.
-
Returns:
0 if a new dataset was created.
1 if an existing dataset was overwritten or extended.
-
Caveat:
- If the file exists already, the new data is added to the exisiting file
- If the index already contains data, it will be overwritten
- Use
assume_valid = true to skip internal validation (faster, but unsafe if data may be corrupted).
- Throws an error if the index was not reserved during
hdf5_create.
- All structured data is written in single precision (but can can be provided as float or double)
- Unstructured datatypes are maintained in the HDF file
- Supported unstructured types: string, double, float, (u)int32, (u)int64
- Supported unstructured size: up to 3 dimensions
- Storage order of the unstructured data is maintained
hdf5_write_dset - Write a single unstructured dataset to an HDF5 file
-
Description:
- Writes a single unstructured data field to an HDF5 file at the specified 4D index.
- Supported scalar types:
std::string, unsigned, int, long long, unsigned long long, float, double
- Supported Armadillo types:
arma::Col, arma::Row, arma::Mat, and arma::Cube with float, double, int, unsigned, sword, uword, unsigned long long
arma::Row vectors are converted to column vectors (arma::Col) before writing.
- The dataset name is prefixed with
"par_" (default) unless another prefix is specified.
- Throws an error for unsupported types.
- Dataset names may only include alphanumeric characters and underscores.
-
Declaration:
void quadriga_lib::hdf5_write_dset(
std::string fn,
std::string par_name,
const std::any \*par_data,
unsigned ix = 0,
unsigned iy = 0,
unsigned iz = 0,
unsigned iw = 0,
std::string prefix = "par_");
-
Arguments:
std::string fn (input)
Filename of the HDF5 file to which the dataset will be written.
std::string par_name (input)
Name of the parameter to store (without prefix). Must contain only letters, digits, and underscores.
const std::any *par_data (input)
Pointer to the data to be written. Type must be supported (see above).
unsigned ix = 0 (input)
x-Index in the HDF5 file’s 4D layout. Default: 0.
unsigned iy = 0 (input)
y-Index in the HDF5 file’s 4D layout. Default: 0.
unsigned iz = 0 (input)
z-Index in the HDF5 file’s 4D layout. Default: 0.
unsigned iw = 0 (input)
w-Index in the HDF5 file’s 4D layout. Default: 0.
std::string prefix = "par_" (input)
Optional prefix for the dataset name. Default: "par_".
qrt_file_parse - Read metadata from a QRT file
-
Description:
- Parses a QRT file and extracts metadata such as the number of snapshots, origins, destinations, and frequencies.
- All output arguments are optional; pass
nullptr to skip any value you don't need.
- Can also retrieve CIR offsets per destination, human-readable names for origins and destinations, and the file version.
-
Declaration:
void quadriga_lib::qrt_file_parse(
const std::string &fn,
arma::uword *no_cir = nullptr,
arma::uword *no_orig = nullptr,
arma::uword *no_dest = nullptr,
arma::uword *no_freq = nullptr,
arma::uvec *cir_offset = nullptr,
std::vector<std::string> *orig_names = nullptr,
std::vector<std::string> *dest_names = nullptr,
int *version = nullptr)
-
Arguments:
const std::string &fn (input)
Path to the QRT file.
arma::uword *no_cir = nullptr (optional output)
Number of channel snapshots per origin point.
arma::uword *no_orig = nullptr (optional output)
Number of origin points (TX).
arma::uword *no_dest = nullptr (optional output)
Number of destinations (RX).
arma::uword *no_freq = nullptr (optional output)
Number of frequency bands.
arma::uvec *cir_offset = nullptr (optional output)
CIR offset for each destination. Size [no_dest].
std::vector<std::string> *orig_names = nullptr (optional output)
Names of the origin points (TXs). Size [no_orig].
std::vector<std::string> *dest_names = nullptr (optional output)
Names of the destination points (RXs). Size [no_dest].
int *version = nullptr (optional output)
QRT file version number.
-
Example:
arma::uword no_cir, no_orig, no_dest, no_freq;
arma::uvec cir_offset;
std::vector<std::string> orig_names, dest_names;
int version;
quadriga_lib::qrt_file_parse("scene.qrt", &no_cir, &no_orig, &no_dest, &no_freq,
&cir_offset, &orig_names, &dest_names, &version);
qrt_file_read - Read ray-tracing data from a QRT file
-
Description:
- Reads channel impulse response (CIR) data from a QRT file for a specific snapshot and origin point.
- Supports both uplink and downlink directions by swapping TX/RX roles accordingly.
- All output arguments are optional; pass
nullptr to skip any value you don't need.
- The
normalize_M parameter controls how the polarization transfer matrix M and path gains are returned.
- For maximum performance in tight loops, pass a pre-opened
std::ifstream and a pre-populated
qrt_read_cache. With both, the per-call I/O is reduced to 2 seeks and 4 reads.
- Allowed datatypes (
dtype): float or double
-
Declaration:
template <typename dtype>
void quadriga_lib::qrt_file_read(
const std::string &fn,
arma::uword i_cir = 0,
arma::uword i_orig = 0,
bool downlink = true,
arma::Col<dtype> *center_frequency = nullptr,
arma::Col<dtype> *tx_pos = nullptr,
arma::Col<dtype> *tx_orientation = nullptr,
arma::Col<dtype> *rx_pos = nullptr,
arma::Col<dtype> *rx_orientation = nullptr,
arma::Mat<dtype> *fbs_pos = nullptr,
arma::Mat<dtype> *lbs_pos = nullptr,
arma::Mat<dtype> *path_gain = nullptr,
arma::Col<dtype> *path_length = nullptr,
arma::Cube<dtype> *M = nullptr,
arma::Col<dtype> *aod = nullptr,
arma::Col<dtype> *eod = nullptr,
arma::Col<dtype> *aoa = nullptr,
arma::Col<dtype> *eoa = nullptr,
std::vector<arma::Mat<dtype>> *path_coord = nullptr,
int normalize_M = 1,
arma::u32_vec *no_int = nullptr,
arma::fmat *coord = nullptr,
std::ifstream *file = nullptr,
const qrt_read_cache *cache = nullptr)
-
Arguments:
const std::string &fn (input)
Path to the QRT file. Ignored when both file and cache are provided.
arma::uword i_cir = 0 (input)
Snapshot index (0-based).
arma::uword i_orig = 0 (input)
Origin index, 0-based. For downlink, origin corresponds to the transmitter.
bool downlink = true (input)
If true, origin is TX and destination is RX (downlink). If false, roles are swapped (uplink).
arma::Col<dtype> *center_frequency = nullptr (optional output)
Center frequency in Hz. Size [n_freq].
arma::Col<dtype> *tx_pos = nullptr (optional output)
Transmitter position in Cartesian coordinates. Size [3].
arma::Col<dtype> *tx_orientation = nullptr (optional output)
Transmitter orientation (bank, tilt, heading) in radians. Size [3].
arma::Col<dtype> *rx_pos = nullptr (optional output)
Receiver position in Cartesian coordinates. Size [3].
arma::Col<dtype> *rx_orientation = nullptr (optional output)
Receiver orientation (bank, tilt, heading) in radians. Size [3].
arma::Mat<dtype> *fbs_pos = nullptr (optional output)
First-bounce scatterer positions. Size [3, n_path].
arma::Mat<dtype> *lbs_pos = nullptr (optional output)
Last-bounce scatterer positions. Size [3, n_path].
arma::Mat<dtype> *path_gain = nullptr (optional output)
Path gain on linear scale. Size [n_path, n_freq].
arma::Col<dtype> *path_length = nullptr (optional output)
Absolute path length from TX to RX phase center. Size [n_path].
arma::Cube<dtype> *M = nullptr (optional output)
Polarization transfer matrix. Size [8, n_path, n_freq] or [2, n_path, n_freq] for v6 files.
arma::Col<dtype> *aod = nullptr (optional output)
Departure azimuth angles in radians. Size [n_path].
arma::Col<dtype> *eod = nullptr (optional output)
Departure elevation angles in radians. Size [n_path].
arma::Col<dtype> *aoa = nullptr (optional output)
Arrival azimuth angles in radians. Size [n_path].
arma::Col<dtype> *eoa = nullptr (optional output)
Arrival elevation angles in radians. Size [n_path].
std::vector<arma::Mat<dtype>> *path_coord = nullptr (optional output)
Interaction coordinates per path. Vector of length n_path, each matrix of size [3, n_interact + 2].
int normalize_M = 1 (input)
Normalization option for the polarization transfer matrix.
| 0 |
M as stored in QRT file, path_gain is -FSPL |
| 1 |
M has sum-column power of 2, path_gain is -FSPL minus material losses |
arma::u32_vec *no_int = nullptr (optional output)
Number of mesh interactions per path. Size [n_path]. A value of 0 indicates a LOS path.
arma::fmat *coord = nullptr (optional output)
Interaction coordinates. Size [3, sum(no_int)].
std::ifstream *file = nullptr (optional input)
Optional pre-opened binary ifstream. If nullptr, the file is opened from fn and closed
on return. If provided, the stream is left open after return.
const qrt_read_cache *cache = nullptr (optional input)
Optional pre-parsed metadata cache (from qrt_read_cache_init). When provided together with
file, per-call I/O is reduced to 2 seeks and 4 reads.
-
Example:
arma::vec center_freq, tx_pos, rx_pos, path_length, aod, eod, aoa, eoa;
arma::mat fbs_pos, lbs_pos, path_gain;
arma::cube M;
// Single call (opens and closes file internally):
quadriga_lib::qrt_file_read<double>("scene.qrt", 0, 0, true,
¢er_freq, &tx_pos, nullptr, &rx_pos, nullptr,
&fbs_pos, &lbs_pos, &path_gain, &path_length, &M,
&aod, &eod, &aoa, &eoa, nullptr, 1);
// Maximum performance with cache and shared stream:
std::ifstream stream("scene.qrt", std::ios::in | std::ios::binary);
quadriga_lib::qrt_read_cache cache;
quadriga_lib::qrt_read_cache_init("scene.qrt", &cache, &stream);
for (arma::uword ic = 0; ic < cache.no_cir; ++ic)
for (arma::uword io = 0; io < cache.no_orig; ++io)
quadriga_lib::qrt_file_read<double>("", ic, io, true,
¢er_freq, &tx_pos, nullptr, &rx_pos, nullptr,
&fbs_pos, &lbs_pos, &path_gain, &path_length, &M,
&aod, &eod, &aoa, &eoa, nullptr, 1,
nullptr, nullptr, &stream, &cache);
qrt_read_cache_init - Initialize a QRT read cache for fast repeated access
-
Description:
- Reads all fixed metadata from a QRT file into a
qrt_read_cache struct.
- Pre-computes byte offsets so that subsequent
qrt_file_read calls only need to
perform 2 seeks and 4 reads (the per-CIR path data) instead of re-parsing the header.
- Populate the cache once, then pass it to
qrt_file_read together with a shared
std::ifstream for maximum performance in tight loops.
-
Declaration:
quadriga_lib::qrt_read_cache quadriga_lib::qrt_read_cache_init(
const std::string &fn,
std::ifstream *file = nullptr)
-
Arguments:
const std::string &fn (input)
Path to the QRT file.
std::ifstream *file = nullptr (optional input)
Optional pre-opened binary ifstream. If nullptr, the file is opened from fn
and closed on return. If provided, the stream is left open.
-
Returns:
qrt_read_cache cache (output)
Populated cache structure.
-
Example:
std::ifstream stream("scene.qrt", std::ios::in | std::ios::binary);
auto cache = quadriga_lib::qrt_read_cache_init("scene.qrt", &stream);
arma::vec center_freq, tx_pos, rx_pos;
arma::mat path_gain;
arma::cube M;
for (arma::uword ic = 0; ic < cache.no_cir; ++ic)
for (arma::uword io = 0; io < cache.no_orig; ++io)
quadriga_lib::qrt_file_read<double>("", ic, io, true,
¢er_freq, &tx_pos, nullptr, &rx_pos, nullptr,
nullptr, nullptr, &path_gain, nullptr, &M,
nullptr, nullptr, nullptr, nullptr, nullptr, 1,
nullptr, nullptr, &stream, &cache);
quantize_delays - Fixes the path delays to a grid of delay bins
-
Description:
- For channel emulation with finite delay resolution, path delays must be mapped to a fixed grid
of delay bins (taps). Rounding delays to the nearest tap causes discontinuities in the frequency
domain when a delay crosses a tap boundary (e.g. as a mobile terminal moves). This function
instead approximates each path delay using two adjacent taps with power-weighted coefficients,
producing smooth transitions.
- For a path at fractional offset δ between tap indices, two taps are created with complex
coefficients scaled by (1−δ)^α and δ^α, where α is the power
exponent. The default α=1.0 (linear interpolation) is optimal for narrowband systems. Use
α=0.5 to preserve wideband (incoherent) power.
- If input delays are already quantized (all fractional offsets below 0.01), the interpolation
weight computation is skipped but the same delay-selection logic is used.
- The
fix_taps parameter controls whether delay grids are shared across antenna pairs and/or
snapshots, trading accuracy for a more compact representation.
- Input delays may be per-antenna
[n_rx, n_tx, n_path_s] or shared [1, 1, n_path_s]. When
shared and fix_taps is 0 or 3, delays are expanded internally and output delays are per-antenna.
When shared and fix_taps is 1 or 2, output delays remain shared [1, 1, n_taps].
- The number of antennas
n_rx and n_tx must be the same across all snapshots, but the number
of paths n_path_s may differ per snapshot.
-
Declaration:
template <typename dtype>
void quadriga_lib::quantize_delays(
const std::vector<arma::Cube<dtype>> *coeff_re,
const std::vector<arma::Cube<dtype>> *coeff_im,
const std::vector<arma::Cube<dtype>> *delay,
std::vector<arma::Cube<dtype>> *coeff_re_quant,
std::vector<arma::Cube<dtype>> *coeff_im_quant,
std::vector<arma::Cube<dtype>> *delay_quant,
dtype tap_spacing = (dtype)5.0e-9,
arma::uword max_no_taps = 48,
dtype power_exponent = (dtype)1.0,
int fix_taps = 0);
-
Arguments:
const std::vector<arma::Cube<dtype>> *coeff_re (input)
Channel coefficients, real part. Vector of length n_snap, each cube of size
[n_rx, n_tx, n_path_s] where n_path_s may differ across snapshots.
const std::vector<arma::Cube<dtype>> *coeff_im (input)
Channel coefficients, imaginary part. Same sizes as coeff_re.
const std::vector<arma::Cube<dtype>> *delay (input)
Path delays in seconds. Vector of length n_snap, each cube of size
[n_rx, n_tx, n_path_s] or [1, 1, n_path_s]. The number of paths must match coeff_re.
std::vector<arma::Cube<dtype>> *coeff_re_quant (output)
Output coefficients, real part. Vector of length n_snap, each cube of size [n_rx, n_tx, n_taps].
std::vector<arma::Cube<dtype>> *coeff_im_quant (output)
Output coefficients, imaginary part. Vector of length n_snap, each cube of size [n_rx, n_tx, n_taps].
std::vector<arma::Cube<dtype>> *delay_quant (output)
Output delays in seconds. Vector of length n_snap, each cube of size [n_rx, n_tx, n_taps] or
[1, 1, n_taps].
dtype tap_spacing = 5.0e-9 (input)
Spacing of the delay bins in seconds. Default: 5 ns (200 MHz sampling rate).
arma::uword max_no_taps = 48 (input)
Maximum number of output taps. 0 means unlimited.
dtype power_exponent = 1.0 (input)
Interpolation exponent α. Use 1.0 for narrowband (linear) or 0.5 for wideband (power-preserving).
int fix_taps = 0 (input)
Delay sharing mode: 0 = per tx-rx pair and snapshot, 1 = single grid for all,
2 = per snapshot, 3 = per tx-rx pair.
-
Example:
// Create synthetic test data: 2 snapshots with different numbers of paths
std::vector<arma::Cube<double>> cre(2), cim(2), dl(2);
cre[0].set_size(1, 1, 3); cim[0].set_size(1, 1, 3); dl[0].set_size(1, 1, 3);
cre[1].set_size(1, 1, 2); cim[1].set_size(1, 1, 2); dl[1].set_size(1, 1, 2);
cre[0](0,0,0) = 1.0; cre[0](0,0,1) = 0.5; cre[0](0,0,2) = 0.3;
cre[1](0,0,0) = 0.8; cre[1](0,0,1) = 0.4;
cim[0].zeros(); cim[1].zeros();
dl[0](0,0,0) = 0.0; dl[0](0,0,1) = 12.5e-9; dl[0](0,0,2) = 33.4e-9;
dl[1](0,0,0) = 0.0; dl[1](0,0,1) = 10.0e-9;
std::vector<arma::Cube<double>> cre_q, cim_q, dl_q;
quadriga_lib::quantize_delays(&cre, &cim, &dl, &cre_q, &cim_q, &dl_q, 5.0e-9, 48, 1.0, 0);
Channel generation functions
get_channels_ieee_indoor - Generate indoor MIMO channel realizations for IEEE TGn/TGac/TGax/TGah models
-
Description:
- Generates one or multiple indoor channel realizations based on IEEE TGn/TGac/TGax/TGah model definitions.
- 2D model: no elevation angles are used; azimuth angles and planar motion are considered.
- Supports channel model types
A, B, C, D, E, F (as defined by TGn) via ChannelType.
- Can generate MU-MIMO channels (
n_users > 1) with per-user distances/floors and optional angle offsets according to TGac
- Optional time evolution via
observation_time, update_rate, and mobility parameters.
-
Declaration:
std::vector<quadriga_lib::channel<double>> quadriga_lib::get_channels_ieee_indoor(
const arrayant<double> &ap_array,
const arrayant<double> &sta_array,
std::string ChannelType,
double CarrierFreq_Hz = 5.25e9,
double tap_spacing_s = 10.0e-9,
arma::uword n_users = 1,
double observation_time = 0.0,
double update_rate = 1.0e-3,
double speed_station_kmh = 0.0,
double speed_env_kmh = 1.2,
arma::vec Dist_m = {4.99},
arma::uvec n_floors = {0},
bool uplink = false,
arma::mat offset_angles = {},
arma::uword n_subpath = 20,
double Doppler_effect = 50.0,
arma::sword seed = -1,
double KF_linear = NAN,
double XPR_NLOS_linear = NAN,
double SF_std_dB_LOS = NAN,
double SF_std_dB_NLOS = NAN,
double dBP_m = NAN );
-
Arguments:
const arrayant<double> ap_array (input)
Access point array antenna with n_tx elements (= ports after element coupling).
const arrayant<double> sta_array (input)
Mobile station array antenna with n_rx elements (= ports after element coupling).
std::string ChannelType (input)
Channel model type as defined by TGn. Supported: A, B, C, D, E, F.
double CarrierFreq_Hz = 5.25e9 (optional input)
Carrier frequency in Hz.
double tap_spacing_s = 10.0e-9 (optional input)
Tap spacing in seconds. Must be equal to 10 ns / 2^k (TGn default = 10e-9).
arma::uword n_users = 1 (optional input)
Number of users (only for TGac, TGah). Output vector length equals n_users.
double observation_time = 0.0 (optional input)
Channel observation time in seconds. 0.0 creates a static channel.
double update_rate = 1.0e-3 (optional input)
Channel update interval in seconds (only relevant when observation_time > 0).
double speed_station_kmh = 0.0 (optional input)
Station movement speed in km/h. Movement direction is AoA_offset. Only relevant when observation_time > 0.
double speed_env_kmh = 1.2 (optional input)
Environment movement speed in km/h. Default 1.2 for TGn, use 0.089 for TGac. Only relevant when
observation_time > 0.
arma::vec Dist_m = {4.99} (optional input)
TX-to-RX distance(s) in meters. Length n_users or length 1 (same distance for all users). Size
[n_users] or [1].
arma::uvec n_floors = {0} (optional input)
Number of floors for TGah model (per user), up to 4 floors. Length n_users or length 1. Size
[n_users] or [1].
bool uplink = false (optional input)
Channel direction flag. Default is downlink; set to true to generate reverse (uplink) direction.
arma::mat offset_angles = {} (optional input)
Offset angles in degree for MU-MIMO channels. Empty uses model defaults (TGac auto for n_users > 1).
Size [4, n_users] with rows: AoD LOS, AoD NLOS, AoA LOS, AoA NLOS.
arma::uword n_subpath = 20 (optional input)
Number of sub-paths per path/cluster used for Laplacian angular spread mapping.
double Doppler_effect = 50.0 (optional input)
Special Doppler effects: models D, E (fluorescent lights, value = mains freq.) and F (moving
vehicle speed in km/h). Use 0.0 to disable.
arma::sword seed = -1 (optional input)
Numeric seed for repeatability. -1 disables the fixed seed and uses the system random device.
double KF_linear = NAN (optional input)
Overwrites the model-specific KF-value. If this parameter is NAN (default) or negative, model defaults are used:
A/B/C (KF = 1 for d < dBP, 0 otherwise); D (KF = 2 for d < dBP, 0 otherwise); E/F (KF = 4 for d < dBP, 0 otherwise).
KF is applied to the first tap only. Breakpoint distance is ignored for KF_linear >= 0.
double XPR_NLOS_linear = NAN (optional input)
Overwrites the model-specific Cross-polarization ratio. If this parameter is NAN (default) or negative,
the model default of 2 (3 dB) is used. XPR is applied to all NLOS taps.
double SF_std_dB_LOS = NAN (optional input)
Overwrites the model-specific shadow fading for LOS channels. If this parameter is NAN (default),
the model default of 3 dB is used. SF_std_dB_LOS is applied to all LOS channels, where the
AP-STA distance d < dBP.
double SF_std_dB_NLOS = NAN (optional input)
Overwrites the model-specific shadow fading for LOS channels. If this parameter is NAN (default),
the model defaults are A/B: 4 dB, C/D: 5 dB, E/F: 6 dB. SF_std_dB_NLOS is applied to all NLOS channels,
where the AP-STA distance d >= dBP.
double dBP_m = NAN (optional input)
Overwrites the model-specific breakpoint distance. If this parameter is NAN (default) or negative,
the model defaults are A/B/C: 5 m, D: 10 m, E: 20 m, F: 30 m.
-
Returns:
std::vector<quadriga_lib::channel<double>> (output)
Vector of channel objects with length n_users. Each entry contains the generated indoor channel
realization for one user (including direction determined by uplink).
get_channels_irs - Calculate channel coefficients for intelligent reflective surfaces (IRS)
-
Description:
- Calculates MIMO channel coefficients and delays for IRS-assisted communication using two channel segments:
1. TX → IRS; 2. IRS → RX
- The IRS is modeled as a passive antenna array with phase shifts defined via its coupling matrix.
- IRS codebook entries can be selected via a port index (
i_irs).
- Supports combining paths from both segments to form
n_path_irs valid output paths, subject to a gain threshold.
- Optional second IRS array allows different antenna behavior for TX-IRS and IRS-RX directions.
- Returns a boolean vector indicating which path combinations are included in the output.
- Allowed datatypes (
dtype): float or double
-
Declaration:
std::vector<bool> quadriga_lib::get_channels_irs(
const arrayant<dtype> *tx_array,
const arrayant<dtype> *rx_array,
const arrayant<dtype> *irs_array,
dtype Tx, dtype Ty, dtype Tz,
dtype Tb, dtype Tt, dtype Th,
dtype Rx, dtype Ry, dtype Rz,
dtype Rb, dtype Rt, dtype Rh,
dtype Ix, dtype Iy, dtype Iz,
dtype Ib, dtype It, dtype Ih,
const arma::Mat<dtype> *fbs_pos_1,
const arma::Mat<dtype> *lbs_pos_1,
const arma::Col<dtype> *path_gain_1,
const arma::Col<dtype> *path_length_1,
const arma::Mat<dtype> *M_1,
const arma::Mat<dtype> *fbs_pos_2,
const arma::Mat<dtype> *lbs_pos_2,
const arma::Col<dtype> *path_gain_2,
const arma::Col<dtype> *path_length_2,
const arma::Mat<dtype> *M_2,
arma::Cube<dtype> *coeff_re,
arma::Cube<dtype> *coeff_im,
arma::Cube<dtype> *delay,
arma::uword i_irs = 0,
dtype threshold_dB = -140.0,
dtype center_frequency = 0.0,
bool use_absolute_delays = false,
arma::Cube<dtype> *aod = nullptr,
arma::Cube<dtype> *eod = nullptr,
arma::Cube<dtype> *aoa = nullptr,
arma::Cube<dtype> *eoa = nullptr,
const arrayant<dtype> *irs_array_2 = nullptr,
const std::vector<bool> *active_path = nullptr);
-
Arguments:
const arrayant<dtype> *tx_array (input)
Pointer to the transmit antenna array object (with n_tx elements).
const arrayant<dtype> *rx_array (input)
Pointer to the receive antenna array object (with n_rx elements).
const arrayant<dtype> *irs_array (input)
Pointer to the IRS array antenna (with n_irs elements).
dtype Tx, Ty, Tz (input)
Transmitter position in Cartesian coordinates [m].
dtype Tb, Tt, Th (input)
Transmitter orientation (Euler angles) in radians.
dtype Rx, Ry, Rz (input)
Receiver position in Cartesian coordinates [m].
dtype Rb, Rt, Rh (input)
Receiver orientation (Euler angles) in radians.
dtype Ix, Iy, Iz (input)
IRS position in Cartesian coordinates [m].
dtype Ib, It, Ih (input)
IRS orientation (Euler angles) in radians.
const arma::Mat<dtype> *fbs_pos_1 (input)
First-bounce scatterer positions of TX → IRS paths, Size [3, n_path_1].
const arma::Mat<dtype> *lbs_pos_1 (input)
Last-bounce scatterer positions of TX → IRS paths, Size [3, n_path_1].
const arma::Col<dtype> *path_gain_1 (input)
Path gains (linear) for TX → IRS paths, Length n_path_1.
const arma::Col<dtype> *path_length_1 (input)
Path lengths for TX → IRS paths, Length n_path_1.
const arma::Mat<dtype> *M_1 (input)
Polarization transfer matrix for TX → IRS paths, Size [8, n_path_1].
const arma::Mat<dtype> *fbs_pos_2 (input)
First-bounce scatterer positions of IRS → RX paths, Size [3, n_path_2].
const arma::Mat<dtype> *lbs_pos_2 (input)
Last-bounce scatterer positions of IRS → RX paths, Size [3, n_path_2].
const arma::Col<dtype> *path_gain_2 (input)
Path gains (linear) for IRS → RX paths, Length n_path_2.
const arma::Col<dtype> *path_length_2 (input)
Path lengths for IRS → RX paths, Length n_path_2.
const arma::Mat<dtype> *M_2 (input)
Polarization transfer matrix for IRS → RX paths, Size [8, n_path_2].
arma::Cube<dtype> *coeff_re (output)
Real part of resulting IRS-assisted channel coefficients, Size [n_rx, n_tx, n_path_irs].
arma::Cube<dtype> *coeff_im (output)
Imaginary part of channel coefficients, Size [n_rx, n_tx, n_path_irs].
arma::Cube<dtype> *delay (output)
Propagation delays in seconds, Size [n_rx, n_tx, n_path_irs].
arma::uword i_irs = 0 (optional input)
Index of IRS codebook entry (port number), Default: 0.
dtype threshold_dB = -140.0 (optional input)
Threshold (in dB) below which paths are discarded. Default: -140.0.
dtype center_frequency = 0.0 (optional input)
Center frequency in Hz; 0.0 disables phase computation. Default: 0.0.
bool use_absolute_delays = false (optional input)
If true, includes LOS delay in all paths. Default: false.
arma::Cube<dtype> *aod = nullptr (optional output)
Azimuth of Departure angles [rad], Size [n_rx, n_tx, n_path_irs].
arma::Cube<dtype> *eod = nullptr (optional output)
Elevation of Departure angles [rad], Size [n_rx, n_tx, n_path_irs].
arma::Cube<dtype> *aoa = nullptr (optional output)
Azimuth of Arrival angles [rad], Size [n_rx, n_tx, n_path_irs].
arma::Cube<dtype> *eoa = nullptr (optional output)
Elevation of Arrival angles [rad], Size [n_rx, n_tx, n_path_irs].
const arrayant<dtype> *irs_array_2 = nullptr (optional input)
Optional second IRS array (TX side) for asymmetric IRS behavior.
const std::vector<bool> *active_path = nullptr (optional input)
Optional bitmask for selecting active TX-IRS and IRS-RX path pairs. Ignores threshold_dB when provided.
-
Returns:
std::vector<bool>
Boolean mask of length n_path_1 * n_path_2, indicating which path combinations were used.
-
See also:
get_channels_multifreq - Calculate channel coefficients for spherical waves across multiple frequencies
-
Description:
- Extends
get_channels_spherical to support frequency-dependent antenna patterns, path gains,
and polarization transfer (Jones) matrices across multiple output frequencies.
- Geometry is computed once: departure angles, arrival angles, element-resolved path delays, and LOS
path detection are frequency-independent and reused for all output frequencies. This avoids redundant
trigonometry and distance calculations.
- Four frequency grids are aligned by interpolation:
| 1. |
TX array frequencies (defined by tx_array[i].center_frequency) |
| 2. |
RX array frequencies (defined by rx_array[i].center_frequency) |
| 3. |
Input sample frequencies (freq_in) at which path_gain and M are provided |
| 4. |
Target output frequencies (freq_out) at which coefficients and delays are returned |
- For each output frequency, TX and RX antenna patterns are interpolated from their respective
multi-frequency vectors using spherical interpolation (SLERP) with linear fallback, the same
algorithm used in
arrayant_interpolate_multi. The private qd_arrayant_interpolate function is
called directly for maximum performance.
- Path gain is interpolated linearly across frequency. The Jones matrix
M is interpolated using
SLERP for each complex entry pair to preserve phase coherence.
- Extrapolation is handled by clamping to the nearest available frequency entry in all four grids.
- Propagation speed can be set to support both radio (speed of light, default) and acoustic
(speed of sound, ~343 m/s) simulations. This affects wavelength, wave number, and delay calculations.
- The Jones matrix
M supports two formats: 8 rows for full polarimetric
(ReVV, ImVV, ReVH, ImVH, ReHV, ImHV, ReHH, ImHH), or 2 rows for scalar pressure waves
(ReVV, ImVV only), where VH, HV, and HH entries are implicitly zero.
- Antenna element coupling is applied using the coupling matrices from the first entry of each
multi-frequency vector (consistent across all entries by
arrayant_is_valid_multi constraints).
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::get_channels_multifreq(
const std::vector<arrayant<dtype>> &tx_array,
const std::vector<arrayant<dtype>> &rx_array,
dtype Tx, dtype Ty, dtype Tz,
dtype Tb, dtype Tt, dtype Th,
dtype Rx, dtype Ry, dtype Rz,
dtype Rb, dtype Rt, dtype Rh,
const arma::Mat<dtype> &fbs_pos,
const arma::Mat<dtype> &lbs_pos,
const arma::Mat<dtype> &path_gain,
const arma::Col<dtype> &path_length,
const arma::Cube<dtype> &M,
const arma::Col<dtype> &freq_in,
const arma::Col<dtype> &freq_out,
std::vector<arma::Cube<dtype>> &coeff_re,
std::vector<arma::Cube<dtype>> &coeff_im,
std::vector<arma::Cube<dtype>> &delay,
bool use_absolute_delays = false,
bool add_fake_los_path = false,
dtype propagation_speed = dtype(299792458.0))
-
Arguments:
const std::vector<arrayant<dtype>> &tx_array (input)
Multi-frequency transmit array antenna vector. All entries must pass arrayant_is_valid_multi.
const std::vector<arrayant<dtype>> &rx_array (input)
Multi-frequency receive array antenna vector. All entries must pass arrayant_is_valid_multi.
dtype Tx, Ty, Tz (input)
Transmitter position in Cartesian coordinates [m].
dtype Tb, Tt, Th (input)
Transmitter orientation (bank, tilt, heading) in [rad].
dtype Rx, Ry, Rz (input)
Receiver position in Cartesian coordinates [m].
dtype Rb, Rt, Rh (input)
Receiver orientation (bank, tilt, heading) in [rad].
const arma::Mat<dtype> &fbs_pos (input)
First-bounce scatterer positions, Size: [3, n_path].
const arma::Mat<dtype> &lbs_pos (input)
Last-bounce scatterer positions, Size: [3, n_path].
const arma::Mat<dtype> &path_gain (input)
Path gain in linear scale, Size: [n_path, n_freq_in]. Each column corresponds to one input frequency.
const arma::Col<dtype> &path_length (input)
Absolute path lengths from TX to RX phase center, Length: n_path.
const arma::Cube<dtype> &M (input)
Polarization transfer matrix, Size: [8, n_path, n_freq_in] for full polarimetric or
[2, n_path, n_freq_in] for scalar pressure. Each slice corresponds to one input frequency.
Interleaved complex format: (ReVV, ImVV, ReVH, ImVH, ReHV, ImHV, ReHH, ImHH) for 8 rows,
or (ReVV, ImVV) for 2 rows.
const arma::Col<dtype> &freq_in (input)
Input sample frequencies in [Hz] at which path_gain and M are defined, Length: n_freq_in.
const arma::Col<dtype> &freq_out (input)
Target frequencies in [Hz] at which to compute output coefficients and delays, Length: n_freq_out.
std::vector<arma::Cube<dtype>> &coeff_re (output)
Real part of channel coefficients. Vector of length n_freq_out, each cube of size [n_rx, n_tx, n_path].
std::vector<arma::Cube<dtype>> &coeff_im (output)
Imaginary part of channel coefficients. Same structure as coeff_re.
std::vector<arma::Cube<dtype>> &delay (output)
Propagation delays in seconds. Same structure as coeff_re.
bool use_absolute_delays = false (optional input)
If true, LOS delay is included in all paths. Default: false.
bool add_fake_los_path = false (optional input)
Adds a zero-power LOS path if no LOS path was detected. Default: false.
dtype propagation_speed = 299792458.0 (optional input)
Wave propagation speed in [m/s]. Default is the speed of light for radio simulations.
Set to ~343.0 for acoustic simulations in air.
-
Example:
// Build a 2-way speaker as TX (source)
arma::vec freqs = {100.0, 500.0, 1000.0, 5000.0, 10000.0};
auto tx_woofer = quadriga_lib::generate_speaker<double>(
"piston", 0.083, 50.0, 3000.0, 12.0, 24.0, 87.0, "hemisphere",
0.0, 0.0, 0.0, 0.20, 0.30, freqs, 10.0);
auto tx_tweeter = quadriga_lib::generate_speaker<double>(
"piston", 0.013, 1500.0, 20000.0, 24.0, 12.0, 90.0, "hemisphere",
0.0, 0.0, 0.0, 0.20, 0.30, freqs, 10.0);
auto tx = quadriga_lib::arrayant_concat_multi(tx_woofer, tx_tweeter);
// Omnidirectional microphone as RX (single entry, clamped for all frequencies)
std::vector<quadriga_lib::arrayant<double>> rx = { quadriga_lib::generate_arrayant_omni<double>() };
// Simple LOS path setup
arma::mat fbs = arma::mat({0.5, 0.0, 0.0}).t();
arma::mat lbs = arma::mat({0.5, 0.0, 0.0}).t();
arma::vec path_length = {1.0}; // 1 meter distance
// Frequency-flat path gain and scalar Jones matrix
arma::vec freq_in = {100.0, 10000.0};
arma::mat path_gain_mat(1, 2, arma::fill::ones);
arma::cube M_cube(2, 1, 2, arma::fill::zeros);
M_cube(0, 0, 0) = 1.0; M_cube(0, 0, 1) = 1.0; // ReVV = 1 at both freqs
arma::vec freq_out = {200.0, 1000.0, 5000.0};
std::vector<arma::cube> coeff_re, coeff_im, delays;
quadriga_lib::get_channels_multifreq(tx, rx,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // TX at origin
1.0, 0.0, 0.0, 0.0, 0.0, 0.0, // RX at (1,0,0)
fbs_pos, lbs_pos, path_gain_mat, path_length, M_cube,
freq_in, freq_out, coeff_re, coeff_im, delays,
false, false, 343.0); // Speed of sound for acoustics
-
See also:
get_channels_planar - Calculate channel coefficients for planar waves
-
Description:
- Calculates MIMO channel coefficients and delays for a set of planar wave paths between two antenna arrays.
- Interpolates antenna patterns (including orientation and polarization) for both transmitter and receiver arrays.
- Supports LOS path identification based on distance (angles are ignored).
- Polarization transfer matrix models polarization coupling and must be normalized.
- Doppler weights can optionally be calculated from receiver motion relative to path direction.
- Element positions and antenna orientation are fully considered for delay and phase.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::get_channels_planar(
const arrayant<dtype> *tx_array,
const arrayant<dtype> *rx_array,
dtype Tx, dtype Ty, dtype Tz,
dtype Tb, dtype Tt, dtype Th,
dtype Rx, dtype Ry, dtype Rz,
dtype Rb, dtype Rt, dtype Rh,
const arma::Col<dtype> *aod,
const arma::Col<dtype> *eod,
const arma::Col<dtype> *aoa,
const arma::Col<dtype> *eoa,
const arma::Col<dtype> *path_gain,
const arma::Col<dtype> *path_length,
const arma::Mat<dtype> *M,
arma::Cube<dtype> *coeff_re,
arma::Cube<dtype> *coeff_im,
arma::Cube<dtype> *delay,
dtype center_frequency = dtype(0.0),
bool use_absolute_delays = false,
bool add_fake_los_path = false,
arma::Col<dtype> *rx_Doppler = nullptr);
-
Arguments:
const arrayant<dtype> *tx_array (input)
Pointer to the transmit antenna array object (with n_tx elements).
const arrayant<dtype> *rx_array (input)
Pointer to the receive antenna array object (with n_rx elements).
dtype Tx, Ty, Tz (input)
Transmitter position in Cartesian coordinates [m].
dtype Tb, Tt, Th (input)
Transmitter orientation (Euler) angles (bank, tilt, head) in [rad].
dtype Rx, Ry, Rz (input)
Receiver position in Cartesian coordinates [m].
dtype Rb, Rt, Rh (input)
Receiver orientation (Euler) angles (bank, tilt, head) in [rad].
const arma::Col<dtype> *aod (input)
Departure azimuth angles in radians, Length n_path.
const arma::Col<dtype> *eod (input)
Departure elevation angles in radians, Length n_path.
const arma::Col<dtype> *aoa (input)
Arrival azimuth angles in radians, Length n_path.
const arma::Col<dtype> *eoa (input)
Arrival elevation angles in radians, Length n_path.
const arma::Col<dtype> *path_gain (input)
Path gains in linear scale, Length n_path.
const arma::Col<dtype> *path_length (input)
Path lengths from TX to RX phase center, Length n_path.
const arma::Mat<dtype> *M (input)
Polarization transfer matrix of size [8, n_path], interleaved: (ReVV, ImVV, ReVH, ImVH, ReHV, ImHV, ReHH, ImHH).
arma::Cube<dtype> *coeff_re (output)
Real part of channel coefficients, Size [n_rx, n_tx, n_path(+1)].
arma::Cube<dtype> *coeff_im (output)
Imaginary part of channel coefficients, Size [n_rx, n_tx, n_path(+1)].
arma::Cube<dtype> *delay (output)
Propagation delays in seconds, Size [n_rx, n_tx, n_path(+1)].
dtype center_frequency = 0.0 (optional input)
Center frequency in Hz; set to 0 to disable phase calculation. Default: 0.0
bool use_absolute_delays = false (optional input)
If true, includes LOS delay in all paths. Default: false
bool add_fake_los_path = false (optional input)
Adds a zero-power LOS path if no LOS is present. Default: false
arma::Col<dtype> *rx_Doppler = nullptr (optional output)
Doppler weights for moving RX, Length n_path(+1). Positive = towards path, Negative = away.
get_channels_spherical - Calculate channel coefficients for spherical waves
-
Description:
- Calculates MIMO channel coefficients and delays for a set of spherical wave paths between two antenna arrays.
- Interpolates antenna patterns (including orientation and polarization) for both transmitter and receiver arrays.
- Accurately models path-based propagation using provided scatterer positions.
- Supports LOS path identification and handles complex polarization coupling.
- Element positions and antenna orientation are fully considered for delay and phase.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::get_channels_spherical(
const arrayant<dtype> *tx_array,
const arrayant<dtype> *rx_array,
dtype Tx, dtype Ty, dtype Tz,
dtype Tb, dtype Tt, dtype Th,
dtype Rx, dtype Ry, dtype Rz,
dtype Rb, dtype Rt, dtype Rh,
const arma::Mat<dtype> *fbs_pos,
const arma::Mat<dtype> *lbs_pos,
const arma::Col<dtype> *path_gain,
const arma::Col<dtype> *path_length,
const arma::Mat<dtype> *M,
arma::Cube<dtype> *coeff_re,
arma::Cube<dtype> *coeff_im,
arma::Cube<dtype> *delay,
dtype center_frequency = dtype(0.0),
bool use_absolute_delays = false,
bool add_fake_los_path = false,
arma::Cube<dtype> *aod = nullptr,
arma::Cube<dtype> *eod = nullptr,
arma::Cube<dtype> *aoa = nullptr,
arma::Cube<dtype> *eoa = nullptr)
-
Arguments:
const arrayant<dtype> *tx_array (input)
Pointer to the transmit antenna array object (with n_tx elements).
const arrayant<dtype> *rx_array (input)
Pointer to the receive antenna array object (with n_rx elements).
dtype Tx, Ty, Tz (input)
Transmitter position in Cartesian coordinates [m].
dtype Tb, Tt, Th (input)
Transmitter orientation (Euler) angles (bank, tilt, head) in [rad].
dtype Rx, Ry, Rz (input)
Receiver position in Cartesian coordinates [m].
dtype Rb, Rt, Rh (input)
Receiver orientation (Euler) angles (bank, tilt, head) in [rad].
const arma::Mat<dtype> *fbs_pos (input)
First-bounce scatterer positions, Size: [3, n_path].
const arma::Mat<dtype> *lbs_pos (input)
Last-bounce scatterer positions, Size: [3, n_path].
const arma::Col<dtype> *path_gain (input)
Path gains in linear scale, Length n_path.
const arma::Col<dtype> *path_length (input)
Path lengths from TX to RX phase center Length n_path.
const arma::Mat<dtype> *M (input)
Polarization transfer matrix of size [8, n_path], interleaved: (ReVV, ImVV, ReVH, ImVH, ReHV, ImHV, ReHH, ImHH).
arma::Cube<dtype> *coeff_re (output)
Real part of channel coefficients, Size [n_rx, n_tx, n_path].
arma::Cube<dtype> *coeff_im (output)
Imaginary part of channel coefficients, Size [n_rx, n_tx, n_path].
arma::Cube<dtype> *delay (output)
Propagation delays in seconds, Size [n_rx, n_tx, n_path].
dtype center_frequency = 0.0 (optional input)
Center frequency in Hz; set to 0 to disable phase calculation. Default: 0.0
bool use_absolute_delays = false (optional input)
If true, includes LOS delay in all paths. Default: false
bool add_fake_los_path = false (optional input)
Adds a zero-power LOS path if no LOS is present. Default: false
arma::Cube<dtype> *aod = nullptr (optional output)
Azimuth of Departure angles in radians, Size [n_rx, n_tx, n_path].
arma::Cube<dtype> *eod = nullptr (optional output)
Elevation of Departure angles in radians, Size [n_rx, n_tx, n_path].
arma::Cube<dtype> *aoa = nullptr (optional output)
Azimuth of Arrival angles in radians, Size [n_rx, n_tx, n_path].
arma::Cube<dtype> *eoa = nullptr (optional output)
Elevation of Arrival angles in radians, Size [n_rx, n_tx, n_path].
Math Functions
fast_acos - Fast, approximate arc-cosine
-
Description:
Computes elementwise arc-cosine for an Armadillo vector. Designed for high throughput on modern CPUs.
- Operates on input values in [-1, 1]
- AVX2-optimized (8 floats per lane); scalar fallback without AVX2 or on non-AVX2 CPUs
- Parallelizes across cores with OpenMP when enabled
- Results are approximate and may differ from
std::acosf
- For x in [-1, 1], the maximum error is approximately 2 ULP (~2.4e-7)
- Input values outside [-1, 1] produce NaN (IEEE compliant)
- Allowed input datatype:
float (Armadillo fvec) or double (Armadillo vec)
-
Declaration:
void quadriga_lib::fast_acos(const arma::fvec &x, arma::fvec &c);
void quadriga_lib::fast_acos(const arma::vec &x, arma::fvec &c);
-
Arguments:
const arma::fvec &x or const arma::vec &x (input)
Input values in [-1, 1]. Length [n].
arma::fvec &c (output)
Set to acos(x). Resized to length n if needed. Length [n].
-
Example:
arma::fvec x = arma::linspace<arma::fvec>(-1.0f, 1.0f, 1000);
arma::fvec c;
quadriga_lib::fast_acos(x, c);
fast_asin - Fast, approximate arc-sine
-
Description:
Computes elementwise arc-sine for an Armadillo vector. Designed for high throughput on modern CPUs.
- Operates on input values in [-1, 1]
- AVX2-optimized (8 floats per lane); scalar fallback without AVX2 or on non-AVX2 CPUs
- Parallelizes across cores with OpenMP when enabled
- Results are approximate and may differ from
std::asinf
- For x in [-1, 1], the maximum error is approximately 2 ULP (~2.4e-7)
- Input values outside [-1, 1] produce NaN (IEEE compliant)
- Allowed input datatype:
float (Armadillo fvec) or double (Armadillo vec)
-
Declaration:
void quadriga_lib::fast_asin(const arma::fvec &x, arma::fvec &s);
void quadriga_lib::fast_asin(const arma::vec &x, arma::fvec &s);
-
Arguments:
const arma::fvec &x or const arma::vec &x (input)
Input values in [-1, 1]. Length [n].
arma::fvec &s (output)
Set to asin(x). Resized to length n if needed. Length [n].
-
Example:
arma::fvec x = arma::linspace<arma::fvec>(-1.0f, 1.0f, 1000);
arma::fvec s;
quadriga_lib::fast_asin(x, s);
fast_atan2 - Fast, approximate two-argument arc-tangent
-
Description:
Computes elementwise atan2(y, x) for two Armadillo vectors. Designed for high throughput on modern CPUs.
- Returns angles in radians in the range (-pi, pi]
- AVX2-optimized (8 floats per lane); scalar fallback without AVX2 or on non-AVX2 CPUs
- Parallelizes across cores with OpenMP when enabled
- Results are approximate and may differ from
std::atan2f
- Maximum error is approximately 3 ULP (~3.6e-7) across the full domain
atan2(0, 0) returns 0; atan2(±0, -0) returns ±0 (not ±pi)
- Both input vectors must have the same length
- Input and output cannot alias (in-place operation not allowed)
- Allowed input datatype:
float (Armadillo fvec) or double (Armadillo vec)
-
Declaration:
void quadriga_lib::fast_atan2(const arma::fvec &y, const arma::fvec &x, arma::fvec &a);
void quadriga_lib::fast_atan2(const arma::vec &y, const arma::vec &x, arma::fvec &a);
-
Arguments:
const arma::fvec &y or const arma::vec &y (input)
Y-coordinates (numerator of atan2). Length [n].
const arma::fvec &x or const arma::vec &x (input)
X-coordinates (denominator of atan2). Length [n].
arma::fvec &a (output)
Set to atan2(y, x) in radians. Resized to length n if needed. Length [n].
-
Example:
arma::fvec y = {1.0f, -1.0f, 0.0f, 1.0f};
arma::fvec x = {1.0f, 1.0f, -1.0f, 0.0f};
arma::fvec a;
quadriga_lib::fast_atan2(y, x, a);
// a ≈ {0.7854, -0.7854, 3.1416, 1.5708}
fast_cart2geo - Fast, approximate Cartesian-to-geographic conversion
-
Description:
Converts elementwise unit-sphere Cartesian coordinates to azimuth/elevation angles (in radians).
- az = atan2(y, x), el = asin(clamp(z, -1, 1))
- The z-coordinate is clamped to [-1, 1] before computing asin, guarding against FMA rounding artefacts
from upstream matrix multiplications that can push abs(z) slightly above 1
- AVX2-optimized (8 floats per lane); scalar fallback without AVX2 or on non-AVX2 CPUs
- Parallelizes across cores with OpenMP when enabled
- Results are approximate and may differ from
std::atan2f / std::asinf
- All input vectors must have the same length
- Input and output cannot alias (in-place operation not allowed)
- Allowed input datatype:
float (Armadillo fvec) or double (Armadillo vec)
-
Declaration:
void quadriga_lib::fast_cart2geo(const arma::fvec &x, const arma::fvec &y, const arma::fvec &z,
arma::fvec &az, arma::fvec &el);
void quadriga_lib::fast_cart2geo(const arma::vec &x, const arma::vec &y, const arma::vec &z,
arma::fvec &az, arma::fvec &el);
-
Arguments:
const arma::fvec &x or const arma::vec &x (input)
X-coordinates. Length [n].
const arma::fvec &y or const arma::vec &y (input)
Y-coordinates. Length [n].
const arma::fvec &z or const arma::vec &z (input)
Z-coordinates. Length [n].
arma::fvec &az (output)
Azimuth angles in radians. Resized to length n if needed. Length [n].
arma::fvec &el (output)
Elevation angles in radians. Resized to length n if needed. Length [n].
-
Example:
arma::fvec x = {1.0f, 0.0f, -1.0f};
arma::fvec y = {0.0f, 1.0f, 0.0f};
arma::fvec z = {0.0f, 0.0f, 0.0f};
arma::fvec az, el;
quadriga_lib::fast_cart2geo(x, y, z, az, el);
// az ≈ {0.0, 1.5708, 3.1416}, el ≈ {0.0, 0.0, 0.0}
fast_geo2cart - Fast, approximate geographic-to-Cartesian conversion
-
Description:
Converts elementwise azimuth/elevation angles (in radians) to unit-sphere Cartesian coordinates.
- x = cos(el) cos(az), y = cos(el) sin(az), z = sin(el)
- AVX2-optimized (8 floats per lane); scalar fallback without AVX2 or on non-AVX2 CPUs
- Parallelizes across cores with OpenMP when enabled
- Results are approximate and may differ from
std::sinf / std::cosf
- Optionally returns intermediate sin/cos values via pointer arguments; pass
nullptr to skip
- Both input vectors must have the same length
- Input and output cannot alias (in-place operation not allowed)
- Allowed input datatype:
float (Armadillo fvec) or double (Armadillo vec)
-
Declaration:
void quadriga_lib::fast_geo2cart(const arma::fvec &az, const arma::fvec &el,
arma::fvec &x, arma::fvec &y, arma::fvec &z,
arma::fvec *sAZ = nullptr, arma::fvec *cAZ = nullptr,
arma::fvec *sEL = nullptr, arma::fvec *cEL = nullptr);
void quadriga_lib::fast_geo2cart(const arma::vec &az, const arma::vec &el,
arma::fvec &x, arma::fvec &y, arma::fvec &z,
arma::fvec *sAZ = nullptr, arma::fvec *cAZ = nullptr,
arma::fvec *sEL = nullptr, arma::fvec *cEL = nullptr);
-
Arguments:
const arma::fvec &az or const arma::vec &az (input)
Azimuth angles in radians. Length [n].
const arma::fvec &el or const arma::vec &el (input)
Elevation angles in radians. Length [n].
arma::fvec &x (output)
X-coordinates on the unit sphere. Resized to length n if needed. Length [n].
arma::fvec &y (output)
Y-coordinates on the unit sphere. Resized to length n if needed. Length [n].
arma::fvec &z (output)
Z-coordinates on the unit sphere. Resized to length n if needed. Length [n].
arma::fvec *sAZ = nullptr (optional output)
If non-null, set to sin(az). Resized to length n if needed. Length [n] or nullptr.
arma::fvec *cAZ = nullptr (optional output)
If non-null, set to cos(az). Resized to length n if needed. Length [n] or nullptr.
arma::fvec *sEL = nullptr (optional output)
If non-null, set to sin(el). Resized to length n if needed. Length [n] or nullptr.
arma::fvec *cEL = nullptr (optional output)
If non-null, set to cos(el). Resized to length n if needed. Length [n] or nullptr.
-
Example:
arma::fvec az = {0.0f, 1.5708f, 3.1416f};
arma::fvec el = {0.0f, 0.5f, -0.5f};
arma::fvec x, y, z, sAZ, cEL;
quadriga_lib::fast_geo2cart(az, el, x, y, z, &sAZ, nullptr, nullptr, &cEL);
fast_sincos - Fast, approximate sine/cosine
-
Description:
Computes elementwise sine and/or cosine for an Armadillo vector. Designed for high throughput on modern CPUs.
- Operates on input angles in radians
- AVX2-optimized (8 floats per lane); scalar fallback without AVX2 or on non-AVX2 CPUs
- Parallelizes across cores with OpenMP when enabled
- Results are approximate and may differ from
std::sinf / std::cosf
- For x in [-pi, pi], the maximum absolute error is 2^(-22.1), and larger otherwise
- For x in [-500, 500], the maximum absolute error is 2^(-16.0)
- Either output (
s or c) may be nullptr to skip its computation
- Allowed input datatype:
float (Armadillo fvec) or double (Armadillo vec)
-
Declaration:
void quadriga_lib::fast_sincos(const arma::fvec &x, arma::fvec *s = nullptr, arma::fvec *c = nullptr);
void quadriga_lib::fast_sincos(const arma::vec &x, arma::fvec *s = nullptr, arma::fvec *c = nullptr);
-
Arguments:
const arma::fvec x or const arma::vec x (input)
Input angles in radians. Size [n].
arma::fvec *s = nullptr (optional output)
If non-null, set to sin(x). Resized to length n if needed. Size [n] or nullptr.
arma::fvec *c = nullptr (optional output)
If non-null, set to cos(x). Resized to length n if needed. Size [n] or nullptr.
-
Returns:
void (output)
No return value. Results written via output pointers.
-
Example:
arma::fvec x = arma::linspace[arma::fvec](arma::fvec)(0.0f, 6.2831853f, 1000);
arma::fvec s, c;
quadriga_lib::fast_sincos(x, &s, &c); // compute both
quadriga_lib::fast_sincos(x, &s, nullptr); // compute sine only
quadriga_lib::fast_sincos(x, nullptr, &c); // compute cosine only
fast_slerp - Fast, approximate spherical interpolation (SLERP) for complex value pairs
-
Description:
Interpolates elementwise between two complex-valued vectors using spherical linear interpolation
(SLERP) on the normalised directions and linear interpolation of amplitudes.
- Processes per-element interpolation weights (0 = A, 1 = B)
- AVX2-optimized (8 complex pairs per lane); scalar fallback without AVX2 or on non-AVX2 CPUs
- Parallelizes across cores with OpenMP when enabled
- Near-antipodal inputs (phase angle close to pi) smoothly transition to a linear fallback
- If both input amplitudes are negligible, the output is zero
- Maximum error versus double-precision reference is approximately 5 ULP
- Allowed input datatype:
float (Armadillo fvec) or double (Armadillo vec)
- All input vectors must have the same length
-
Declaration:
void quadriga_lib::fast_slerp(const arma::fvec &Ar, const arma::fvec &Ai,
const arma::fvec &Br, const arma::fvec &Bi,
const arma::fvec &w,
arma::fvec &Xr, arma::fvec &Xi);
void quadriga_lib::fast_slerp(const arma::vec &Ar, const arma::vec &Ai,
const arma::vec &Br, const arma::vec &Bi,
const arma::vec &w,
arma::fvec &Xr, arma::fvec &Xi);
-
Arguments:
const arma::fvec &Ar or const arma::vec &Ar (input)
Real part of source A. Length [n].
const arma::fvec &Ai or const arma::vec &Ai (input)
Imaginary part of source A. Length [n].
const arma::fvec &Br or const arma::vec &Br (input)
Real part of source B. Length [n].
const arma::fvec &Bi or const arma::vec &Bi (input)
Imaginary part of source B. Length [n].
const arma::fvec &w or const arma::vec &w (input)
Per-element interpolation weight in [0, 1]. 0 returns A, 1 returns B. Length [n].
arma::fvec &Xr (output)
Real part of interpolated result. Resized to length n if needed. Length [n].
arma::fvec &Xi (output)
Imaginary part of interpolated result. Resized to length n if needed. Length [n].
-
Returns:
void (output)
No return value. Results written to Xr and Xi.
-
Example:
arma::fvec Ar = {1.0f, 0.0f}, Ai = {0.0f, 1.0f};
arma::fvec Br = {0.0f, 1.0f}, Bi = {1.0f, 0.0f};
arma::fvec w = {0.5f, 0.5f};
arma::fvec Xr, Xi;
quadriga_lib::fast_slerp(Ar, Ai, Br, Bi, w, Xr, Xi);
Miscellaneous / Tools
acdf - Calculate the empirical averaged cumulative distribution function (CDF)
-
Description:
- Calculates the empirical CDF from the given data matrix, where each column represents an
independent data set (e.g., repeated experiment runs).
- Individual CDFs are computed per column by histogramming into the given (or auto-generated) bins
and taking the cumulative sum normalized by the number of valid samples.
- An averaged CDF is obtained by interpolating in quantile space: for a fine grid of probability
levels, the corresponding x-values from each individual CDF are averaged, then the result is
mapped back to the bin grid.
- Quantile statistics (mean and standard deviation) are reported at the 0.1, 0.2, ..., 0.9
probability levels.
Inf and NaN values in the data are excluded from the computation.
- If
bins points to an empty vector, 201 equally spaced bins spanning the data range are
generated and stored back. If bins points to a non-empty vector, those bin centers are used.
If bins is nullptr, bins are auto-generated internally.
-
Declaration:
template <typename dtype>
void quadriga_lib::acdf(const arma::Mat<dtype> &data,
arma::Col<dtype> *bins = nullptr,
arma::Mat<dtype> *Sh = nullptr,
arma::Col<dtype> *Sc = nullptr,
arma::Col<dtype> *mu = nullptr,
arma::Col<dtype> *sig = nullptr,
arma::uword n_bins = 201);
-
Arguments:
const arma::Mat<dtype> &data (input)
Input data matrix. Size [n_samples, n_sets]. Each column is one data set.
arma::Col<dtype> *bins = nullptr (optional input/output)
Bin centers for the histogram. Length [n_bins]. If pointing to an empty vector, auto-generated
bins are stored here. If pointing to a non-empty vector, those bin centers are used. If nullptr,
bins are auto-generated internally.
arma::Mat<dtype> *Sh = nullptr (optional output)
Individual CDFs, one per column of data. Size [n_bins, n_sets].
arma::Col<dtype> *Sc = nullptr (optional output)
Averaged CDF obtained by quantile-space averaging across data sets. Length [n_bins].
arma::Col<dtype> *mu = nullptr (optional output)
Mean of the 0.1, 0.2, ..., 0.9 quantiles across data sets. Length [9].
arma::Col<dtype> *sig = nullptr (optional output)
Standard deviation of the 0.1, 0.2, ..., 0.9 quantiles across data sets. Length [9].
arma::uword n_bins = 201 (input)
Number of bins to generate when bins are auto-generated. Must be at least 2. Ignored when
non-empty bins are provided.
-
Example:
#include "quadriga_tools.hpp"
// Generate random data: 10000 samples x 5 experiment runs
arma::mat data = arma::randn<arma::mat>(10000, 5);
arma::vec bins;
arma::mat Sh;
arma::vec Sc, mu, sig;
quadriga_lib::acdf(data, &bins, &Sh, &Sc, &mu, &sig);
// bins has 201 elements, Sh is [201, 5], Sc is [201], mu and sig are [9]
calc_angular_spreads_sphere - Calculate azimuth and elevation angular spreads with spherical wrapping
-
Description:
- Calculates the RMS azimuth and elevation angular spreads from a set of power-weighted angles.
- Inputs and outputs use
std::vector<arma::Col<dtype>> so that each channel impulse response
(CIR) can have a different number of paths.
- The RMS angular spread is computed as
sqrt(sum(pw .* d.^2)) where d are the wrapped
deviations from the circular mean. This is the second-moment definition used in 3GPP TR 38.901,
as opposed to the standard-deviation form sqrt(E[d^2] - E[d]^2).
- Uses spherical coordinate wrapping to avoid the pole singularity: the power-weighted mean
direction is computed in Cartesian coordinates and all paths are rotated so the centroid lies
on the equator before computing spreads.
- Without spherical wrapping, azimuth spread near the poles is inflated (large azimuth spread
despite energy being focused into a small solid angle). This method corrects for that.
- Optionally computes an optimal bank (roll) angle that maximizes azimuth spread and minimizes
elevation spread, corresponding to the principal axes of the angular power distribution.
- The bank angle is derived analytically from the eigenvectors of the 2x2 power-weighted
covariance matrix of the centered azimuth and elevation angles.
- An optional quantization step can group nearby paths before computing the spread.
- Setting
disable_wrapping to true skips the rotation and computes spreads directly from the
raw azimuth and elevation angles (equivalent to treating them as independent 1D variables).
In this mode, the orientation output will be zero and phi/theta will equal the input az/el.
-
Declaration:
template <typename dtype>
void quadriga_lib::calc_angular_spreads_sphere(
const std::vector<arma::Col<dtype>> &az,
const std::vector<arma::Col<dtype>> &el,
const std::vector<arma::Col<dtype>> &powers,
arma::Col<dtype> *azimuth_spread = nullptr,
arma::Col<dtype> *elevation_spread = nullptr,
arma::Mat<dtype> *orientation = nullptr,
std::vector<arma::Col<dtype>> *phi = nullptr,
std::vector<arma::Col<dtype>> *theta = nullptr,
bool disable_wrapping = false,
bool calc_bank_angle = true,
dtype quantize = (dtype)0);
-
Arguments:
const std::vector<arma::Col<dtype>> &az (input)
Azimuth angles in [rad], ranging from -pi to pi. Vector of length n_cir, each element has
length n_path (may differ per CIR).
const std::vector<arma::Col<dtype>> &el (input)
Elevation angles in [rad], ranging from -pi/2 to pi/2. Vector of length n_cir, each element
has length n_path matching the corresponding element in az.
const std::vector<arma::Col<dtype>> &powers (input)
Path powers in [W]. Vector of length n_cir, each element has length n_path matching the
corresponding element in az.
arma::Col<dtype> *azimuth_spread = nullptr (optional output)
RMS azimuth angular spread in [rad]. Length [n_cir].
arma::Col<dtype> *elevation_spread = nullptr (optional output)
RMS elevation angular spread in [rad]. Length [n_cir].
arma::Mat<dtype> *orientation = nullptr (optional output)
Power-weighted mean-angle orientation using aircraft principal axes: row 0 = bank angle,
row 1 = tilt angle, row 2 = heading angle, all in [rad]. Size [3, n_cir].
std::vector<arma::Col<dtype>> *phi = nullptr (optional output)
Rotated azimuth angles in [rad]. Vector of length n_cir, each element has length n_path.
std::vector<arma::Col<dtype>> *theta = nullptr (optional output)
Rotated elevation angles in [rad]. Vector of length n_cir, each element has length n_path.
bool disable_wrapping = false (input)
If true, skip the spherical rotation and compute spreads directly from raw angles. The
orientation output will be zero and phi/theta will equal the input az/el.
bool calc_bank_angle = true (input)
If true, the optimal bank angle is computed analytically. Only used when disable_wrapping
is false.
dtype quantize = 0 (input)
Angular quantization step in [deg]. Paths within this angular distance are grouped and their
powers summed before computing the spread. Set to 0 to treat all paths independently.
-
Example:
std::vector<arma::vec> az(2), el(2), powers(2);
az[0] = {0.1, -0.1, 0.05}; // CIR 0: 3 paths
az[1] = {0.2, -0.2, 0.1, -0.1}; // CIR 1: 4 paths
el[0] = {0.0, 0.0, 0.0};
el[1] = {0.05, -0.05, 0.0, 0.0};
powers[0] = {1.0, 1.0, 0.5};
powers[1] = {2.0, 1.0, 1.5, 0.5};
arma::vec as, es;
arma::mat orient;
quadriga_lib::calc_angular_spreads_sphere(az, el, powers, &as, &es, &orient);
// as(0), as(1) contain the azimuth spreads for each CIR
calc_cross_polarization_ratio - Calculate the cross-polarization ratio (XPR) for linear and circular polarization bases
-
Description:
- Computes the aggregate cross-polarization ratio (XPR) from the polarization transfer matrices
of all channel impulse responses (CIRs) using the total-power-ratio method (Option B).
- For each CIR, the total co-polarized and cross-polarized received powers are accumulated
across all qualifying paths, and the XPR is obtained as a single ratio of the totals.
- This method is physically meaningful: it corresponds to what a receiver antenna measures as
the ratio of co-polarized to cross-polarized energy across the entire channel impulse response.
- In addition to the linear V/H basis, the XPR is also computed in the circular LHCP/RHCP basis
by applying the unitary Jones matrix transformation M_circ = T M_lin T^-1.
- The LOS path is identified by comparing each path's absolute length against the direct
TX-RX distance
dTR. All paths with path_length < dTR + window_size are excluded from
the XPR calculation by default (controlled by include_los).
- The polarization transfer matrix
M is stored in column-major order with interleaved
real/imaginary parts: rows = [Re(M_vv), Im(M_vv), Re(M_hv), Im(M_hv), Re(M_vh), Im(M_vh),
Re(M_hh), Im(M_hh)], i.e., 8 rows per path.
M may or may not be normalized. Normalization does not affect the XPR since it cancels
in the ratio. However, it does affect the path gain output pg.
- If the total cross-polarized power is zero and co-polarized power is positive (perfect
polarization isolation), the XPR is set to infinity. If both are zero (no qualifying paths),
the XPR is set to 0.
-
Declaration:
template <typename dtype>
void quadriga_lib::calc_cross_polarization_ratio(
const std::vector<arma::Col<dtype>> &powers,
const std::vector<arma::Mat<dtype>> &M,
const std::vector<arma::Col<dtype>> &path_length,
const arma::Mat<dtype> &tx_pos,
const arma::Mat<dtype> &rx_pos,
arma::Mat<dtype> *xpr = nullptr,
arma::Col<dtype> *pg = nullptr,
bool include_los = false,
dtype window_size = 0.01);
-
Arguments:
const std::vector<arma::Col<dtype>> &powers (input)
Path powers in Watts. Vector of length [n_cir], each element is a column vector of length [n_path].
const std::vector<arma::Mat<dtype>> &M (input)
Polarization transfer matrices. Vector of length [n_cir], each element is a matrix of size [8, n_path]
with interleaved real/imaginary parts in column-major order.
const std::vector<arma::Col<dtype>> &path_length (input)
Absolute path length from TX to RX phase center in meters. Vector of length [n_cir],
each element is a column vector of length [n_path].
const arma::Mat<dtype> &tx_pos (input)
Transmitter position in Cartesian coordinates. Size [3, 1] (fixed TX) or [3, n_cir] (mobile TX).
const arma::Mat<dtype> &rx_pos (input)
Receiver position in Cartesian coordinates. Size [3, 1] (fixed RX) or [3, n_cir] (mobile RX).
arma::Mat<dtype> *xpr = nullptr (optional output)
Cross-polarization ratio in linear scale. Size [n_cir, 6] with columns:
0 = Aggregate linear XPR (total V+H co-pol / total V+H cross-pol),
| 1 = V-XPR ( |
M_vv |
^2 / |
M_hv |
^2, power-summed over paths),
|
| 2 = H-XPR ( |
M_hh |
^2 / |
M_vh |
^2, power-summed over paths),
|
3 = Aggregate circular XPR (total L+R co-pol / total L+R cross-pol),
| 4 = LHCP XPR ( |
M_LL |
^2 / |
M_RL |
^2, power-summed over paths),
|
| 5 = RHCP XPR ( |
M_RR |
^2 / |
M_LR |
^2, power-summed over paths). |
arma::Col<dtype> *pg = nullptr (optional output)
Total path gain computed over all paths (including LOS). Length [n_cir].
Calculated as the sum of powers[p] * ( |
M_vv |
^2 + |
M_hv |
^2 + |
M_vh |
^2 + |
M_hh |
^2) over all paths. |
bool include_los = false (input)
If true, include LOS and near-LOS paths in the XPR calculation.
If false (default), exclude paths with path_length < dTR + window_size.
dtype window_size = 0.01 (input)
LOS window size in meters. Paths within dTR + window_size of the direct path are excluded
from the XPR calculation when include_los is false. Default is 0.01 m (1 cm).
-
Example:
#include "quadriga_channel.hpp"
// Single CIR with 3 paths
arma::vec pw = {1.0, 0.5, 0.3};
arma::mat M(8, 3);
M.col(0) = {1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0}; // LOS: diagonal
M.col(1) = {0.9, 0.0, 0.1, 0.0, 0.1, 0.0, 0.8, 0.0}; // NLOS
M.col(2) = {0.7, 0.0, 0.2, 0.0, 0.15, 0.0, 0.6, 0.0}; // NLOS
arma::vec pl = {10.0, 12.0, 15.0};
arma::mat tx(3, 1); tx.col(0) = {0.0, 0.0, 0.0};
arma::mat rx(3, 1); rx.col(0) = {10.0, 0.0, 0.0};
std::vector<arma::vec> powers_vec = {pw};
std::vector<arma::mat> M_vec = {M};
std::vector<arma::vec> pl_vec = {pl};
arma::mat xpr;
arma::vec pg;
quadriga_lib::calc_cross_polarization_ratio(powers_vec, M_vec, pl_vec, tx, rx, &xpr, &pg);
// xpr has size [1, 6], pg has size [1]
calc_delay_spread - Calculate the RMS delay spread in [s]
-
Description:
- Computes the root-mean-square (RMS) delay spread from a given set of delays and corresponding
linear-scale powers for each channel impulse response (CIR).
- An optional power threshold in [dB] relative to the strongest path can be applied. Paths with
power below
p_max(dB) - threshold are excluded from the calculation.
- An optional granularity parameter in [s] groups paths in the delay domain. Powers of paths
falling into the same delay bin are summed before computing the delay spread. This is useful
when the system bandwidth limits the time resolution (e.g. 50 ns at 20 MHz bandwidth).
- When granularity is applied the function recursively calls itself on the binned power delay
profile.
- Optionally returns the mean delay for each CIR.
-
Declaration:
template <typename dtype>
arma::Col<dtype> quadriga_lib::calc_delay_spread(
const std::vector<arma::Col<dtype>> &delays,
const std::vector<arma::Col<dtype>> &powers,
dtype threshold = 100.0,
dtype granularity = 0.0,
arma::Col<dtype> *mean_delay = nullptr);
-
Arguments:
const std::vector<arma::Col<dtype>> &delays (input)
Delays in [s]. A vector of length n_cir, where each element is an Armadillo column vector
of length n_path (the number of paths may differ per CIR).
const std::vector<arma::Col<dtype>> &powers (input)
Path powers on a linear scale [W]. Same structure as delays.
dtype threshold = 100.0 (input)
Power threshold in [dB] relative to the strongest path. Paths with power below
max_power / 10^(0.1 * threshold) are excluded. Default: 100 dB (effectively all paths).
dtype granularity = 0.0 (input)
Window size in [s] for grouping paths in the delay domain. Paths whose delays fall into the
same bin of width granularity have their powers summed. Default: 0 (no grouping).
arma::Col<dtype> *mean_delay = nullptr (optional output)
Mean delay in [s] for each CIR. Length [n_cir].
-
Returns:
arma::Col<dtype> ds (output)
RMS delay spread in [s] for each CIR. Length [n_cir].
-
Example:
std::vector<arma::vec> delays = { {0.0, 1e-6, 2e-6} };
std::vector<arma::vec> powers = { {1.0, 0.5, 0.25} };
arma::vec mean_delay;
arma::vec ds = quadriga_lib::calc_delay_spread(delays, powers, 100.0, 0.0, &mean_delay);
// ds(0) ≈ 0.6901e-6, mean_delay(0) ≈ 0.5714e-6
calc_rician_k_factor - Calculate the Rician K-Factor from channel impulse response data
-
Description:
- The Rician K-Factor (KF) is defined as the ratio of signal power in the dominant line-of-sight
(LOS) path to the power in the scattered (non-line-of-sight, NLOS) paths.
- The LOS path is identified by matching the absolute path length with the direct distance between
TX and RX positions (
dTR).
- All paths arriving within
dTR + window_size are considered LOS and their power is summed.
- Paths arriving after
dTR + window_size are considered NLOS and their power is summed.
- If the total NLOS power is zero (i.e. no scattered paths), the K-Factor is set to infinity (
HUGE_VAL).
- If the total LOS power is zero (i.e. no LOS paths), the K-Factor is set to zero.
- The transmitter and receiver positions can be fixed (size
[3, 1]) or mobile (size [3, n_cir]).
Fixed positions are reused for all channel snapshots.
- Optional output
pg returns the total path gain (sum of all path powers) for each snapshot.
-
Declaration:
template <typename dtype>
void quadriga_lib::calc_rician_k_factor(
const std::vector<arma::Col<dtype>> &powers,
const std::vector<arma::Col<dtype>> &path_length,
const arma::Mat<dtype> &tx_pos,
const arma::Mat<dtype> &rx_pos,
arma::Col<dtype> *kf = nullptr,
arma::Col<dtype> *pg = nullptr,
dtype window_size = 0.01);
-
Arguments:
const std::vector<arma::Col<dtype>> &powers (input)
Path powers in Watts [W]. Vector of length n_cir, where each element is a column vector of
length n_path (number of paths may vary per snapshot).
const std::vector<arma::Col<dtype>> &path_length (input)
Absolute path lengths from TX to RX phase center in meters. Vector of length n_cir, where
each element is a column vector of length n_path matching the corresponding entry in powers.
const arma::Mat<dtype> &tx_pos (input)
Transmitter position in Cartesian coordinates [x; y; z]. Size [3, 1] for a fixed TX or
[3, n_cir] for a mobile TX.
const arma::Mat<dtype> &rx_pos (input)
Receiver position in Cartesian coordinates [x; y; z]. Size [3, 1] for a fixed RX or
[3, n_cir] for a mobile RX.
arma::Col<dtype> *kf = nullptr (optional output)
Rician K-Factor on linear scale. Length [n_cir].
arma::Col<dtype> *pg = nullptr (optional output)
Total path gain (sum of path powers). Length [n_cir].
dtype window_size = 0.01 (input)
LOS window size in meters. Paths with length ≤ dTR + window_size are considered LOS.
-
Example:
#include "quadriga_tools.hpp"
// Single snapshot with 3 paths
std::vector<arma::vec> powers(1), path_length(1);
powers[0] = {1.0, 0.5, 0.25}; // Path powers in W
path_length[0] = {10.0, 11.0, 12.0}; // Path lengths in m
arma::mat tx_pos(3, 1), rx_pos(3, 1);
tx_pos.col(0) = {0.0, 0.0, 0.0};
rx_pos.col(0) = {10.0, 0.0, 0.0}; // dTR = 10.0 m
arma::vec kf, pg;
quadriga_lib::calc_rician_k_factor(powers, path_length, tx_pos, rx_pos, &kf, &pg, 0.01);
// kf[0] = 1.0 / (0.5 + 0.25) = 1.333...
// pg[0] = 1.0 + 0.5 + 0.25 = 1.75
calc_rotation_matrix - Calculate rotation matrices from Euler angles
-
Description:
- Computes 3D rotation matrices from input Euler angles (bank, tilt, head).
- The result is returned in column-major order as a 3×3 matrix per input orientation vector.
- Calculations are internally performed in double precision for improved numerical accuracy, even if
dtype is float.
- Supports optional inversion of the y-axis and optional transposition of the output matrix.
- Allowed datatypes (
dtype): float or double
-
Declaration:
arma::Cube<dtype> quadriga_lib::calc_rotation_matrix(const arma::Cube<dtype> &orientation,
bool invert_y_axis = false, bool transposeR = false);
arma::Mat<dtype> quadriga_lib::calc_rotation_matrix(const arma::Mat<dtype> &orientation,
bool invert_y_axis = false, bool transposeR = false);
arma::Col<dtype> quadriga_lib::calc_rotation_matrix(const arma::Col<dtype> &orientation,
bool invert_y_axis = false, bool transposeR = false);
-
Arguments:
const arma::Cube<dtype> &orientation or const arma::Mat<dtype> &orientation or const arma::Col<dtype> &orientation (input)
Input Euler angles (bank, tilt, head) in radians, Size [3, n_row, n_col] or [3, n_mat] or Size [3].
bool invert_y_axis = false (optional input)
If true, the y-axis of the rotation is inverted. Default: false.
bool transposeR = false (optional input)
If true, the transpose of the rotation matrix is returned. Default: false.
-
Returns:
arma::Cube<dtype> or arma::Mat<dtype> or arma::Col<dtype>
Rotation matrices in column-major ordering. Size [9, n_row, n_col] or [9, n_mat] or [9].
-
Example:
arma::cube ori(3, 1, 1);
ori(0, 0, 0) = 0.0; // bank
ori(1, 0, 0) = 0.0; // tilt
ori(2, 0, 0) = 1.5708; // head
auto R = quadriga_lib::calc_rotation_matrix(ori);
cart2geo - Convert Cartesian coordinates to geographic coordinates (azimuth, elevation, distance)
-
Description:
- Transforms 3D Cartesian coordinates
(x, y, z) into geographic coordinates:
- Azimuth angle [rad]
- Elevation angle [rad]
- Distance (vector norm)
- Azimuth is measured in the x-y plane from the x-axis; elevation is from the x-y plane upward.
- Allowed datatypes (
dtype): float or double
-
Declaration:
arma::Cube<dtype> quadriga_lib::cart2geo(const arma::Cube<dtype> &cart);
arma::Mat<dtype> quadriga_lib::cart2geo(const arma::Mat<dtype> &cart);
arma::Col<dtype> quadriga_lib::cart2geo(const arma::Col<dtype> &cart);
-
Arguments:
const arma::Cube<dtype> cart or const arma::Mat<dtype> cart or const arma::Col<dtype> cart (input)
Cartesian coordinate vectors (x, y, z), Size [3, n_row, n_col] or [3, n_row] or [3].
-
Returns:
arma::Cube<dtype> or arma::Mat<dtype> or arma::Col<dtype>
Geographic coordinate vectors (azimuth, elevation, distance), Size [n_row, n_col, 3] or [n_row, 3] or [3].
-
Example:
arma::vec cart = {1.0, 1.0, 1.0};
auto geo = quadriga_lib::cart2geo(cart);
colormap - Generate colormap
-
Description:
- Returns a 64x3 or 256x3 colormap matrix with RGB values in unsigned char format.
- Each row corresponds to an RGB color entry of the selected colormap.
- Useful for visualization purposes (e.g., heatmaps or 3D rendering).
- Available color maps include:
jet, parula, winter, hot, turbo, copper, spring, cool, gray, autumn, summer.
-
Declaration:
arma::uchar_mat quadriga_lib::colormap(std::string map, bool high_res = false)
-
Arguments:
std::string map (input)
Name of the desired colormap. Must be one of:
"jet", "parula", "winter", "hot", "turbo", "copper", "spring", "cool", "gray", "autumn", "summer".
bool high_res (input)
Enables 256 color steps
-
Returns:
arma::uchar_mat
A matrix of size [64 x 3] or [256 x 3] containing RGB color values as unsigned chars in the range [0, 255].
-
Example:
arma::uchar_mat cm = quadriga_lib::colormap("turbo");
geo2cart - Transform geographic (azimuth, elevation, length) to Cartesian coordinates
-
Description:
- Converts azimuth and elevation angles (in radians) into 3D Cartesian coordinates.
- Optional radial distance (
length) can be provided; otherwise, unit vectors are returned.
- Useful for converting spherical direction data into vector representations.
- Allowed datatypes (
dtype): float or double
-
Declaration:
arma::Cube<dtype> quadriga_lib::geo2cart(
const arma::Mat<dtype> &azimuth,
const arma::Mat<dtype> &elevation,
const arma::Mat<dtype> &length = {})
-
Arguments:
const arma::Mat<dtype> azimuth (input)
Azimuth angles in radians. Size [n_row, n_col].
const arma::Mat<dtype> elevation (input)
Elevation angles in radians. Size [n_row, n_col].
const arma::Mat<dtype> length = {} (optional input)
Radial distance (length). Same size as azimuth/elevation or empty for unit vectors. Size [n_row, n_col] or [0, 0].
-
Returns:
arma::Cube<dtype> (output)
Cartesian coordinates with dimensions [3, n_row, n_col], representing (x, y, z) for each input direction.
-
Example:
arma::mat az(2, 2), el(2, 2), len(2, 2, arma::fill::ones);
auto cart = quadriga_lib::geo2cart(az, el, len);
interp_1d / interp_2d - Perform linear interpolation (1D or 2D) on single or multiple data sets.
-
Description:
- Interpolates given input data at specified output points.
- Supports single and multiple data sets.
- Returns interpolated results either directly or through reference argument.
- Data types (
dtype): float or double
-
Declarations:
void interp_2D(const arma::Cube<dtype> &input, const arma::Col<dtype> &xi, const arma::Col<dtype> &yi,
const arma::Col<dtype> &xo, const arma::Col<dtype> &yo, arma::Cube<dtype> &output);
arma::Cube<dtype> interp_2D(const arma::Cube<dtype> &input, const arma::Col<dtype> &xi, const arma::Col<dtype> &yi,
const arma::Col<dtype> &xo, const arma::Col<dtype> &yo);
arma::Mat<dtype> interp_2D(const arma::Mat<dtype> &input, const arma::Col<dtype> &xi, const arma::Col<dtype> &yi,
const arma::Col<dtype> &xo, const arma::Col<dtype> &yo);
arma::Mat<dtype> interp_1D(const arma::Mat<dtype> &input, const arma::Col<dtype> &xi, const arma::Col<dtype> &xo);
arma::Col<dtype> interp_1D(const arma::Col<dtype> &input, const arma::Col<dtype> &xi, const arma::Col<dtype> &xo);
-
Arguments:
input: Input data array/matrix (size details below)
xi: Input x-axis sampling points, vector of length nx
yi: Input y-axis sampling points (for 2D only), vector of length ny
xo: Output x-axis sampling points, vector of length mx
yo: Output y-axis sampling points (for 2D only), vector of length my
output: Interpolated data cube (modified in-place for one variant)
-
Input / Output size details:
- 2D interpolation of multiple datasets (
arma::Cube):
Input size: [ny, nx, ne], Output size: [my, mx, ne]
- 2D interpolation of single dataset (
arma::Mat):
Input size: [ny, nx], Output size: [my, mx]
- 1D interpolation of multiple datasets (
arma::Mat):
Input size: [nx, ne], Output size: [mx, ne]
- 1D interpolation of single dataset (
arma::Col):
Input length: [nx], Output length: [mx]
-
Examples:
- 2D interpolation example:
arma::cube input(5, 5, 2, arma::fill::randu); // example input data
arma::vec xi = arma::linspace(0, 4, 5);
arma::vec yi = arma::linspace(0, 4, 5);
arma::vec xo = arma::linspace(0, 4, 10);
arma::vec yo = arma::linspace(0, 4, 10);
arma::cube output;
quadriga_lib::interp_2D(input, xi, yi, xo, yo, output);
- 1D interpolation example:
arma::vec input = arma::linspace(0, 1, 5);
arma::vec xi = arma::linspace(0, 4, 5);
arma::vec xo = arma::linspace(0, 4, 10);
auto output = quadriga_lib::interp_1D(input, xi, xo);
write_png - Write data to a PNG file
-
Description:
- Converts input data into a color-coded PNG file for visualization
- Support optional selection of a colormap, as well a minimum and maximum value limits
- Allowed datatypes (
dtype): float or double
- Uses the LodePNG library for PNG writing
-
Declaration:
void write_png( const arma::Mat<dtype> &data,
std::string fn,
std::string colormap = "jet",
dtype min_val = NAN,
dtype max_val = NAN,
bool log_transform = false);
-
Arguments:
const arma::Mat<dtype> &data
Data matrix
std::string fn
Path to the .png file to be written
std::string colormap
Name of the desired colormap. Must be one of:
"jet", "parula", "winter", "hot", "turbo", "copper", "spring", "cool", "gray", "autumn", "summer".
dtype min_val
Minimum value. Values below this value will have be encoded with the color of the smallest value.
If NAN is provided (default), the lowest values is determined from the data.
dtype max_val
Maximum value. Values above this value will have be encoded with the color of the largest value.
If NAN is provided (default), the largest values is determined from the data.
bool log_transform
If enabled, the data values are transformed to the log-domain (10*log10(data)) before processing.
Default: false (disabled)
-
See also:
Site-Specific Simulation Tools
calc_diffraction_gain - Calculate diffraction gain for multiple transmit and receive positions
-
Description:
Diffraction refers to the phenomenon where waves bend or interfere around the edges of an obstacle,
extending into the region that would otherwise be in the obstacle's geometrical shadow. The object
causing the diffraction acts as a secondary source for the wave's propagation. A specific example of
this is the knife-edge effect, or knife-edge diffraction, where a sharp, well-defined obstacle—like
a mountain range or a building wall—partially truncates the incident radiation.
To estimate the diffraction gain in a three-dimensional space, one can assess the extent to which the
Fresnel ellipsoid is obstructed by objects, and then evaluate the impact of this obstruction on the
received power. This method presupposes that diffracted waves travel along slightly varied paths
before arriving at a receiver. These waves may reach the receiver out of phase with the primary wave
due to their different travel lengths, leading to either constructive or destructive interference.
The process of estimating the gain involves dividing the wave propagation from a transmitter to a
receiver into n_path paths. These paths are represented by elliptic arcs, which are further
approximated using n_seg line segments. Each segment can be individually blocked or attenuated
by environmental objects. To determine the overall diffraction gain, a weighted sum of these
individual path contributions is calculated. The weighting is adjusted to align with the uniform
theory of diffraction (UTD) coefficients in two dimensions, but the methodology is adapted for
any 3D object shape.
- This function computes the diffraction gain between multiple transmit and receive positions using a 3D triangular mesh.
- Supports accelerated computation via mesh segmentation: large meshes can be split into smaller sub-meshes to skip irrelevant geometry.
- Optionally returns the gain per link and the coordinates along the diffracted path.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
void quadriga_lib::calc_diffraction_gain(
const arma::Mat<dtype> *orig,
const arma::Mat<dtype> *dest,
const arma::Mat<dtype> *mesh,
const arma::Mat<dtype> *mtl_prop,
dtype center_frequency,
int lod = 2,
arma::Col<dtype> *gain = nullptr,
arma::Cube<dtype> *coord = nullptr,
int verbose = 0,
const arma::u32_vec *sub_mesh_index = nullptr);
-
Arguments:
const arma::Mat<dtype> *orig (input)
Matrix of origin (transmitter) points, size [n_pos, 3].
const arma::Mat<dtype> *dest (input)
Matrix of destination (receiver) points, size [n_pos, 3].
const arma::Mat<dtype> *mesh (input)
Triangular mesh geometry. Each row contains 3 vertices, flattened as [X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3], size [no_mesh, 9].
const arma::Mat<dtype> *mtl_prop (input)
Material properties per triangle, size [no_mesh, 5], See obj_file_read for details
dtype center_frequency (input)
Center frequency in Hz used for diffraction calculations.
arma::Col<dtype> *gain (output, optional)
Calculated diffraction gain in linear scale, vector of size [n_pos].
arma::Cube<dtype> *coord (output, optional)
Coordinates of the diffracted path, size [3, n_seg-1, n_pos]. Includes one point per segment excluding endpoints.
int verbose = 0 (optional input)
Verbosity level for debug and diagnostic output. Higher values enable more log messages. Default: 0.
const arma::u32_vec *sub_mesh_index (input, optional)
Optional sub-mesh indexing for acceleration. Vector mapping triangles to sub-meshes, length [no_mesh].
-
Technical Notes:
- If
sub_mesh_index is provided, spatial bounding boxes for each sub-mesh are automatically computed.
Any TX-RX path not intersecting a bounding box is excluded from detailed edge evaluation.
- The diffraction algorithm supports multiple edges and considers the geometric configuration and material properties of each triangle.
- The LOD parameter controls both accuracy and computational cost. Lower values use simplified heuristics;
higher values may result in significantly more computation time.
- Each ellipsoid consists of
n_path diffraction paths. The number of paths is determined by the
level of detail (lod). See generate_diffraction_paths for details
-
See also:
combine_irs_coord - Combine path interaction coordinates for channels with intelligent reflective surfaces (IRS)
-
Description:
- Merges two propagation segments — (1) TX → IRS and (2) IRS → RX — into complete TX → RX paths via an IRS.
- Interaction coordinates of both segments are stored in a compressed format where
no_interact is a vector of
length n_path storing the number of interactions per path and interact_coord stores all
interaction coordinates in path order.
- Each combined path includes interaction points from both segments, optionally reversed for each segment.
- The number of output paths
n_path_irs is at most n_path_1 × n_path_2, but can be reduced by specifying active_path.
- Output includes the number and coordinates of interaction points per IRS-reflected path.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
void quadriga_lib::combine_irs_coord(
dtype Ix, dtype Iy, dtype Iz,
const arma::u32_vec *no_interact_1,
const arma::Mat<dtype> *interact_coord_1,
const arma::u32_vec *no_interact_2,
const arma::Mat<dtype> *interact_coord_2,
arma::u32_vec *no_interact,
arma::Mat<dtype> *interact_coord,
bool reverse_segment_1 = false,
bool reverse_segment_2 = false,
const std::vector<bool> *active_path = nullptr);
-
Arguments:
dtype Ix, Iy, Iz (input)
IRS position in Cartesian coordinates [m].
const arma::u32_vec *no_interact_1 (input)
Number of interaction points in segment 1 (TX → IRS), vector of length [n_path_1]
const arma::Mat<dtype> *interact_coord_1 (input)
Interaction coordinates for segment 1, matrix of size [3, sum(no_interact_1)].
const arma::u32_vec *no_interact_2 (input)
Number of interaction points in segment 2 (IRS → RX), vector of length [n_path_2]
const arma::Mat<dtype> *interact_coord_2 (input)
Interaction coordinates for segment 2, matrix of size [3, sum(no_interact_2)].
arma::u32_vec *no_interact (output)
Combined number of interaction points per IRS-based path, vector of length [n_path_irs].
arma::Mat<dtype> *interact_coord (output)
Combined interaction coordinates, matrix of size [3, sum(no_interact)].
bool reverse_segment_1 = false (optional input)
If true, reverses the interaction coordinates of segment 1. TX and IRS positions are not swapped. Default: false.
bool reverse_segment_2 = false (optional input)
If true, reverses the interaction coordinates of segment 2. IRS and RX positions are not swapped. Default: false.
const std::vector<bool> *active_path = nullptr (optional input)
Optional mask vector of length [n_path_1 × n_path_2]. If provided, only paths with true are combined.
This is generated as the output of get_channels_irs
-
Technical Notes:
- Paths are created by appending interaction coordinates from both segments. Reversing only affects the order of these coordinates.
- Output matrix
interact_coord is built sequentially and should be preallocated only if the total number of interaction points is known in advance.
- The function supports sparsely activated paths via
active_path, this is generated as a return value of get_channels_irs
and filters out paths that have very little power after being reflected by the IRS.
- This function is typically used as a complementary step to delay or coefficient calculations involving IRS-based channels.
It calculated the required data for visualizing paths, e.g. in Blender or other visualization tools.
-
See also:
- get_channels_irs (for computing IRS channels)
- coord2path (for processing coordinates, calculating departure and arrival angels, etc.)
coord2path - Convert path interaction coordinates into FBS/LBS positions, path length and angles
-
Description:
- Interaction coordinates can be stored in a compressed format where
no_interact is a vector of
length n_path storing the number of interactions per path and interact_coord stores all
interaction coordinates in path order.
- This function processes the interaction coordinates of to extract relevant propagation metrics.
- Calculates absolute path lengths and first/last bounce scatterer positions and angles.
- LOS paths are assigned a virtual FBS/LBS position at the midpoint between TX and RX.
- Automatically resizes output arguments if necessary.
- Optionally reverses TX and RX to simulate the reverse link.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::coord2path(
dtype Tx, dtype Ty, dtype Tz,
dtype Rx, dtype Ry, dtype Rz,
const arma::u32_vec *no_interact,
const arma::Mat<dtype> *interact_coord,
arma::Col<dtype> *path_length,
arma::Mat<dtype> *fbs_pos,
arma::Mat<dtype> *lbs_pos,
arma::Mat<dtype> *path_angles,
std::vector<arma::Mat<dtype>> *path_coord,
bool reverse_path);
-
Arguments:
dtype Tx, Ty, Tz (input)
Transmitter position in Cartesian coordinates in [m]
dtype Rx, Ry, Rz (input)
Receiver position in Cartesian coordinates in [m]
const arma::u32_vec *no_interact (input)
Vector of length n_path indicating the number of interactions per path (0 = LOS)
const arma::Mat<dtype> *interact_coord (input)
Matrix of size [3, sum(no_interact)] containing interaction coordinates in path order
arma::Col<dtype> *path_length (output, optional)
Absolute path lengths from TX to RX, Length [n_path]
arma::Mat<dtype> *fbs_pos (output, optional)
First-bounce scatterer positions, Size [3, n_path]; For LOS, set to midpoint between TX and RX
arma::Mat<dtype> *lbs_pos (output, optional)
Last-bounce scatterer positions, Size [3, n_path]. For LOS, set to midpoint between TX and RX
arma::Mat<dtype> *path_angles (output, optional)
Departure and arrival angles: columns {AOD, EOD, AOA, EOA}, size [n_path, 4]
std::vector<arma::Mat<dtype>> *path_coord (output, optional)
Full path coordinates (including TX and RX), vector of size n_path with each entry of size [3, n_interact+2]
bool reverse_path = false (optional input)
If true, swaps TX and RX and reverses all interaction sequences.
-
Example:
// Suppose we have two paths: one LOS, one single-bounce.
// Path 0: LOS (no_interact[0] = 0)
// Path 1: single bounce at {5,0,2}.
arma::u32_vec no_int = {0, 1};
arma::mat interact(3, 1);
interact.col(0) = {5.0, 0.0, 2.0};
arma::vec lengths;
arma::mat fbs, lbs;
arma::mat angles;
std::vector<arma::mat> coords;
quadriga_lib::coord2path<double>(
0.0, 0.0, 0.0, // TX at origin
10.0, 0.0, 0.0, // RX at x = 10 m
&no_int, &interact, &lengths, &fbs, &lbs, &angles, &coords);
// After the call:
// lengths: [10.0, 10.77]
// fbs: Path 1 [5,0,0], Path 2 [5,0,2]
// lbs: Path 1 [5,0,0], Path 2 [5,0,2]
// angles: Path 1 [0, 0, pi, p], Path 2 [0, 22°, pi, 22°]
// coords[0]: [ [0,0,0], [10,0,0] ]
// coords[1]: [ [0,0,0], [5,0,2], [10,0,0] ]
generate_diffraction_paths - Generate propagation paths for estimating the diffraction gain
-
Description:
This function generates the elliptic propagation paths and corresponding weights necessary for the
calculation of the diffraction gain in calc_diffraction_gain.
-
Caveat:
- Each ellipsoid consists of
n_path diffraction paths. The number of paths is determined by the
level of detail (lod).
- All diffraction paths of an ellipsoid originate at
orig and arrive at dest
- Each diffraction path has
n_seg segments
- Points
orig and dest lay on the semi-major axis of the ellipsoid
- The generated rays sample the volume of the ellipsoid
- Weights are calculated from the Knife-edge diffraction model when parts of the ellipsoid are shadowed
- Initial weights are normalized such that
sum(prod(weights,3),2) = 1
- Inputs
orig and dest may be provided as double or single precision
- Supported datatypes
dtype are float or double
-
Declaration:
void generate_diffraction_paths(
const arma::Mat<dtype> *orig,
const arma::Mat<dtype> *dest,
dtype center_frequency,
int lod,
arma::Cube<dtype> *ray_x,
arma::Cube<dtype> *ray_y,
arma::Cube<dtype> *ray_z,
arma::Cube<dtype> *weight);
-
Arguments:
const arma::Mat<dtype> *orig (input)
Pointer to Armadillo matrix containing the origin points of the propagation ellipsoid (e.g.
transmitter positions). Size: [ n_pos, 3 ]
const arma::Mat<dtype> *dest (input)
Pointer to Armadillo matrix containing the destination point of the propagation ellipsoid (e.g.
receiver positions). Size: [ n_pos, 3 ]
dtype center_frequency (input)
The center frequency in [Hz], scalar, default = 299792458 Hz
int lod (input)
Level of detail, scalar value
lod = 1 |
results in n_path = 7 and n_seg = 3 |
lod = 2 |
results in n_path = 19 and n_seg = 3 |
lod = 3 |
results in n_path = 37 and n_seg = 4 |
lod = 4 |
results in n_path = 61 and n_seg = 5 |
lod = 5 |
results in n_path = 1 and n_seg = 2 (for debugging) |
lod = 6 |
results in n_path = 2 and n_seg = 2 (for debugging) |
arma::Cube<dtype> *ray_x (output)
Pointer to an Armadillo cube for the x-coordinates of the generated rays; Size: [ n_pos, n_path, n_seg-1 ]
Size will be adjusted if not set correctly.
arma::Cube<dtype> *ray_y (output)
Pointer to an Armadillo cube for the y-coordinates of the generated rays; Size: [ n_pos, n_path, n_seg-1 ]
Size will be adjusted if not set correctly.
arma::Cube<dtype> *ray_z (output)
Pointer to an Armadillo cube for the z-coordinates of the generated rays; Size: [ n_pos, n_path, n_seg-1 ]
Size will be adjusted if not set correctly.
arma::Cube<dtype> *weight (output)
Pointer to an Armadillo cube for the weights; Size: [ n_pos, n_path, n_seg ]
Size will be adjusted if not set correctly.
-
See also:
icosphere - Construct a geodesic polyhedron (icosphere) from triangles
-
Description:
- Generates a convex polyhedral surface (icosphere) made entirely of triangles, based on recursive subdivision of an icosahedron.
- Useful for sampling directions uniformly on a sphere, for applications like ray tracing, antenna pattern evaluation, or spatial grids.
- Each triangular face points outward from the center and has an associated normal.
- Optionally returns vertex directions and vector lengths in either Cartesian or spherical coordinates.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
arma::uword quadriga_lib::icosphere(
arma::uword n_div,
dtype radius,
arma::Mat<dtype> *center,
arma::Col<dtype> *length = nullptr,
arma::Mat<dtype> *vert = nullptr,
arma::Mat<dtype> *direction = nullptr,
bool direction_xyz = false);
-
Arguments:
arma::uword n_div (input)
Number of subdivisions per triangle edge. The total number of faces will be n_faces = 20 × n_div².
dtype radius (input)
Radius of the icosphere in meters. All triangle vertices lie on this sphere.
arma::Mat<dtype> *center (output)
Unit vectors pointing from the origin to the center of each triangle face. Usually a bit shorter than radius, Size [n_faces, 3].
arma::Col<dtype> *length = nullptr (optional output)
Vector magnitudes of each center vector (usually slightly less than radius). Vector size [n_faces].
arma::Mat<dtype> *vert = nullptr (optional output)
Vertex vectors from each triangle’s center to its three vertices, flattened as [x1, y1, z1, x2, y2, z2, x3, y3, z3]. Size [n_faces, 9].
arma::Mat<dtype> *direction = nullptr (optional output)
Direction vectors of the three triangle edges. Format depends on direction_xyz: If false
(spherical): [v1az, v1el, v2az, v2el, v3az, v3el], If true (Cartesian): [v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z],
Size [n_faces, 6] or [n_faces, 9].
bool direction_xyz = false (optional input)
If true, output directions in Cartesian coordinates. If false, output in spherical azimuth/elevation. Default: false.
-
Returns:
arma::uword
The number of triangular faces generated: n_faces = 20 × n_div².
-
Technical Notes:
- The generated mesh is well-suited for uniform angular sampling on a sphere.
- Triangle vertices are calculated relative to the center to ensure they lie on the desired sphere.
- The radius parameter scales the final structure without changing angular spacing.
-
Example:
arma::fmat center, vert, direction;
arma::fvec length;
// 4 subdivisions → 320 faces, map to unit sphere, output Cartesian directions
auto n = quadriga_lib::icosphere<float>(4, 1.0, ¢er, &length, &vert, &direction, true);
mitsuba_xml_file_write - Write geometry and material data to a Mitsuba 3 XML scene file.
-
Description:
This routine converts a triangular surface mesh stored in quadriga-lib data structures into the
XML format understood by Mitsuba 3 www.mitsuba-renderer.org.
The generated file can be loaded directly by NVIDIA Sionna RT for differentiable radio-propagation
simulations.
- Converts a 3D geometry mesh into Mitsuba 3 XML format for use with rendering tools.
- Enables exporting models from
quadriga-lib to be used with Mitsuba 3 or Sionna RT:
- Mitsuba 3: Research-oriented retargetable rendering system.
- NVIDIA Sionna: Hardware-accelerated differentiable ray tracer for wireless propagation, built on Mitsuba 3.
- Supports grouping faces into named objects and assigning materials by name.
- Optionally maps materials to ITU default presets used by Sionna RT.
- Allowed datatypes (
dtype): float or double
-
Declaration:
void quadriga_lib::mitsuba_xml_file_write(
const std::string &fn,
const arma::Mat<dtype> &vert_list,
const arma::umat &face_ind,
const arma::uvec &obj_ind,
const arma::uvec &mtl_ind,
const std::vector<std::string> &obj_names,
const std::vector<std::string> &mtl_names,
const arma::Mat<dtype> &bsdf = {},
bool map_to_itu_materials = false);
-
Arguments:
const std::string fn (input)
Output file name (including path and .xml extension).
const arma::Mat<dtype> vert_list (input)
Vertex list, size [n_vert, 3], each row is a vertex (x, y, z) in Cartesian coordinates [m].
const arma::umat face_ind (input)
Face indices (0-based), size [n_mesh, 3], each row defines a triangle via vertex indices.
const arma::uvec obj_ind (input)
Object indices (1-based), size [n_mesh]. Assigns each triangle to an object.
const arma::uvec mtl_ind (input)
Material indices (1-based), size [n_mesh]. Assigns each triangle to a material.
const std::vector<std::string> obj_names (input)
Names of objects. Length must be equal to max(obj_ind).
const std::vector<std::string> mtl_names (input)
Names of materials. Length must be equal to max(mtl_ind).
const arma::Mat<dtype> bsdf = {} (optional input)
Material reflectivity data (BSDF parameters), size [mtl_names.size(), 17]. If omitted, the null BSDF is used.
Note that Sionna RT ignores all BSDF parameters. They are only used by the Mitsuma rendering system.
See obj_file_read for a definition of the data fields.
bool map_to_itu_materials = false (optional input)
If true, maps material names to ITU-defined presets used by Sionna RT. Default: false
-
See also:
obj_file_read - Read Wavefront `.obj` file and extract geometry and material information
-
Description:
- Parses a Wavefront
.obj file containing triangularized 3D geometry.
- Extracts triangle face data, material properties, vertex indices, and optional metadata such as object/material names.
- Multiple triangles belonging to the same object are grouped together by
obj_ind.
- Supports default and custom ITU-compliant materials encoded via the
usemtl tag.
- Automatically resizes output matrices/vectors as needed to match the file content.
- Returns the number of triangular mesh elements found in the file.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
arma::uword quadriga_lib::obj_file_read(
std::string fn,
arma::Mat<dtype> *mesh = nullptr,
arma::Mat<dtype> *mtl_prop = nullptr,
arma::Mat<dtype> *vert_list = nullptr,
arma::umat *face_ind = nullptr,
arma::uvec *obj_ind = nullptr,
arma::uvec *mtl_ind = nullptr,
std::vector<std::string> *obj_names = nullptr,
std::vector<std::string> *mtl_names = nullptr,
arma::Mat<dtype> *bsdf = nullptr,
const std::string &materials_csv = "");
-
Arguments:
std::string fn (input)
Path to the .obj file to be read.
arma::Mat<dtype> *mesh = nullptr (optional output)
Flattened triangle mesh data. Each row holds [X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3]. Size: [n_mesh, 9].
arma::Mat<dtype> *mtl_prop = nullptr (optional output)
Material properties for each triangle. Size: [n_mesh, 5].
arma::Mat<dtype> *vert_list = nullptr (optional output)
List of all vertex positions found in the .obj file. Size: [n_vert, 3].
arma::umat *face_ind = nullptr (optional output)
Indices into vert_list for each triangle (0-based). Size: [n_mesh, 3].
arma::uvec *obj_ind = nullptr (optional output)
Object index (1-based) for each triangle. Size: [n_mesh].
arma::uvec *mtl_ind = nullptr (optional output)
Material index (1-based) for each triangle. Size: [n_mesh].
std::vector<std::string> *obj_names = nullptr (optional output)
Names of objects found in the file. Length: max(obj_ind).
std::vector<std::string> *mtl_names = nullptr (optional output)
Names of materials found in the file. Length: max(mtl_ind).
arma::Mat<dtype> *bsdf = nullptr (optional output)
Principled BSDF (Bidirectional Scattering Distribution Function) values extracted from the
.MTL file. Size [mtl_names.size(), 17]. Values are:
| 0 |
Base Color Red |
Range 0-1 |
Default = 0.8 |
| 1 |
Base Color Green |
Range 0-1 |
Default = 0.8 |
| 2 |
Base Color Blue |
Range 0-1 |
Default = 0.8 |
| 3 |
Transparency (alpha) |
Range 0-1 |
Default = 1.0 (fully opaque) |
| 4 |
Roughness |
Range 0-1 |
Default = 0.5 |
| 5 |
Metallic |
Range 0-1 |
Default = 0.0 |
| 6 |
Index of refraction (IOR) |
Range 0-4 |
Default = 1.45 |
| 7 |
Specular Adjustment to the IOR |
Range 0-1 |
Default = 0.5 (no adjustment) |
| 8 |
Emission Color Red |
Range 0-1 |
Default = 0.0 |
| 9 |
Emission Color Green |
Range 0-1 |
Default = 0.0 |
| 10 |
Emission Color Blue |
Range 0-1 |
Default = 0.0 |
| 11 |
Sheen |
Range 0-1 |
Default = 0.0 |
| 12 |
Clearcoat |
Range 0-1 |
Default = 0.0 |
| 13 |
Clearcoat roughness |
Range 0-1 |
Default = 0.0 |
| 14 |
Anisotropic |
Range 0-1 |
Default = 0.0 |
| 15 |
Anisotropic rotation |
Range 0-1 |
Default = 0.0 |
| 16 |
Transmission |
Range 0-1 |
Default = 0.0 |
std::string materials_csv (optional input)
Path to optional CSV file containing custom material properties. If empty, default ITU-R P.2040-3
materials are used. CSV format: Header row with columns 'name', 'a', 'b', 'c', 'd', 'att' (order can vary).
Each row defines a material with: name (string), electromagnetic parameters a,b,c,d (doubles),
and additional attenuation att (dB). Relative permittivity: eta = a * f_GHz^b; Conductivity:
sigma = c * f_GHz^d
-
Returns:
arma::uword
Number of mesh triangles found in the file (n_mesh).
-
Technical Notes:
- Unknown or missing materials default to
"vacuum" (ε_r = 1, σ = 0).
- Materials are applied per triangle via the
usemtl tag in the .obj file.
- Input geometry must be fully triangulated—quads and n-gons are not supported.
- File parsing is case-sensitive for material names.
-
Material Tag Format:
- Default materials (ITU-R P.2040-3 Table 3):
"usemtl itu_concrete", "itu_brick", "itu_wood", "itu_water", etc.
- Frequency range: 1–40 GHz (limited to 1–10 GHz for ground materials)
- Custom materials syntax:
"usemtl Name::A:B:C:D:att" with A, B: Real permittivity ε_r = A * fGHz^B,
C, D: Conductivity σ = C * fGHz^D, att: Penetration loss in dB (fixed, per interaction)
-
Material properties:
Each material is defined by its electrical properties. Radio waves that interact with a building will
produce losses that depend on the electrical properties of the building materials, the material
structure and the frequency of the radio wave. The fundamental quantities of interest are the electrical
permittivity (ϵ) and the conductivity (σ). A simple regression model for the frequency dependence is
obtained by fitting measured values of the permittivity and the conductivity at a number of frequencies.
The five parameters returned in mtl_prop then are:
- Real part of relative permittivity at f = 1 GHz (a)
- Frequency dependence of rel. permittivity (b) such that ϵ = a · f^b
- Conductivity at f = 1 GHz (c)
- Frequency dependence of conductivity (d) such that σ = c· f^d
- Fixed attenuation in dB applied to each transition
A more detailed explanation together with a derivation can be found in ITU-R P.2040. The following
list of material is currently supported and the material can be selected by using the usemtl tag
in the OBJ file. When using Blender, the simply assign a material with that name to an object or face.
The following materials are defined by default:
| Name |
a |
b |
c |
d |
Att |
max fGHz |
|
| vacuum / air |
1.0 |
0.0 |
0.0 |
0.0 |
0.0 |
100 |
|
| textiles |
1.5 |
0.0 |
5e-5 |
0.62 |
0.0 |
100 |
|
| plastic |
2.44 |
0.0 |
2.33e-5 |
1.0 |
0.0 |
100 |
|
| ceramic |
6.5 |
0.0 |
0.0023 |
1.32 |
0.0 |
100 |
|
| sea_water |
80.0 |
-0.25 |
4.0 |
0.58 |
0.0 |
100 |
|
| sea_ice |
3.2 |
-0.022 |
1.1 |
1.5 |
0.0 |
100 |
|
| water |
80.0 |
-0.18 |
0.6 |
1.52 |
0.0 |
20 |
|
| water_ice |
3.17 |
-0.005 |
5.6e-5 |
1.7 |
0.0 |
20 |
|
| itu_concrete |
5.24 |
0.0 |
0.0462 |
0.7822 |
0.0 |
100 |
|
| itu_brick |
3.91 |
0.0 |
0.0238 |
0.16 |
0.0 |
40 |
|
| itu_plasterboard |
2.73 |
0.0 |
0.0085 |
0.9395 |
0.0 |
100 |
|
| itu_wood |
1.99 |
0.0 |
0.0047 |
1.0718 |
0.0 |
100 |
|
| itu_glass |
6.31 |
0.0 |
0.0036 |
1.3394 |
0.0 |
100 |
|
| itu_ceiling_board |
1.48 |
0.0 |
0.0011 |
1.075 |
0.0 |
100 |
|
| itu_chipboard |
2.58 |
0.0 |
0.0217 |
0.78 |
0.0 |
100 |
|
| itu_plywood |
2.71 |
0.0 |
0.33 |
0.0 |
0.0 |
40 |
|
| itu_marble |
7.074 |
0.0 |
0.0055 |
0.9262 |
0.0 |
60 |
|
| itu_floorboard |
3.66 |
0.0 |
0.0044 |
1.3515 |
0.0 |
100 |
|
| itu_metal |
1.0 |
0.0 |
1.0e7 |
0.0 |
0.0 |
100 |
|
| itu_very_dry_ground |
3.0 |
0.0 |
0.00015 |
2.52 |
0.0 |
10 |
|
| itu_medium_dry_ground |
15.0 |
-0.1 |
0.035 |
1.63 |
0.0 |
10 |
|
| itu_wet_ground |
30.0 |
-0.4 |
0.15 |
1.3 |
0.0 |
10 |
|
| itu_vegetation |
1.0 |
0.0 |
1.0e-4 |
1.1 |
0.0 |
100 |
|
| irr_glass |
6.27 |
0.0 |
0.0043 |
1.1925 |
23.0 |
100 |
|
-
Example:
arma::mat mesh, mtl_prop, vert_list;
arma::umat face_ind;
arma::uvec obj_ind, mtl_ind;
std::vector<std::string> obj_names, mtl_names;
quadriga_lib::obj_file_read<double>("cube.obj", &mesh, &mtl_prop, &vert_list, &face_ind, &obj_ind, &mtl_ind, &obj_names, &mtl_names);
obj_overlap_test - Detect overlapping 3D objects in a triangular mesh
-
Description:
- Tests whether any objects in a triangular mesh overlap by checking for shared volume or intersection.
- Touching faces or edges are not considered overlapping
- Returns the indices (1-based) of all objects that intersect with at least one other object.
- Can optionally output a list of overlap reasons for diagnostic purposes.
- Uses a configurable geometric tolerance to account for numerical precision.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
arma::uvec quadriga_lib::obj_overlap_test(
const arma::Mat<dtype> *mesh,
const arma::uvec *obj_ind,
std::vector[std::string](std::string) *reason = nullptr,
dtype tolerance = 0.0005);
-
Arguments:
const arma::Mat<dtype> *mesh (input)
Triangular mesh geometry. Each row contains 3 vertices flattened as [X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3]. Size: [n_mesh, 9].
const arma::uvec *obj_ind (input)
Object indices (1-based) that map multiple triangles in mesh to objects; Size: [n_mesh];
This is an output generated by obj_file_read.
std::vector<std::string> *reason = nullptr (optional output)
Human-readable list of overlap reasons corresponding to each overlapping object. Length: [n_overlap].
dtype tolerance = 0.0005 (optional input)
Geometric tolerance (in meters) used to determine intersections. Default: 0.0005 (0.5 mm).
-
Returns:
arma::uvec: Vector of unique object indices (1-based) that were found to overlap, size [n_overlap].
-
Technical Notes:
- Overlap detection includes checks for: Intersecting triangle faces (shared volume), Vertices or edges penetrating another object’s bounding volume.
- The
tolerance accounts for modeling inaccuracies and numerical instability—small overlaps below this threshold are ignored.
- This function does not modify the mesh or attempt to repair overlapping geometry — it only reports it.
-
See also:
path_to_tube - Convert a 3D path into a tube surface for visualization
-
Description:
- Converts a sequence of 3D points (path) into a tubular surface using a ring of vertices around each path segment.
- Produces a quad-based mesh suitable for rendering in 3D tools such as Blender or MeshLab.
- Uses circular cross-sections with configurable radius and edge count.
- Internal calculations are performed in double precision to ensure numerical stability along complex paths.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
void quadriga_lib::path_to_tube(
const arma::Mat<dtype> *path_coord,
arma::Mat<dtype> *vert,
arma::umat *faces,
dtype radius = 1.0,
arma::uword n_edges = 5);
-
Arguments:
const arma::Mat<dtype> *path_coord (input)
Ordered list of 3D coordinates along the path, matrix of size [3, n_coord].
arma::Mat<dtype> *vert (output)
Generated tube vertex positions. Size: [3, n_coord × n_edges].
arma::umat *faces (output)
Quad face indices into vert. Each row contains 4 indices forming a quad. Size: [4, (n_coord - 1) × n_edges].
dtype radius = 1.0 (optional input)
Radius of the tube cross-section (in meters). Default: 1.0.
arma::uword n_edges = 5 (optional input)
Number of vertices used to approximate each circular cross-section. Must be ≥ 3. Default: 5.
-
Technical Notes:
- The generated tube is centered around the path with perpendicular circular cross-sections.
- Orientation between path segments is handled with continuous frame alignment to reduce twisting.
- Quad faces are generated by connecting adjacent rings along the path.
- Output
faces can be directly exported to formats like .obj or .ply.
point_cloud_aabb - Compute the Axis-Aligned Bounding Boxes (AABB) of a 3D point cloud
point_cloud_segmentation - Reorganize a point cloud into spatial sub-clouds for efficient processing
-
Description:
- Recursively partitions a 3D point cloud into smaller sub-clouds, each below a given size threshold.
- Sub-clouds are aligned to a specified SIMD vector size (e.g., for AVX or CUDA), with padding if necessary.
- Outputs (
pointsR) a reorganized version of the input points that groups points by sub-cloud.
- Also produces forward and reverse index maps to track the reordering of points.
- Useful for optimizing spatial processing tasks such as bounding volume hierarchies or GPU batch execution.
- Allowed datatypes (
dtype): float or double
-
Declaration:
arma::uword quadriga_lib::point_cloud_segmentation(
const arma::Mat<dtype> *points,
arma::Mat<dtype> *pointsR,
arma::u32_vec *sub_cloud_index,
arma::uword target_size = 1024,
arma::uword vec_size = 1,
arma::u32_vec *forward_index = nullptr,
arma::u32_vec *reverse_index = nullptr);
-
Arguments:
const arma::Mat<dtype> *points (input)
Original 3D point cloud to be segmented. Size: [n_points, 3].
arma::Mat<dtype> *pointsR (output)
Reorganized point cloud with points grouped by sub-cloud. Size: [n_pointsR, 3].
arma::u32_vec *sub_cloud_index (output)
Vector of starting indices (0-based) for each sub-cloud within pointsR. Length: [n_sub].
arma::uword target_size = 1024 (optional input)
Maximum number of elements allowed per sub-cloud (before padding). Default: 1024.
arma::uword vec_size = 1 (optional input)
Vector alignment size for SIMD or CUDA. The number of points in each sub-cloud is padded to a multiple of this value. Default: 1.
arma::u32_vec *forward_index = nullptr (optional output)
Index map from original points to reorganized pointsR (1-based). Size: [n_pointsR]. Padding indices are 0.
arma::u32_vec *reverse_index = nullptr (optional output)
Index map from pointsR back to original points (0-based). Size: [n_points].
-
Returns:
arma::uword
Number of generated sub-clouds, n_sub.
-
Technical Notes:
- Sub-clouds are formed using recursive spatial splitting (e.g., median-split along bounding box axes).
- Padding points are placed at the AABB center of the corresponding sub-cloud and can be ignored in processing.
- This function is typically used as a preprocessing step for GPU acceleration or bounding volume hierarchy (BVH) generation.
- If
vec_size = 1, no padding is applied and all output maps contain valid indices only.
-
See also:
point_cloud_split - Split a point cloud into two sub-clouds along a spatial axis
-
Description:
- Divides a 3D point cloud into two sub-clouds along the specified axis.
- Attempts to split the data at the median value to balance the number of points in each half.
- Returns the axis used for the split, or a negative value if a valid split was not possible (e.g., all points fall on one side).
- Output point clouds are written into
pointsA and pointsB, and their size is adjusted accordingly.
- An optional indicator vector identifies the target sub-cloud (A or B) for each input point.
- Used in recursive spatial partitioning such as building BVH structures.
- Allowed datatypes (
dtype): float or double
-
Declaration:
int quadriga_lib::point_cloud_split(
const arma::Mat<dtype> *points,
arma::Mat<dtype> *pointsA,
arma::Mat<dtype> *pointsB,
int axis = 0,
arma::Col<int> *split_ind = nullptr);
-
Arguments:
const arma::Mat<dtype> *points (input)
Input point cloud. Size: [n_points, 3].
arma::Mat<dtype> *pointsA (output)
First sub-cloud after split. Size: [n_pointsA, 3].
arma::Mat<dtype> *pointsB (output)
Second sub-cloud after split. Size: [n_pointsB, 3].
int axis = 0 (optional input)
Axis to split along: 0 = longest extent (default), 1 = x-axis, 2 = y-axis, 3 = z-axis.
arma::Col<int> *split_ind = nullptr (optional output)
Vector of length [n_points], where each element is: 1 if the point goes to pointsA, 2 if it goes to pointsB, 0 if error.
-
Returns:
int
Axis used for splitting: 1 = x, 2 = y, 3 = z, or -1, -2, -3 if the split failed (no points assigned to one of the outputs).
-
Notes:
- The function does not modify
pointsA or pointsB if the split fails.
- The selected axis is based on the bounding box if
axis == 0
- This function is a building block for spatial acceleration structures (e.g., BVH, KD-trees), see point_cloud_segmentation
-
See also:
point_inside_mesh - Test whether 3D points are inside a triangle mesh using raycasting
-
Description:
- Uses raycasting to determine whether each 3D point lies inside a triangle mesh.
- Requires that the mesh is watertight and all normals are pointing outwards.
- For each point, multiple rays are cast in various directions.
- If any ray intersects a mesh element with a negative incidence angle, the point is classified as inside.
- Output can be binary (0 = outside, 1 = inside) or labeled with object indices.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
arma::u32_vec quadriga_lib::point_inside_mesh(
const arma::Mat<dtype> *points,
const arma::Mat<dtype> *mesh,
const arma::u32_vec *obj_ind = nullptr,
dtype distance = 0.0);
-
Arguments:
const arma::Mat<dtype> *points (input)
3D point coordinates to test, size [n_points, 3].
const arma::Mat<dtype> *mesh (input)
Triangular mesh faces. Each row represents a triangle using 3 vertices in row-major format (x1,y1,z1,x2,y2,z2,x3,y3,z3), size [n_mesh, 9].
const arma::u32_vec *obj_ind = nullptr (optional input)
Optional object index for each mesh element (1-based), size [n_mesh]. If provided, the return vector will contain the index of the enclosing object instead of binary values.
dtype distance = 0.0 (optional input)
Optional distance in meters from objects that should be considered as inside the object.
Possible range: 0 - 20 m. Using this parameter significantly increases computation time.
-
Returns:
arma::u32_vec, size [n_points]
For each point: Returns 0 if the point is outside the mesh (or all objects), 1 if inside (or close to) any mesh object
(if obj_ind not given), or returns the 1-based object index if obj_ind is provided.
ray_mesh_interact - Calculates interactions (reflection, transmission, refraction) of radio waves with objects
-
Description:
- Radio waves that interact with a building, or other objects in the environment, will produce losses
that depend on the electrical properties of the materials and material structure.
- This function calculates the interactions of radio-waves with objects in a 3D environment.
- It considers a plane wave incident upon a planar interface between two homogeneous and isotropic
media of differing electric properties. The media extend sufficiently far from the interface such
that the effect of any other interface is negligible.
- Supports beam-based modeling using triangular ray tubes defined by
trivec and tridir.
- Air to media transition is assumed if front side of a face is hit and FBS != SBS
- Media to air transition is assumed if back side of a face is hit and FBS != SBS
- Media to media transition is assumed if FBS = SBS with opposing face orientations
- Order of the vertices determines side (front or back) of a mesh element
- Overlapping geometry in the triangle mesh must be avoided, since materials are transparent to radio
waves.
- Implementation is done according to ITU-R P.2040-1.
- Rays that do not interact with the environment (i.e. for which
fbs_ind = 0) are omitted from the output.
- Maintains consistency of input and output ray formats, including direction encoding (spherical or Cartesian).
- Assigns interaction type codes to output rays, allowing classification (e.g., single hit, total
reflection, material-to-material).
- Allowed datatypes (
dtype): float or double.
-
Declaration:
void quadriga_lib::ray_mesh_interact(
int interaction_type,
dtype center_frequency,
const arma::Mat<dtype> *orig,
const arma::Mat<dtype> *dest,
const arma::Mat<dtype> *fbs,
const arma::Mat<dtype> *sbs,
const arma::Mat<dtype> *mesh,
const arma::Mat<dtype> *mtl_prop,
const arma::u32_vec *fbs_ind,
const arma::u32_vec *sbs_ind,
const arma::Mat<dtype> *trivec = nullptr,
const arma::Mat<dtype> *tridir = nullptr,
const arma::Col<dtype> *orig_length = nullptr,
arma::Mat<dtype> *origN = nullptr,
arma::Mat<dtype> *destN = nullptr,
arma::Col<dtype> *gainN = nullptr,
arma::Mat<dtype> *xprmatN = nullptr,
arma::Mat<dtype> *trivecN = nullptr,
arma::Mat<dtype> *tridirN = nullptr,
arma::Col<dtype> *orig_lengthN = nullptr,
arma::Col<dtype> *fbs_angleN = nullptr,
arma::Col<dtype> *thicknessN = nullptr,
arma::Col<dtype> *edge_lengthN = nullptr,
arma::Mat<dtype> *normal_vecN = nullptr,
arma::s32_vec *out_typeN = nullptr);
-
Arguments:
int interaction_type (input)
Type of interaction: 0 = reflection, 1 = transmission, 2 = refraction.
dtype center_frequency (input)
Center frequency in Hz.
const arma::Mat<dtype> orig, dest (input)
Ray origin and destination points in global Cartesian space. Size: [n_ray, 3].
const arma::Mat<dtype> fbs, sbs (input)
First and second interaction points. Size: [n_ray, 3].
const arma::Mat<dtype> *mesh (input)
Triangle mesh faces. Size: [n_mesh, 9], See obj_file_read for details
const arma::Mat<dtype> *mtl_prop (input)
Material properties per face. Size: [n_mesh, 5], See obj_file_read for details
const arma::u32_vec fbs_ind, sbs_ind (input)
Mesh indices of interaction points (1-based). Size: [n_ray].
const arma::Mat<dtype> *trivec (optional input)
The 3 vectors pointing from the center point of the ray at the origin to the vertices of a triangular
propagation tube (=beam wavefront), the values are in the order [ v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z ];
Size: [no_ray, 9]
const arma::Mat<dtype> *tridir (optional input)
The directions of the vertex-rays. Size: [ n_ray, 6 ] or [ n_ray, 9 ],
For 6 columns, values are in geographic coordinates (azimuth and elevation angle in rad); the
values are in the order [ v1az, v1el, v2az, v2el, v3az, v3el ];
For 9 columns, the input is in Cartesian coordinates and the values are in the order
[ v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z ]
const arma::Col<dtype> *orig_length (optional input)
Path length at origin. Size: [n_ray]. Default is 0
arma::Mat<dtype> *origN (output)
New ray origins after the interaction with the medium, usually placed close to the FBS location.
A small offset of 0.001 m in the direction of travel after the interaction with the medium is added
to avoid getting stuct inside a mesh element. Size: [ no_rayN, 3 ]
arma::Mat<dtype> *destN (output)
New ray destinaion after the interaction with the medium, taking the change of direction into account;
Size: [no_rayN, 3]
arma::Col<dtype> *gainN (output)
Gain (negative loss) caused by the interaction with the medium, averaged over both polarization
directions. This value includes the in-medium attenuation, but does not account for FSPL.
Values are in linear scale (not dB); Size: [no_rayN]
arma::Mat<dtype> *xprmatN (output)
Polarization transfer matrix; Interleaved complex values (ReVV, ImVV, ReVH, ImVH, ReHV, ImHV, ReHH, ImHH);
The values account for the following effects: (1) gain caused by the interaction with the medium,
(2) different reflection/transmission coefficients for transverse electric (TE) and transverse
magnetic (TM) polarisation, (3) orientation of the incidence plane, (4) in-medium attenuation.
FSPL is excluded, Size: [ no_rayN, 8 ]
arma::Mat<dtype> trivecN, tridirN (output)
New ray tube geometry and direction. Format matches input.
arma::Col<dtype> *orig_lengthN (output)
Length of the ray from orig to origN. If orig_length is given as input, its value is added.
Size: [ no_rayN ]
arma::Col<dtype> *fbs_angleN (output)
Angle between incoming ray and FBS in [rad], Size [ n_rayN ]
arma::Col<dtype> *thicknessN (output)
Material thickness in meters calculated from the difference between FBS and SBS, Size [ n_rayN ]
arma::Col<dtype> *edge_lengthN (output)
Max. edge length of the ray tube triangle at the new origin. A value of infinity indicates that only
a part of the ray tube hits the object. Size [ n_rayN, 3 ]
arma::Mat<dtype> *normal_vecN (output)
Normal vector of FBS and SBS, Size [ n_rayN, 6 ]
arma::s32_vec *out_typeN (output)
A numeric indicator describing the type of the interaction. The total refection indicator is only
set in refraction mode. Size [ n_rayN ]
| No |
θF<0 |
θS<0 |
dFS=0 |
TotRef |
iSBS=0 |
NF=-NS |
NF=NS |
startIn |
endIn |
Meaning |
| 0 |
|
|
|
|
|
|
|
|
|
Undefined |
| 1 |
no |
N/A |
no |
N/A |
yes |
N/A |
N/A |
no |
yes |
Single Hit o-i |
| 2 |
yes |
N/A |
no |
no |
yes |
N/A |
N/A |
yes |
no |
Single Hit i-o |
| 3 |
yes |
N/A |
no |
yes |
yes |
N/A |
N/A |
yes |
no |
Single Hit i-o, TR |
| --- |
----- |
------ |
------- |
-------- |
-------- |
-------- |
------- |
--------- |
------- |
---------------------------- |
| 4 |
no |
yes |
yes |
no |
no |
yes |
no |
yes |
yes |
M2M, M2 hit first |
| 5 |
yes |
no |
yes |
no |
no |
yes |
no |
yes |
yes |
M2M, M1 hit first |
| 6 |
yes |
no |
yes |
yes |
no |
yes |
no |
yes |
yes |
M2M, M1 hit first, TR |
| --- |
----- |
------ |
------- |
-------- |
-------- |
-------- |
------- |
--------- |
------- |
---------------------------- |
| 7 |
no |
no |
yes |
N/A |
no |
no |
yes |
no |
yes |
Overlapping Faces, o-i |
| 8 |
yes |
yes |
yes |
no |
no |
no |
yes |
yes |
no |
Overlapping Faces, i-o |
| 9 |
yes |
yes |
yes |
yes |
no |
no |
yes |
yes |
no |
Overlapping Faces, i-o, TR |
| --- |
----- |
------ |
------- |
-------- |
-------- |
-------- |
------- |
--------- |
------- |
---------------------------- |
| 10 |
no |
yes |
yes |
N/A |
no |
no |
no |
no |
no |
Edge Hit, o-i-o |
| 11 |
yes |
no |
yes |
no |
no |
no |
no |
yes |
yes |
Edge Hit, i-o-i |
| 12 |
yes |
no |
yes |
yes |
no |
no |
no |
yes |
yes |
Edge Hit, i-o-i, TR |
| 13 |
no |
no |
yes |
N/A |
no |
no |
no |
no |
yes |
Edge Hit, o-i |
| 14 |
yes |
yes |
yes |
no |
no |
no |
no |
yes |
no |
Edge Hit, i-o |
| 15 |
yes |
yes |
yes |
yes |
no |
no |
no |
yes |
no |
Edge Hit, i-o, TR |
-
See also:
ray_point_intersect - Calculates the intersection of ray beams with points in three dimensions
-
Description:
Unlike traditional ray tracing, where rays do not have a physical size, beam tracing models rays as
beams with volume. Beams are defined by triangles whose vertices diverge as the beam extends. This
approach is used to simulate a kind of divergence or spread in the beam, reminiscent of how radio
waves spreads as they travel from a point source. The volumetric nature of the beams allows for more
realistic modeling of energy distribution. As beams widen, the energy they carry can be distributed
across their cross-sectional area, affecting the intensity of the interaction with surfaces.
Unlike traditional ray tracing where intersections are line-to-geometry tests, beam tracing requires
volumetric intersection tests.
- This function computes whether points in 3D Cartesian space are intersected by ray beams.
- A ray beam is defined by a ray origin and a triangular wavefront formed by three directional vectors.
- Returns a list of ray indices (0-based) that intersect with each point in the input point cloud.
- Supports three compute kernels: GENERIC (scalar), AVX2 (SIMD, 8 points in parallel), and CUDA (GPU).
- The
use_kernel parameter selects the kernel: 0 = auto, 1 = GENERIC, 2 = AVX2, 3 = CUDA.
- In auto mode (0), CUDA is selected only when
n_points >= 10000 and a CUDA-capable GPU is available;
otherwise AVX2 is preferred if available, falling back to GENERIC.
- Optional support for pre-segmented point clouds (e.g., using point_cloud_segmentation)
to reduce computational cost.
- All internal computations use single precision for speed.
- Recommended to use with small tube radius and well-distributed points for optimal accuracy.
-
Declaration:
template <typename dtype>
std::vector<arma::u32_vec> quadriga_lib::ray_point_intersect(
const arma::Mat<dtype> *points,
const arma::Mat<dtype> *orig,
const arma::Mat<dtype> *trivec,
const arma::Mat<dtype> *tridir,
const arma::u32_vec *sub_cloud_index = nullptr,
arma::u32_vec *hit_count = nullptr,
int use_kernel = 0,
int gpu_id = 0);
-
Arguments:
const arma::Mat<dtype> *points (input)
3D coordinates of the point cloud. Size: [n_points, 3].
const arma::Mat<dtype> *orig (input)
Ray origin positions in global coordinate system. Size: [n_ray, 3].
const arma::Mat<dtype> *trivec (input)
The 3 vectors pointing from the center point of the ray at the ray origin to the vertices of
a triangular propagation tube (the beam), the values are in the order
[ v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z ]; Size: [ no_ray, 9 ]
const arma::Mat<dtype> *tridir (input)
The directions of the vertex-rays. Size: [ n_ray, 9 ], Values must be given in Cartesian
coordinates in the order [ d1x, d1y, d1z, d2x, d2y, d2z, d3x, d3y, d3z ]; The vector does
not need to be normalized.
const arma::u32_vec *sub_cloud_index = nullptr (optional input)
Index vector to mark boundaries between point cloud segments (e.g. for SIMD optimization). Length: [n_sub].
When using AVX2, sub-cloud start indices must be aligned to multiples of 8.
arma::u32_vec *hit_count = nullptr (optional output)
Output array with number of rays that intersected each point. Length: [n_points].
int use_kernel = 0 (optional input)
Selects the compute kernel: 0 = auto (default), 1 = GENERIC (scalar CPU), 2 = AVX2 (SIMD),
3 = CUDA (GPU). An error is thrown if the requested kernel is not available at runtime.
int gpu_id = 0 (optional input)
GPU device ID for CUDA kernel. Ignored when not using CUDA.
-
Returns:
std::vector<arma::u32_vec>
List of ray indices that intersected each point (0-based). Each entry in the returned vector corresponds to one point.
-
See also:
ray_triangle_intersect - Calculates the intersection of rays and triangles in three dimensions
-
Description:
- Implements the Möller–Trumbore algorithm to compute intersections between rays and triangles in 3D.
- Supports three compute kernels: GENERIC (scalar), AVX2 (SIMD, 8 triangles in parallel), and CUDA (GPU).
- The
use_kernel parameter selects the kernel: 0 = auto, 1 = GENERIC, 2 = AVX2, 3 = CUDA.
- In auto mode (0), CUDA is selected only when
n_ray >= 10000 and a CUDA-capable GPU is available;
otherwise AVX2 is preferred if available, falling back to GENERIC.
- Can detect first and second intersections (FBS/SBS), number of intersections, and intersection indices.
- Supports a compact input format where origin and destination coordinates are stored together.
- Allowed datatypes (
dtype): float or double
- Internal computations are carried out in single precision regardless of
dtype.
-
Declaration:
void quadriga_lib::ray_triangle_intersect(
const arma::Mat<dtype> *orig,
const arma::Mat<dtype> *dest,
const arma::Mat<dtype> *mesh,
arma::Mat<dtype> *fbs = nullptr,
arma::Mat<dtype> *sbs = nullptr,
arma::u32_vec *no_interact = nullptr,
arma::u32_vec *fbs_ind = nullptr,
arma::u32_vec *sbs_ind = nullptr,
const arma::u32_vec *sub_mesh_index = nullptr,
bool transpose_inputs = false,
int use_kernel = 0,
int gpu_id = 0);
-
Arguments:
const arma::Mat<dtype> *orig (input)
Ray origins in global coordinate system (GCS). Size: [n_ray, 3], or [n_ray, 6] if dest == nullptr.
In the latter case, columns must be ordered as {xo, yo, zo, xd, yd, zd}.
const arma::Mat<dtype> *dest (input)
Ray destinations in GCS. Size: [n_ray, 3]. Set to nullptr if orig has 6 columns and contains
both origin and destination.
const arma::Mat<dtype> *mesh (input)
Triangular surface mesh. Size: [n_mesh, 9], where each row contains the 3 vertices
{x1 y1 z1 x2 y2 z2 x3 y3 z3}.
arma::Mat<dtype> *fbs (optional output)
First-bounce surface intersection points (FBS). Size: [n_ray, 3].
arma::Mat<dtype> *sbs (optional output)
Second-bounce surface intersection points (SBS). Size: [n_ray, 3].
arma::u32_vec *no_interact (optional output)
Number of intersections per ray (0, 1, or 2). Size: [n_ray].
arma::u32_vec *fbs_ind (optional output)
1-based index of the first intersected mesh element, 0 = no intersection. Size: [n_ray].
arma::u32_vec *sbs_ind (optional output)
1-based index of the second intersected mesh element, 0 = no second intersection. Size: [n_ray].
const arma::u32_vec *sub_mesh_index (optional input)
Indexes indicating start of sub-meshes in mesh. Size: [n_sub]. Enables faster processing via segmentation.
When using AVX2, sub-mesh start indices must be aligned to multiples of 8.
bool transpose_inputs = false (optional input)
If true, treats orig/dest as [3, n_ray] and mesh as [9, n_mesh].
int use_kernel = 0 (optional input)
Selects the compute kernel: 0 = auto (default), 1 = GENERIC (scalar CPU), 2 = AVX2 (SIMD),
3 = CUDA (GPU). An error is thrown if the requested kernel is not available at runtime.
int gpu_id = 0 (optional input)
GPU device ID for CUDA kernel. Ignored when not using CUDA.
-
See also:
subdivide_rays - Subdivide ray beams into four smaller sub-beams
-
Description:
- Subdivides each ray beam (defined by a triangular wavefront) into four new beams with adjusted origin, shape, and direction.
- Supports input in Spherical or Cartesian direction format.
- When
dest is not provided, the corresponding output destN is omitted.
- Useful for hierarchical ray tracing or angular resolution refinement.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
arma::uword quadriga_lib::subdivide_rays(
const arma::Mat<dtype> *orig,
const arma::Mat<dtype> *trivec,
const arma::Mat<dtype> *tridir,
const arma::Mat<dtype> *dest = nullptr,
arma::Mat<dtype> *origN = nullptr,
arma::Mat<dtype> *trivecN = nullptr,
arma::Mat<dtype> *tridirN = nullptr,
arma::Mat<dtype> *destN = nullptr,
const arma::u32_vec *index = nullptr,
const double ray_offset = 0.0);
-
Arguments:
const arma::Mat<dtype> *orig (input)
Ray origin points in global coordinate system (GCS).
Size: [n_ray, 3].
const arma::Mat<dtype> *trivec (input)
Vectors pointing from the ray origin to the three triangle vertices.
Size: [n_ray, 9], order: [x1 y1 z1 x2 y2 z2 x3 y3 z3].
const arma::Mat<dtype> *tridir (input)
Directions of the three vertex-rays.
Format can be Spherical [n_ray, 6] as [v1az v1el v2az v2el v3az v3el],
or Cartesian [n_ray, 9] as [v1x v1y v1z v2x v2y v2z v3x v3y v3z].
const arma::Mat<dtype> *dest = nullptr (input)
Ray destination points. If nullptr, the output destN will remain empty.
Size: [n_ray, 3].
arma::Mat<dtype> *origN (output)
New ray origins after subdivision.
Size: [n_rayN, 3].
arma::Mat<dtype> *trivecN (output)
Updated vectors for each subdivided triangle beam.
Size: [n_rayN, 9].
arma::Mat<dtype> *tridirN (output)
New directions of the subdivided vertex-rays, in the same format as input.
Size: [n_rayN, 6] (spherical) or [n_rayN, 9] (Cartesian).
arma::Mat<dtype> *destN (output)
Updated destination points.
Size: [n_rayN, 3], empty if input dest was nullptr.
const arma::u32_vec *index (optional input)
List of ray indices to be subdivided (0-based). Only the specified rays are subdivided.
Size: [n_ind].
const double ray_offset = 0.0 (optional input)
Offset (in meters) applied to the origin of each subdivided ray along its propagation direction.
Default: 0.0.
-
Returns:
arma::uword n_rayN
Number of output rays, typically 4 × n_ray or 4 × n_ind if index is provided.
-
See also:
subdivide_triangles - Subdivide triangles into smaller triangles
-
Description:
- Uniformly subdivides each input triangle into
n_div × n_div smaller triangles.
- Increases spatial resolution for mesh-based processing (e.g., ray tracing or visualization).
- Optional input/output material properties are duplicated across subdivided triangles.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
arma::uword quadriga_lib::subdivide_triangles(
arma::uword n_div,
const arma::Mat<dtype> *triangles_in,
arma::Mat<dtype> *triangles_out,
const arma::Mat<dtype> *mtl_prop = nullptr,
arma::Mat<dtype> *mtl_prop_out = nullptr);
-
Arguments:
arma::uword n_div (input)
Number of subdivisions per triangle edge;
total output triangles: n_triangles_out = n_triangles_in × n_div × n_div.
const arma::Mat<dtype> *triangles_in (input)
Vertices of the triangular mesh in global Cartesian coordinates; each face is described by 3
points in 3D-space: [ v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z ]; Size: [n_triangles_in, 9]
arma::Mat<dtype> *triangles_out (output)
Vertices of the sub-divided mesh in global Cartesian coordinates; Size: [n_triangles_out, 9]
const arma::Mat<dtype> *mtl_prop = nullptr (optional input)
Material properties associated for the input triangles; Size: [n_triangles_in, 5].
arma::Mat<dtype> *mtl_prop_out = nullptr (optional output)
Material properties for the subdivided triangles, copied from the parent triangle,
Size: [n_triangles_out, 5].
-
Returns:
arma::uword n_triangles_out
Number of generated triangles (equals n_triangles_in × n_div × n_div).
triangle_mesh_aabb - Calculate the axis-aligned bounding box (AABB) of a triangle mesh and its sub-meshes
triangle_mesh_segmentation - Reorganize a 3D mesh into smaller sub-meshes for faster processing
-
Description:
This function processes the elements of a large triangle mesh by clustering those that are
closely spaced. The resulting mesh retains the same elements but rearranges their order.
The function aims to minimize the size of the axis-aligned bounding box around each cluster,
referred to as a sub-mesh, while striving to maintain a specific number of elements within
each cluster.
- Subdivision is recursive and based on bounding box partitioning until each sub-mesh contains no more than
target_size triangles.
- Sub-meshes are aligned to
vec_size for SIMD or GPU optimization; padded with dummy triangles at the center of each sub-mesh if needed.
- If material properties are provided, these are also reorganized and padded accordingly.
- The function returns the number of created sub-meshes, and reorders the triangles and materials.
- Allowed datatypes (
dtype): float or double.
-
Declaration:
arma::uword quadriga_lib::triangle_mesh_segmentation(
const arma::Mat<dtype> *mesh,
arma::Mat<dtype> *meshR,
arma::u32_vec *sub_mesh_index,
arma::uword target_size = 1024,
arma::uword vec_size = 1,
const arma::Mat<dtype> *mtl_prop = nullptr,
arma::Mat<dtype> *mtl_propR = nullptr,
arma::u32_vec *mesh_index = nullptr);
-
Arguments:
const arma::Mat<dtype> *mesh (input)
Vertices of the triangular mesh in global Cartesian coordinates. Each face is described by 3
points in 3D-space: [ v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z ]; Size: [n_mesh, 9]
arma::Mat<dtype> *meshR (output)
Vertices of the clustered mesh in global Cartesian coordinates; Size: [n_meshR, 9]
arma::u32_vec *sub_mesh_index (output)
Start indices of the sub-meshes in 0-based notation; Vector of length [n_sub]
arma::uword target_size = 1024 (input)
The target number of elements of each sub-mesh. Default value = 1024. For best performance, the
value should be around sgrt(n_mesh)
arma::uword vec_size = 1 (input)
Vector size for SIMD processing (e.g. 8 for AVX2, 32 for CUDA). Default value = 1.
For values > 1,the number of rows for each sub-mesh in the output is increased to a multiple
of vec_size. For padding, zero-sized triangles are placed at the center of the AABB of
the corresponding sub-mesh.
const arma::Mat<dtype> *mtl_prop = nullptr (optional input)
Material properties corresponding to the original mesh; Size: [n_mesh, 5]; See obj_file_read
arma::Mat<dtype> *mtl_propR = nullptr (optional output)
Reorganized material properties, aligned and padded if necessary, Size: [n_meshR, 5]
arma::u32_vec *mesh_index = nullptr (optional output)
1-based mapping from the original mesh to the reorganized mesh; Size: [n_meshR] (0 = padding)
-
Returns:
arma::uword n_sub
The number of created sub-meshes. Output matrices are resized accordingly.
triangle_mesh_split - Split a 3D mesh into two sub-meshes along a given axis
-
Description:
- Divides a triangular mesh into two sub-meshes along a selected axis (or automatically the longest).
- The function chooses a split point based on the bounding box center of the selected axis.
- Returns the axis used for the split:
1 = x, 2 = y, 3 = z; or negative values if the split failed.
- An optional indicator vector identifies the target sub-mesh (A or B) for each input point.
- On failure (i.e., all triangles fall into one side), outputs
meshA and meshB remain unchanged.
- Allowed datatypes (
dtype): float or double
-
Declaration:
int quadriga_lib::triangle_mesh_split(
const arma::Mat<dtype> *mesh,
arma::Mat<dtype> *meshA,
arma::Mat<dtype> *meshB,
int axis = 0,
arma::Col<int> *split_ind = nullptr);
-
Arguments:
const arma::Mat<dtype> *mesh (input)
Triangle mesh input; each row contains one triangle as [x1 y1 z1 x2 y2 z2 x3 y3 z3]
Size: [n_mesh, 9]
arma::Mat<dtype> *meshA (output)
First resulting sub-mesh; triangles with centroid below split threshold. Size: [n_meshA, 9]
arma::Mat<dtype> *meshB (output)
Second resulting sub-mesh; triangles with centroid above split threshold. Size: [n_meshB, 9]
int axis = 0 (optional input)
Axis to split along: 0 = longest extent (default), 1 = x-axis, 2 = y-axis, 3 = z-axis.
arma::Col<int> *split_ind = nullptr (optional output)
Output vector indicating assignment of each triangle: 1 = meshA, 2 = meshB, 0 = not assigned (on failure)
Length: [n_mesh]
-
Returns:
int
The axis used for the split (1, 2, or 3), or negative value on failure (-1, -2, or -3).