hermespy-rt

Minimalistic signal processing ray-tracer in C
git clone https://git.ea.contact/hermespy-rt
Log | Files | Refs

commit cbe54e2e4f21cc541f89c2824180cee2b122c038
parent 2f0804aa2c350c0b80e73bdbf3a1701dfff878df
Author: Egor Achkasov <eaachkasov@edu.hse.ru>
Date:   Tue,  4 Feb 2025 16:20:09 +0100

rm hit_points out; add directions outs; fix memory dealloc in pybind

Diffstat:
Mcompute_paths.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mcompute_paths.h | 7++++---
Mcompute_paths_pybind11.cpp | 80+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtest.c | 11++++++-----
Mtest.py | 14++++++++------
5 files changed, 129 insertions(+), 83 deletions(-)

diff --git a/compute_paths.c b/compute_paths.c @@ -567,14 +567,15 @@ void compute_paths( IN size_t num_tx, /* number of transmitters */ IN size_t num_paths, /* number of paths */ IN size_t num_bounces, /* number of bounces */ - OUT float *hit_points, /* output array of hit points (num_rx, num_tx, num_paths, 3) */ /* LoS */ + OUT float *directions_los, /* output array of directions (num_rx, num_tx, 3) */ OUT float *a_te_re_los, /* output array real parts of TE gains (num_rx, num_tx) */ OUT float *a_te_im_los, /* output array imaginary parts of TE gains (num_rx, num_tx) */ OUT float *a_tm_re_los, /* output array real parts of TM gains (num_rx, num_tx) */ OUT float *a_tm_im_los, /* output array imaginary parts of TM gains (num_rx, num_tx) */ OUT float *tau_los, /* output array of delays (num_rx, num_tx) */ /* Scatter */ + OUT float *directions_scat, /* output array of directions (num_rx, num_tx, num_paths, 3) */ OUT float *a_te_re_scat, /* output array real parts of TE gains (num_bounces, num_rx, num_tx, num_paths) */ OUT float *a_te_im_scat, /* output array imaginary parts of TE gains (num_bounces, num_rx, num_tx, num_paths) */ OUT float *a_tm_re_scat, /* output array real parts of TM gains (num_bounces, num_rx, num_tx, num_paths) */ @@ -584,7 +585,7 @@ void compute_paths( { /* Init loop indices and offset variables */ size_t i, j; - size_t off, off_scat, off_hit_points; + size_t off, off_scat; /* Load the scene */ Mesh mesh = load_mesh_ply(mesh_filepath, carrier_frequency); @@ -658,19 +659,38 @@ void compute_paths( float free_space_loss_multiplier = 4.f * PI * carrier_frequency * 1e9 / SPEED_OF_LIGHT; float free_space_loss; - /* Calculate LoS paths */ + /***************************************************************************/ + /* LoS paths */ + /***************************************************************************/ /* Consider imaginary parts of the LoS gains to be 0 in any way */ for (off = 0; off != num_rx * num_tx; ++off) a_te_im_los[off] = a_tm_im_los[off] = 0.f; + /* Calculate LoS paths directly from tx to rx */ for (i = 0; i != num_rx; ++i) for (j = 0; j != num_tx; ++j) { off = i * num_tx + j; t = -1.f; ind = -1; + + /* Create a ray from the tx to the rx */ r.o = tx_pos_v[j]; r.d = vec3_sub(&rx_pos_v[i], &r.o); + + /* In case the tx and the rx are at the same position */ + if (vec3_dot(&r.d, &r.d) < __FLT_EPSILON__) { + /* Assume the direction to be (1, 0, 0) */ + directions_los[off * 3] = 1.f; + directions_los[off * 3 + 1] = 0.f; + directions_los[off * 3 + 2] = 0.f; + /* Set gains to 1+0j */ + a_te_re_los[off] = a_tm_re_los[off] = 1.f; + /* Set delay to 0 */ + tau_los[off] = 0.f; + continue; + } + /* Is there any abstacle between the tx and the rx? */ moeller_trumbore(&r, &mesh, &t, &ind, &theta); if (ind != -1 && t <= 1.f) { @@ -678,9 +698,14 @@ void compute_paths( a_te_re_los[off] = a_tm_re_los[off] = tau_los[off] = 0.f; continue; } - /* No triangle has been hit, the path is LoS */ + + /* No obstacle has been hit, the path is LoS */ /* Calculate the distance */ t = sqrtf(vec3_dot(&r.d, &r.d)); + /* Calculate the direction */ + directions_los[off * 3] = -r.d.x / t; + directions_los[off * 3 + 1] = -r.d.y / t; + directions_los[off * 3 + 2] = -r.d.z / t; /* Calculate the free space loss */ free_space_loss = free_space_loss_multiplier * t; if (free_space_loss > 1.f) { @@ -692,35 +717,41 @@ void compute_paths( tau_los[off] = t / SPEED_OF_LIGHT; } + /***************************************************************************/ + /* Scatter paths */ + /***************************************************************************/ + /* Bounce the rays. On each bounce: * - Add path length / SPEED_OF_LIGHT to tau * - Update gains using eqs. (31a)-(31b) from ITU-R P.2040-3 - * - Spawn refraction rays with gains as per eqs. (31c)-(31d) from ITU-R P.2040-3 + * - Spawn scattering rays towards the rx + * - TODO Spawn refraction rays with gains as per eqs. (31c)-(31d) from ITU-R P.2040-3 */ for (i = 0; i < num_bounces; ++i) { for (off = 0; off != num_tx * num_paths; ++off) { + /***********************************************************************/ + /* Reflect the rays */ + /***********************************************************************/ /* Check if the ray is active */ if (!(active[off / 8] & (1 << (off % 8)))) continue; /* Init */ t = -1.f; ind = -1; - /* Find the hit point and trinagle. - Calculate an angle of incidence */ + /* Find the hit point and trinagle and the angle of incidence */ moeller_trumbore(&rays[off], &mesh, &t, &ind, &theta); if (ind == -1) { /* Ray hit nothing */ active[off / 8] &= ~(1 << (off % 8)); --num_active; continue; } - /* Get the normal */ + /* Get the hit primitive's normal */ n = mesh.normals[ind]; - /* Calculate the reflection coefficients - R_{eTE} and R_{eTM} */ + /* Calculate the reflection coefficients R_{eTE} and R_{eTM} */ refl_coefs(&mesh.rms[mesh.rm_indices[ind]], - theta, - &r_te_re, &r_te_im, - &r_tm_re, &r_tm_im); + theta, + &r_te_re, &r_te_im, + &r_tm_re, &r_tm_im); /* Calculate the free space loss */ free_space_loss = free_space_loss_multiplier * t; if (free_space_loss > 1.f) { @@ -729,7 +760,7 @@ void compute_paths( r_tm_re /= free_space_loss; r_tm_im /= free_space_loss; } - /* Update the gains */ + /* Update the gains as a' = a * refl_coefs */ a_te_re_new = a_te_re_refl[off] * r_te_re - a_te_im_refl[off] * r_te_im; a_te_im_new = a_te_re_refl[off] * r_te_im + a_te_im_refl[off] * r_te_re; a_tm_re_new = a_tm_re_refl[off] * r_tm_re - a_tm_im_refl[off] * r_tm_im; @@ -740,22 +771,31 @@ void compute_paths( a_tm_im_refl[off] = a_tm_im_new; /* Update the delay */ tau_t[off] += t / SPEED_OF_LIGHT; + + /* Move and reflect the ray */ + r = rays[off]; /* Advance the ray to the hit point */ - *h = vec3_scale(&rays[off].d, t); - rays[off].o = vec3_add(h, &rays[off].o); - /* Reflect the ray's direction */ - t = vec3_dot(&rays[off].d, &n); + *h = vec3_scale(&r.d, t); + r.o = vec3_add(h, &r.o); + /* Reflect the ray's direction as d' = d - 2*(d \cdot n)*n */ + t = vec3_dot(&r.d, &n); *h = vec3_scale(&n, 2.f * t); - rays[off].d = vec3_sub(&rays[off].d, h); - /* Advance the ray a bit to avoid self-intersection */ - *h = vec3_scale(&rays[off].d, 1e-4f); - rays[off].o = vec3_add(&rays[off].o, h); + r.d = vec3_sub(&r.d, h); + /* Advance the ray origin a bit to avoid intersection with the same triangle */ + *h = vec3_scale(&r.d, 1e-4f); + r.o = vec3_add(&r.o, h); + /* Save the reflected ray */ + rays[off] = r; + + /***********************************************************************/ + /* Scatter the rays */ + /***********************************************************************/ /* Scatter a path from the hit point towards the rx */ - r.o = rays[off].o; for (j = 0; j != num_rx; ++j) { off_scat = i * num_rx * num_tx * num_paths + j * num_tx * num_paths + off; r.d = vec3_sub(&rx_pos_v[j], &r.o); + r.d = vec3_normalize(&r.d); ind = -1; moeller_trumbore(&r, &mesh, &t, &ind, &theta); if (ind != -1 && t <= 1.f) { @@ -765,11 +805,10 @@ void compute_paths( continue; } /* No obstacle has been hit */ - /* Save the hit point coordinates */ - off_hit_points = off_scat * 3; - hit_points[off_hit_points] = r.o.x; - hit_points[off_hit_points + 1] = r.o.y; - hit_points[off_hit_points + 2] = r.o.z; + /* Calculate the direction */ + directions_scat[off_scat * 3] = -r.d.x; + directions_scat[off_scat * 3 + 1] = -r.d.y; + directions_scat[off_scat * 3 + 2] = -r.d.z; /* Calculate the scattering angle */ theta_s = acosf(vec3_dot(&r.d, &n) / sqrtf(vec3_dot(&r.d, &r.d))); /* Calculate the scattering coefficients */ @@ -791,6 +830,11 @@ void compute_paths( a_tm_im_scat[off_scat] /= free_space_loss; } } + + /***********************************************************************/ + /* Refract the rays */ + /***********************************************************************/ + /* TODO */ } } diff --git a/compute_paths.h b/compute_paths.h @@ -23,9 +23,8 @@ * * Outputs: * - * \param hit_points global cartesean coordinates of the hit points, shape (num_bounces, num_rx, num_tx, num_paths, 3) - * * LoS: + * \param directions_los output array of directions of incidence for LoS, shape (num_rx, num_tx, 3) * \param a_te_re_los output array of real parts of transverse electric gains for LoS, shape (num_rx, num_tx) * \param a_te_im_los output array of imaginary parts of transverse electric gains for LoS, shape (num_rx, num_tx) * \param a_tm_re_los output array of real parts of transverse magnetic gains for LoS, shape (num_rx, num_tx) @@ -33,6 +32,7 @@ * \param tau_los output array of delays for LoS in seconds, shape (num_rx, num_tx) * * Scatter: + * \param directions_scat output array of directions of incidence for scatter, shape (num_rx, num_tx, num_paths, 3) * \param a_te_re_scat output array of real parts of transverse electric gains for scatter, shape (num_bounces, num_rx, num_tx, num_paths) * \param a_te_im_scat output array of imaginary parts of transverse electric gains for scatter, shape (num_bounces, num_rx, num_tx, num_paths) * \param a_tm_re_scat output array of real parts of transverse magnetic gains for scatter, shape (num_bounces, num_rx, num_tx, num_paths) @@ -50,14 +50,15 @@ void compute_paths( IN size_t num_tx, /* number of transmitters */ IN size_t num_paths, /* number of paths */ IN size_t num_bounces, /* number of bounces */ - OUT float *hit_points, /* bouncing hit points, shape (num_bounces, num_rx, num_tx, num_paths, 3) */ /* LoS */ + OUT float *directions_los, /* output array of directions of incidence (num_rx, num_tx, 3) */ OUT float *a_te_re_los, /* output array real parts of TE gains (num_rx, num_tx) */ OUT float *a_te_im_los, /* output array imaginary parts of TE gains (num_rx, num_tx) */ OUT float *a_tm_re_los, /* output array real parts of TM gains (num_rx, num_tx) */ OUT float *a_tm_im_los, /* output array imaginary parts of TM gains (num_rx, num_tx) */ OUT float *tau_los, /* output array of delays (num_rx, num_tx) */ /* Scatter */ + OUT float *directions_scat, /* output array of directions of incidence (num_rx, num_tx, num_paths, 3) */ OUT float *a_te_re_scat, /* output array real parts of TE gains (num_bounces, num_rx, num_tx, num_paths) */ OUT float *a_te_im_scat, /* output array imaginary parts of TE gains (num_bounces, num_rx, num_tx, num_paths) */ OUT float *a_tm_re_scat, /* output array real parts of TM gains (num_bounces, num_rx, num_tx, num_paths) */ diff --git a/compute_paths_pybind11.cpp b/compute_paths_pybind11.cpp @@ -11,11 +11,20 @@ extern "C" { namespace py = pybind11; +// Helper function to create a numpy array from a C array +py::array_t<float> make_py_array(std::vector<size_t> shape, float *data) { + return py::array_t<float>( + //shape, data, py::capsule(data, [](float *ptr) { delete[] ptr; }) + shape, data + ); +} + std::tuple< - py::array_t<float>, // hit_points + py::array_t<float>, // directions_los py::array_t<float>, py::array_t<float>, // a_te_re_los, a_te_im_los py::array_t<float>, py::array_t<float>, // a_tm_re_los, a_tm_im_los py::array_t<float>, // tau_los + py::array_t<float>, // directions_scat py::array_t<float>, py::array_t<float>, // a_te_re_scat, a_te_im_scat py::array_t<float>, py::array_t<float>, // a_tm_re_scat, a_tm_im_scat py::array_t<float> // tau_scat @@ -39,12 +48,13 @@ compute_paths_wrapper( py::buffer_info tx_vel_info = tx_velocities.request(); // Output - float *hit_points = new float[num_bounces * num_rx * num_tx * num_paths * 3]; + float *directions_los = new float[num_rx * num_tx * 3]; float *a_te_re_los = new float[num_rx * num_tx]; float *a_te_im_los = new float[num_rx * num_tx]; float *a_tm_re_los = new float[num_rx * num_tx]; float *a_tm_im_los = new float[num_rx * num_tx]; float *tau_los = new float[num_rx * num_tx]; + float *directions_scat = new float[num_rx * num_tx * num_paths * 3]; float *a_te_re_scat = new float[num_bounces * num_rx * num_tx * num_paths]; float *a_te_im_scat = new float[num_bounces * num_rx * num_tx * num_paths]; float *a_tm_re_scat = new float[num_bounces * num_rx * num_tx * num_paths]; @@ -63,48 +73,36 @@ compute_paths_wrapper( (size_t)num_tx, (size_t)num_paths, (size_t)num_bounces, - hit_points, // Hit points - a_te_re_los, a_te_im_los, a_tm_re_los, a_tm_im_los, tau_los, // LoS outputs - a_te_re_scat, a_te_im_scat, a_tm_re_scat, a_tm_im_scat, tau_scat // Scatter outputs + // LoS outputs + directions_los, + a_te_re_los, + a_te_im_los, + a_tm_re_los, + a_tm_im_los, + tau_los, + // Scatter outputs + directions_scat, + a_te_re_scat, + a_te_im_scat, + a_tm_re_scat, + a_tm_im_scat, + tau_scat ); - // Convert output arrays into numpy arrays for easy use in Python - // TODO prevent copying data - // TODO construct np.complex64 array for a - py::array_t<float> hit_points_array = py::array_t<float>({num_bounces, num_rx, num_tx, num_paths, 3}, hit_points); - py::array_t<float> a_te_re_los_array = py::array_t<float>({num_rx, num_tx}, a_te_re_los); - py::array_t<float> a_te_im_los_array = py::array_t<float>({num_rx, num_tx}, a_te_im_los); - py::array_t<float> a_tm_re_los_array = py::array_t<float>({num_rx, num_tx}, a_tm_re_los); - py::array_t<float> a_tm_im_los_array = py::array_t<float>({num_rx, num_tx}, a_tm_im_los); - py::array_t<float> tau_los_array = py::array_t<float>({num_rx, num_tx}, tau_los); - py::array_t<float> a_te_re_scat_array = py::array_t<float>({num_bounces, num_rx, num_tx, num_paths}, a_te_re_scat); - py::array_t<float> a_te_im_scat_array = py::array_t<float>({num_bounces, num_rx, num_tx, num_paths}, a_te_im_scat); - py::array_t<float> a_tm_re_scat_array = py::array_t<float>({num_bounces, num_rx, num_tx, num_paths}, a_tm_re_scat); - py::array_t<float> a_tm_im_scat_array = py::array_t<float>({num_bounces, num_rx, num_tx, num_paths}, a_tm_im_scat); - py::array_t<float> tau_scat_array = py::array_t<float>({num_bounces, num_rx, num_tx, num_paths}, tau_scat); - - // Deallocate arrays - delete[] hit_points; - delete[] a_te_re_los; - delete[] a_te_im_los; - delete[] a_tm_re_los; - delete[] a_tm_im_los; - delete[] tau_los; - delete[] a_te_re_scat; - delete[] a_te_im_scat; - delete[] a_tm_re_scat; - delete[] a_tm_im_scat; - delete[] tau_scat; - - // Return the results as a tuple (gains, delays) + // Cast to numpy arrays and return return std::make_tuple( - hit_points_array, - a_te_re_los_array, a_te_im_los_array, - a_tm_re_los_array, a_tm_im_los_array, - tau_los_array, - a_te_re_scat_array, a_te_im_scat_array, - a_tm_re_scat_array, a_tm_im_scat_array, - tau_scat_array + make_py_array({num_rx, num_tx, 3}, directions_los), + make_py_array({num_rx, num_tx}, a_te_re_los), + make_py_array({num_rx, num_tx}, a_te_im_los), + make_py_array({num_rx, num_tx}, a_tm_re_los), + make_py_array({num_rx, num_tx}, a_tm_im_los), + make_py_array({num_rx, num_tx}, tau_los), + make_py_array({num_rx, num_tx, num_paths, 3}, directions_scat), + make_py_array({num_bounces, num_rx, num_tx, num_paths}, a_te_re_scat), + make_py_array({num_bounces, num_rx, num_tx, num_paths}, a_te_im_scat), + make_py_array({num_bounces, num_rx, num_tx, num_paths}, a_tm_re_scat), + make_py_array({num_bounces, num_rx, num_tx, num_paths}, a_tm_im_scat), + make_py_array({num_bounces, num_rx, num_tx, num_paths}, tau_scat) ); } diff --git a/test.c b/test.c @@ -19,12 +19,13 @@ int main(int argc, char **argv) size_t num_tx = 1; size_t num_paths = 10000; size_t num_bounces = 3; - float *hit_points = (float*)malloc(num_bounces * num_rx * num_tx * num_paths * 3 * sizeof(float)); + float *directions_los = (float*)malloc(num_rx * num_tx * 3 * sizeof(float)); float *a_te_re_los = (float*)malloc(num_rx * num_tx * sizeof(float)); float *a_te_im_los = (float*)malloc(num_rx * num_tx * sizeof(float)); float *a_tm_re_los = (float*)malloc(num_rx * num_tx * sizeof(float)); float *a_tm_im_los = (float*)malloc(num_rx * num_tx * sizeof(float)); float *tau_los = (float*)malloc(num_rx * num_tx * sizeof(float)); + float *directions_scat = (float*)malloc(num_rx * num_tx * num_paths * 3 * sizeof(float)); float *a_te_re_scat = (float*)malloc(num_bounces * num_rx * num_tx * num_paths * sizeof(float)); float *a_te_im_scat = (float*)malloc(num_bounces * num_rx * num_tx * num_paths * sizeof(float)); float *a_tm_re_scat = (float*)malloc(num_bounces * num_rx * num_tx * num_paths * sizeof(float)); @@ -38,9 +39,8 @@ int main(int argc, char **argv) tx_velocities, carrier_frequency, num_rx, num_tx, num_paths, num_bounces, - hit_points, - a_te_re_los, a_te_im_los, a_tm_re_los, a_tm_im_los, tau_los, - a_te_re_scat, a_te_im_scat, a_tm_re_scat, a_tm_im_scat, tau_scat + directions_los, a_te_re_los, a_te_im_los, a_tm_re_los, a_tm_im_los, tau_los, + directions_scat, a_te_re_scat, a_te_im_scat, a_tm_re_scat, a_tm_im_scat, tau_scat ); /* Save the results */ @@ -51,12 +51,13 @@ int main(int argc, char **argv) fwrite(data, sizeof(float), size, f); \ fclose(f); - WRITE_BIN("hit_points.bin", hit_points, num_bounces * num_rx * num_tx * num_paths * 3); + WRITE_BIN("directions_los.bin", directions_los, num_rx * num_tx * 3); WRITE_BIN("a_te_re_los.bin", a_te_re_los, num_rx * num_tx); WRITE_BIN("a_te_im_los.bin", a_te_im_los, num_rx * num_tx); WRITE_BIN("a_tm_re_los.bin", a_tm_re_los, num_rx * num_tx); WRITE_BIN("a_tm_im_los.bin", a_tm_im_los, num_rx * num_tx); WRITE_BIN("tau_los.bin", tau_los, num_rx * num_tx); + WRITE_BIN("directions_scat.bin", directions_scat, num_rx * num_tx * num_paths * 3); WRITE_BIN("a_te_re_scat.bin", a_te_re_scat, num_bounces * num_rx * num_tx * num_paths); WRITE_BIN("a_te_im_scat.bin", a_te_im_scat, num_bounces * num_rx * num_tx * num_paths); WRITE_BIN("a_tm_re_scat.bin", a_tm_re_scat, num_bounces * num_rx * num_tx * num_paths); diff --git a/test.py b/test.py @@ -5,7 +5,7 @@ from rt import compute_paths # Define inputs mesh_filepath = __file__[:__file__.rfind('/') + 1] + 'scenes/box.ply' rx_positions = np.array([[0., 0., 2.5]], dtype=np.float64) -tx_positions = np.array([[0., 0., 2.5]], dtype=np.float64) +tx_positions = np.array([[0., 0., 2.51]], dtype=np.float64) rx_velocities = np.array([[0., 0., 0.]], dtype=np.float64) tx_velocities = np.array([[0., 0., 0.]], dtype=np.float64) carrier_frequency = 3.0 @@ -27,17 +27,19 @@ output = compute_paths( num_paths, num_bounces ) -hit_points = output[0] +directions_los = output[0] a_te_los = output[1] + 1.j*output[2] a_tm_los = output[3] + 1.j*output[4] tau_los = output[5] -a_te_scat = output[6] + 1.j*output[7] -a_tm_scat = output[8] + 1.j*output[9] -tau_scat = output[10] +directions_scat = output[6] +a_te_scat = output[7] + 1.j*output[8] +a_tm_scat = output[9] + 1.j*output[10] +tau_scat = output[11] print("#####################") print("LoS:") print("#####################") +print(f"shape(directions_los): {directions_los.shape}") print(f"shape(a_te_los): {a_te_los.shape}") print(f"Delays: {tau_los}") print(f"a_te_los.real min, max: {np.min(a_te_los.real)}, {np.max(a_te_los.real)}") @@ -48,6 +50,7 @@ print(f"a_tm_los.imag min, max: {np.min(a_tm_los.imag)}, {np.max(a_tm_los.imag)} print("\n#####################") print("Scatter:") print("#####################") +print(f"shape(directions_scat): {directions_scat.shape}") print(f"shape(a_te_scat): {a_te_scat.shape}") print(f"Delays: {tau_scat}") print(f"a_te_scat.real min, max: {np.min(a_te_scat.real)}, {np.max(a_te_scat.real)}") @@ -56,6 +59,5 @@ print(f"a_tm_scat.real min, max: {np.min(a_tm_scat.real)}, {np.max(a_tm_scat.rea print(f"a_tm_scat.imag min, max: {np.min(a_tm_scat.imag)}, {np.max(a_tm_scat.imag)}") # Asssert shapes -assert hit_points.shape == (num_bounces, num_rx, num_tx, num_paths, 3) assert a_te_los.shape == a_tm_los.shape == tau_los.shape == (num_rx, num_tx) assert a_te_scat.shape == a_tm_scat.shape == tau_scat.shape == (num_bounces, num_rx, num_tx, num_paths)