hermespy-rt

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

commit a33a3e9722d8156792bb4cdd38bdd0522455f847
parent 4c724153aee45f424e5628508317387ee4e7c8e9
Author: Egor Achkasov <eaachkasov@edu.hse.ru>
Date:   Wed, 15 Jan 2025 10:39:05 +0100

Impl LoS, Scatter; Output only active paths

Diffstat:
Mcompute_paths.c | 178++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mcompute_paths.h | 12+++++++-----
Mcompute_paths_pybind11.cpp | 24+++++++++++-------------
Mtest.c | 18++++++++----------
4 files changed, 143 insertions(+), 89 deletions(-)

diff --git a/compute_paths.c b/compute_paths.c @@ -390,20 +390,25 @@ void compute_paths( IN size_t num_paths, /* number of paths */ IN size_t num_bounces, /* number of bounces */ IN size_t num_samples, /* number of samples */ - OUT float *a_te_re, /* output array real parts of TE gains (num_rx, num_tx, num_paths) */ - OUT float *a_te_im, /* output array imaginary parts of TE gains (num_rx, num_tx, num_paths) */ - OUT float *a_tm_re, /* output array real parts of TM gains (num_rx, num_tx, num_paths) */ - OUT float *a_tm_im, /* output array imaginary parts of TM gains (num_rx, num_tx, num_paths) */ - OUT float *tau /* output array of delays (num_rx, num_tx, num_paths) */ + OUT size_t *num_paths_out, /* number of paths computed */ + OUT float *a_te_re, /* output array real parts of TE gains (num_rx, num_tx, num_paths_out) */ + OUT float *a_te_im, /* output array imaginary parts of TE gains (num_rx, num_tx, num_paths_out) */ + OUT float *a_tm_re, /* output array real parts of TM gains (num_rx, num_tx, num_paths_out) */ + OUT float *a_tm_im, /* output array imaginary parts of TM gains (num_rx, num_tx, num_paths_out) */ + OUT float *tau /* output array of delays (num_rx, num_tx, num_paths_out) */ ) { + /* Init loop indices and offset variables */ + size_t i, j; + size_t off; + /* Load the scene */ Mesh mesh = load_mesh_ply(mesh_filepath, carrier_frequency); /* Calculate a fibonacci sphere */ Vec3 *ray_directions = (Vec3*)malloc(num_paths * sizeof(Vec3)); float k, phi, theta; - for (size_t i = 0; i < num_paths; ++i) { + for (i = 0; i < num_paths; ++i) { k = (float)i + .5f; phi = acos(1.f - 2.f * k / num_paths); theta = PI * (1.f + sqrtf(5.f)) * k; @@ -418,24 +423,43 @@ void compute_paths( /* Shape (num_tx, num_paths) */ Ray *rays = (Ray*)malloc(num_tx * num_paths * sizeof(Ray)); Vec3 tx_position; - for (size_t i = 0; i < num_tx; ++i) { + off = 0; + for (i = 0; i < num_tx; ++i) { tx_position = (Vec3){tx_positions[i * 3], tx_positions[i * 3 + 1], tx_positions[i * 3 + 2]}; - for (size_t j = 0; j < num_paths; ++j) { - rays[i * num_paths + j].o = tx_position; - rays[i * num_paths + j].d = ray_directions[j]; + for (j = 0; j < num_paths; ++j) { + rays[off].o = tx_position; + rays[off].d = ray_directions[j]; + ++off; } } free(ray_directions); /* Initialize variables needed for the RT */ + /* Number of output paths is unknown at this point */ + *num_paths_out = 0; + /* As num_paths_out is not known in advance, + * we will realloc the output arrays during propagation. + * We will start with a capacity of num_paths_out_realloc_cap + * and reallocate the arrays when needed. + */ + const size_t num_paths_out_realloc_cap = 1024; + size_t num_cur_allocs = 1; + /* Init the output gains and delays */ + /* shape (num_rx, num_tx, num_paths_out) */ + a_te_re = (float*)malloc(num_rx * num_tx * num_paths_out_realloc_cap * sizeof(float)); + a_te_im = (float*)malloc(num_rx * num_tx * num_paths_out_realloc_cap * sizeof(float)); + a_tm_re = (float*)malloc(num_rx * num_tx * num_paths_out_realloc_cap * sizeof(float)); + a_tm_im = (float*)malloc(num_rx * num_tx * num_paths_out_realloc_cap * sizeof(float)); + tau = (float*)malloc(num_rx * num_tx * num_paths_out_realloc_cap * sizeof(float)); + /* Bouncing rays gains and delays */ /* shape (num_tx, num_paths) */ - float *tau_t = (float*)calloc(num_tx * num_paths, sizeof(float)); float *a_te_re_t = (float*)malloc(num_tx * num_paths * sizeof(float)); float *a_te_im_t = (float*)calloc(num_tx * num_paths, sizeof(float)); float *a_tm_re_t = (float*)malloc(num_tx * num_paths * sizeof(float)); float *a_tm_im_t = (float*)calloc(num_tx * num_paths, sizeof(float)); for (size_t i = 0; i < num_tx * num_paths; ++i) a_te_re_t[i] = a_tm_re_t[i] = 1.f; + float *tau_t = (float*)calloc(num_tx * num_paths, sizeof(float)); /* Active rays bitmask. 1 if the ray is still traced, 0 if it has left the scene */ uint8_t *active = (uint8_t*)malloc((num_tx * num_paths / 8 + 1) * sizeof(uint8_t)); for (size_t i = 0; i < num_tx * num_paths / 8 + 1; ++i) @@ -444,29 +468,63 @@ void compute_paths( float t, r_te_re, r_te_im, r_tm_re, r_tm_im; float a_te_re_new, a_te_im_new, a_tm_re_new, a_tm_im_new; int32_t ind; - Ray *r; + Ray r, *r_ptr; Vec3 *h = (Vec3*)malloc(sizeof(Vec3)); /* Friis free space loss multiplier. Multiply this by a distance and take the second power to get a free space loss. */ float free_space_loss_multiplier = 2.f * PI * carrier_frequency * 1e9 / SPEED_OF_LIGHT; float free_space_loss; + /* Calculate LoS paths directly from tx to rx */ + for (i = 0; i != num_rx; ++i) + for (j = 0; j != num_tx; ++j) { + t = -1.f; + ind = -1; + r.o = (Vec3){tx_positions[j * 3], tx_positions[j * 3 + 1], tx_positions[j * 3 + 2]}; + r.d = (Vec3){rx_positions[i * 3], rx_positions[i * 3 + 1], rx_positions[i * 3 + 2]}; + r.d = vec3_sub(&r.d, &r.o); + moeller_trumbore(&r, &mesh, &t, &ind, &theta); + if (ind == -1) continue; + /* No triangle has been hit, the path is LoS */ + /* Calculate teh distance */ + t = sqrtf(vec3_dot(&r.d, &r.d)); + /* Calculate the delay */ + tau[*num_paths_out] = t / SPEED_OF_LIGHT; + /* Calculate the free space loss */ + free_space_loss = free_space_loss_multiplier * t; + free_space_loss = free_space_loss * free_space_loss; + /* Set the gains for this LoS path */ + a_te_re[*num_paths_out] = a_tm_re[*num_paths_out] = 1.f / free_space_loss; + a_te_im[*num_paths_out] = a_tm_im[*num_paths_out] = 0.f; + /* Increment the number of paths */ + ++(*num_paths_out); + /* Realloc the output arrays if needed */ + if (*num_paths_out % num_paths_out_realloc_cap == 0) { + ++num_cur_allocs; + a_te_re = (float*)realloc(a_te_re, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + a_te_im = (float*)realloc(a_te_im, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + a_tm_re = (float*)realloc(a_tm_re, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + a_tm_im = (float*)realloc(a_tm_im, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + tau = (float*)realloc(tau, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + } + } + /* 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 */ - for (size_t i = 0; i < num_bounces; ++i) { - for (size_t off = 0; off != num_tx * num_paths; ++off) { + for (i = 0; i < num_bounces; ++i) { + for (off = 0; off != num_tx * num_paths; ++off) { /* Check if the ray is active */ if (!(active[off / 8] & (1 << (off % 8)))) continue; /* Init */ t = -1.f; ind = -1; - r = &rays[off]; + r_ptr = &rays[off]; /* Find the hit point and trinagle. Calculate an angle of incidence */ - moeller_trumbore(r, &mesh, &t, &ind, &theta); + moeller_trumbore(r_ptr, &mesh, &t, &ind, &theta); if (ind == -1) { /* Ray hit nothing */ active[off / 8] &= ~(1 << (off % 8)); a_te_im_t[off] = a_te_re_t[off] = 0.f; @@ -497,57 +555,55 @@ void compute_paths( a_tm_re_t[off] = a_tm_re_new; a_tm_im_t[off] = a_tm_im_new; /* Update the delay */ - tau[off] += t / SPEED_OF_LIGHT; + tau_t[off] += t / SPEED_OF_LIGHT; /* Advance the ray to the hit point */ - *h = vec3_scale(&r->d, t); - r->o = vec3_add(h, &r->o); + *h = vec3_scale(&r_ptr->d, t); + r_ptr->o = vec3_add(h, &r_ptr->o); + /* Scatter a path from the hit point towards the rx */ + r.o = r_ptr->o; + for (j = 0; j != num_rx; ++j) { + r.d = (Vec3){rx_positions[j * 3], rx_positions[j * 3 + 1], rx_positions[j * 3 + 2]}; + r.d = vec3_sub(&r.d, &r.o); + ind = -1; + moeller_trumbore(&r, &mesh, &t, &ind, &theta); + if (ind == -1) continue; + /* No obstacle has been hit */ + /* Calculate the distance */ + t = sqrtf(vec3_dot(&r.d, &r.d)); + /* Calculate the delay */ + tau[*num_paths_out] = tau_t[off] + t / SPEED_OF_LIGHT; + /* Calculate the free space loss */ + free_space_loss = free_space_loss_multiplier * t; + free_space_loss = free_space_loss * free_space_loss; + /* Set the gains */ + a_te_re[*num_paths_out] = a_te_re_t[off] / free_space_loss; + a_te_im[*num_paths_out] = a_te_im_t[off] / free_space_loss; + a_tm_re[*num_paths_out] = a_tm_re_t[off] / free_space_loss; + a_tm_im[*num_paths_out] = a_tm_im_t[off] / free_space_loss; + /* Increment the number of paths */ + ++(*num_paths_out); + /* Realloc the output arrays if needed */ + if (*num_paths_out % num_paths_out_realloc_cap == 0) { + ++num_cur_allocs; + a_te_re = (float*)realloc(a_te_re, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + a_te_im = (float*)realloc(a_te_im, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + a_tm_re = (float*)realloc(a_tm_re, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + a_tm_im = (float*)realloc(a_tm_im, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + tau = (float*)realloc(tau, num_rx * num_tx * num_cur_allocs * num_paths_out_realloc_cap * sizeof(float)); + } + } } } - free(h); - /* Init a */ - for (size_t i = 0; i < num_rx * num_tx * num_paths; ++i) { - a_te_re[i] = a_te_im[i] = 0.f; - a_tm_re[i] = a_tm_im[i] = 0.f; - } - - /* Do the final propagations from the final hit points - * directly to the receivers. - * Also broadcast the gains and delays to rx - * by adding num_rx dimension to the local tau_t and a_t. - */ - size_t off_a; - for (size_t i = 0; i < num_rx; ++i) - for (size_t off = 0; off != num_tx * num_paths; ++off) { - /* Check if the ray is active */ - if (!(active[off / 8] & (1 << (off % 8)))) - continue; - /* Init */ - t = -1.f; - ind = -1; - r = &rays[off]; - r->d = (Vec3){rx_positions[i * 3], rx_positions[i * 3 + 1], rx_positions[i * 3 + 2]}; - r->d = vec3_sub(&r->d, &r->o); - off_a = i * num_tx * num_paths + off; - /* See if the return ray hits any abstacle */ - moeller_trumbore(r, &mesh, &t, &ind, &theta); - if (ind == -1) { /* Return ray hit nothing */ - tau[off_a] = -1.; - a_te_im[off_a] = a_te_re[off_a] = 0.f; - a_tm_im[off_a] = a_tm_re[off_a] = 0.f; - continue; - } - /* Set the gains */ - a_te_re[off_a] = a_te_re_t[off]; - a_te_im[off_a] = a_te_im_t[off]; - a_tm_re[off_a] = a_tm_re_t[off]; - a_tm_im[off_a] = a_tm_im_t[off]; - /* Set the delays */ - t = sqrtf(vec3_dot(&r->d, &r->d)); - tau[off_a] = tau_t[off] + t / SPEED_OF_LIGHT; - } + /* Realloc the output arrays to the actual number of paths */ + a_te_re = (float*)realloc(a_te_re, num_rx * num_tx * *num_paths_out * sizeof(float)); + a_te_im = (float*)realloc(a_te_im, num_rx * num_tx * *num_paths_out * sizeof(float)); + a_tm_re = (float*)realloc(a_tm_re, num_rx * num_tx * *num_paths_out * sizeof(float)); + a_tm_im = (float*)realloc(a_tm_im, num_rx * num_tx * *num_paths_out * sizeof(float)); + tau = (float*)realloc(tau, num_rx * num_tx * *num_paths_out * sizeof(float)); /* Free */ + free(h); free(active); free(rays); free_mesh(&mesh); diff --git a/compute_paths.h b/compute_paths.h @@ -22,6 +22,7 @@ * \param num_paths number of paths to compute. Must be > 0 * \param num_bounces number of bounces to compute. Must be > 0 * \param num_samples number of samples in the original signal. Must be > 0 + * \param num_paths_out output number of paths computed. That includes LoS, scatter, and bounces that never left the scene. * \param a_te_re output array of real parts of transverse electric gains, shape (num_rx, num_tx, num_paths) * \param a_te_im output array of imaginary parts of transverse electric gains, shape (num_rx, num_tx, num_paths) * \param a_tm_re output array of real parts of transverse magnetic gains, shape (num_rx, num_tx, num_paths) @@ -41,11 +42,12 @@ void compute_paths( IN size_t num_paths, /* number of paths */ IN size_t num_bounces, /* number of bounces */ IN size_t num_samples, /* number of samples */ - OUT float *a_te_re, /* output array real parts of TE gains (num_rx, num_tx, num_paths) */ - OUT float *a_te_im, /* output array imaginary parts of TE gains (num_rx, num_tx, num_paths) */ - OUT float *a_tm_re, /* output array real parts of TM gains (num_rx, num_tx, num_paths) */ - OUT float *a_tm_im, /* output array imaginary parts of TM gains (num_rx, num_tx, num_paths) */ - OUT float *tau /* output array of delays (num_rx, num_tx, num_paths) */ + OUT size_t *num_paths_out, /* number of paths computed */ + OUT float *a_te_re, /* output array real parts of TE gains (num_rx, num_tx, num_paths_out) */ + OUT float *a_te_im, /* output array imaginary parts of TE gains (num_rx, num_tx, num_paths_out) */ + OUT float *a_tm_re, /* output array real parts of TM gains (num_rx, num_tx, num_paths_out) */ + OUT float *a_tm_im, /* output array imaginary parts of TM gains (num_rx, num_tx, num_paths_out) */ + OUT float *tau /* output array of delays (num_rx, num_tx, num_paths_out) */ ); #endif /* COMPUTE_PATHS_H */ diff --git a/compute_paths_pybind11.cpp b/compute_paths_pybind11.cpp @@ -35,12 +35,9 @@ compute_paths_wrapper( py::buffer_info rx_vel_info = rx_velocities.request(); py::buffer_info tx_vel_info = tx_velocities.request(); - // Output arrays - float *a_te_im = new float[num_tx * num_paths]; - float *a_te_re = new float[num_tx * num_paths]; - float *a_tm_im = new float[num_tx * num_paths]; - float *a_tm_re = new float[num_tx * num_paths]; - float *tau = new float[num_tx * num_paths]; + // Output + size_t num_paths_out; + float *a_te_re, *a_te_im, *a_tm_re, *a_tm_im, *tau; // Call the C function compute_paths( @@ -56,6 +53,7 @@ compute_paths_wrapper( (size_t)num_paths, (size_t)num_bounces, (size_t)num_samples, + &num_paths_out, a_te_re, a_te_im, a_tm_re, a_tm_im, // Gains tau // Delays ); @@ -63,17 +61,17 @@ compute_paths_wrapper( // 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> a_te_im_array = py::array_t<float>({num_rx, num_tx, num_paths}, a_te_im); - py::array_t<float> a_te_re_array = py::array_t<float>({num_rx, num_tx, num_paths}, a_te_re); - py::array_t<float> a_tm_im_array = py::array_t<float>({num_rx, num_tx, num_paths}, a_tm_im); - py::array_t<float> a_tm_re_array = py::array_t<float>({num_rx, num_tx, num_paths}, a_tm_re); - py::array_t<float> tau_array = py::array_t<float>({num_rx, num_tx, num_paths}, tau); + py::array_t<float> a_te_re_array = py::array_t<float>({num_rx, num_tx, (int)num_paths_out}, a_te_re); + py::array_t<float> a_te_im_array = py::array_t<float>({num_rx, num_tx, (int)num_paths_out}, a_te_im); + py::array_t<float> a_tm_re_array = py::array_t<float>({num_rx, num_tx, (int)num_paths_out}, a_tm_re); + py::array_t<float> a_tm_im_array = py::array_t<float>({num_rx, num_tx, (int)num_paths_out}, a_tm_im); + py::array_t<float> tau_array = py::array_t<float>({num_rx, num_tx, (int)num_paths_out}, tau); // Deallocate arrays - delete[] a_te_im; delete[] a_te_re; - delete[] a_tm_im; + delete[] a_te_im; delete[] a_tm_re; + delete[] a_tm_im; delete[] tau; // Return the results as a tuple (gains, delays) diff --git a/test.c b/test.c @@ -21,11 +21,8 @@ int main(int argc, char **argv) size_t num_paths = 10000; size_t num_bounces = 3; size_t num_samples = 150; - float *a_te_re = (float*)malloc(num_rx * num_tx * num_paths * sizeof(float)); - float *a_te_im = (float*)malloc(num_rx * num_tx * num_paths * sizeof(float)); - float *a_tm_re = (float*)malloc(num_rx * num_tx * num_paths * sizeof(float)); - float *a_tm_im = (float*)malloc(num_rx * num_tx * num_paths * sizeof(float)); - float *tau = (float*)malloc(num_rx * num_tx * num_paths * sizeof(float)); + size_t num_paths_out; + float *a_te_re, *a_te_im, *a_tm_re, *a_tm_im, *tau; compute_paths( argv[1], rx_positions, @@ -35,23 +32,24 @@ int main(int argc, char **argv) carrier_frequency, sampling_frequency, num_rx, num_tx, num_paths, num_bounces, num_samples, + &num_paths_out, a_te_re, a_te_im, a_tm_re, a_tm_im, tau); /* Save the results */ FILE *f = fopen("a_te_re.bin", "wb"); - fwrite(a_te_re, sizeof(float), num_rx * num_tx * num_paths, f); + fwrite(a_te_re, sizeof(float), num_rx * num_tx * num_paths_out, f); fclose(f); f = fopen("a_te_im.bin", "wb"); - fwrite(a_te_im, sizeof(float), num_rx * num_tx * num_paths, f); + fwrite(a_te_im, sizeof(float), num_rx * num_tx * num_paths_out, f); fclose(f); f = fopen("a_tm_re.bin", "wb"); - fwrite(a_tm_re, sizeof(float), num_rx * num_tx * num_paths, f); + fwrite(a_tm_re, sizeof(float), num_rx * num_tx * num_paths_out, f); fclose(f); f = fopen("a_tm_im.bin", "wb"); - fwrite(a_tm_im, sizeof(float), num_rx * num_tx * num_paths, f); + fwrite(a_tm_im, sizeof(float), num_rx * num_tx * num_paths_out, f); fclose(f); f = fopen("tau.bin", "wb"); - fwrite(tau, sizeof(float), num_rx * num_tx * num_paths, f); + fwrite(tau, sizeof(float), num_rx * num_tx * num_paths_out, f); fclose(f); }