Terms
Description
compute_paths gives you geometry. Vertices, object indices, a mask. But no power, no fields. The EM module has fresnel_coefficients, sp_directions, Dipole.fields, the material database, etc. but there's nothing that strings them together for you.
The only place the full pipeline actually works is deepmimo.py export (lines 410-494), inside a plugin most users won't find. #355 tried to fix accuracy there and got stuck with ~3-16 degrees of phase error on multi-bounce.
Without an easy way to go from geometry to power, the tutorials end up doing 0.5 ** num_bounces / path_length**2 (see smoothing.ipynb).
Jerome has mentioned that EM/radio-propagation features are a known gap, since his thesis focused on the geometry side of ray paths. He's expressed interest in contributions here.
I'm proposing a small differt/em/_pipeline.py module with a few functions, exposed at differt.em:
@eqx.filter_jit
def compute_received_fields(
paths: Paths,
mesh: TriangleMesh,
frequency: Float[ArrayLike, ""],
*,
antenna_tx: BaseAntenna | None = None,
antenna_rx: BaseAntenna | None = None,
polarization: Float[ArrayLike, "3"] = jnp.array([0.0, 0.0, 1.0]),
) -> tuple[Complex[Array, "*batch 3"], Float[Array, "*batch"]]:
"""Per-path complex E-field vector and propagation delay."""
@eqx.filter_jit
def compute_received_power(
paths: Paths,
mesh: TriangleMesh,
frequency: Float[ArrayLike, ""],
*,
dB: bool = False,
coherent: bool = True,
**kwargs,
) -> Float[Array, " *reduced_batch"]:
"""Total received power, summed over paths."""
@eqx.filter_jit
def compute_cir(
paths: Paths,
mesh: TriangleMesh,
frequency: Float[ArrayLike, ""],
**kwargs,
) -> tuple[Complex[Array, " *reduced_batch"], Float[Array, " *reduced_batch"]]:
"""Channel impulse response: (complex amplitude, delay) per path."""
These take Paths + TriangleMesh instead of TriangleScene so they work at any abstraction level.
Internally this is mostly pulling the logic out of deepmimo.py:410-494 into a standalone function: compute segment directions, look up materials, get Fresnel coefficients per bounce, project into s/p basis, apply coefficients, spreading factor, phase shift. It should dispatch on interaction_types so once #429 (diffraction_coefficients) is done, diffraction just plugs in. For now it only needs to handle REFLECTION.
When paths.mask is float-valued (smoothing mode), fields get multiplied by confidence rather than boolean-indexed, so jax.grad(compute_received_power)(...) works out of the box.
Once this exists, deepmimo.export() should just call it internally.
For validation I'd compare LOS against analytic fspl(), single reflection against the manual example in the reflection_coefficients docstring (which gets Brewster angle right), and multi-reflection against Sionna on a simple scene.
This would also make #430 (compute_coverage_map) and #431 (transition_matrices) possible, since both need a way to get power from paths. It supersedes #355, which tried to fix EM in the DeepMIMO export and got stuck. #206 and #202 also need this before they can be closed.
Terms
Description
compute_pathsgives you geometry. Vertices, object indices, a mask. But no power, no fields. The EM module hasfresnel_coefficients,sp_directions,Dipole.fields, the material database, etc. but there's nothing that strings them together for you.The only place the full pipeline actually works is
deepmimo.pyexport (lines 410-494), inside a plugin most users won't find. #355 tried to fix accuracy there and got stuck with ~3-16 degrees of phase error on multi-bounce.Without an easy way to go from geometry to power, the tutorials end up doing
0.5 ** num_bounces / path_length**2(seesmoothing.ipynb).Jerome has mentioned that EM/radio-propagation features are a known gap, since his thesis focused on the geometry side of ray paths. He's expressed interest in contributions here.
I'm proposing a small
differt/em/_pipeline.pymodule with a few functions, exposed atdiffert.em:These take
Paths+TriangleMeshinstead ofTriangleSceneso they work at any abstraction level.Internally this is mostly pulling the logic out of
deepmimo.py:410-494into a standalone function: compute segment directions, look up materials, get Fresnel coefficients per bounce, project into s/p basis, apply coefficients, spreading factor, phase shift. It should dispatch oninteraction_typesso once #429 (diffraction_coefficients) is done, diffraction just plugs in. For now it only needs to handleREFLECTION.When
paths.maskis float-valued (smoothing mode), fields get multiplied by confidence rather than boolean-indexed, sojax.grad(compute_received_power)(...)works out of the box.Once this exists,
deepmimo.export()should just call it internally.For validation I'd compare LOS against analytic
fspl(), single reflection against the manual example in thereflection_coefficientsdocstring (which gets Brewster angle right), and multi-reflection against Sionna on a simple scene.This would also make #430 (
compute_coverage_map) and #431 (transition_matrices) possible, since both need a way to get power from paths. It supersedes #355, which tried to fix EM in the DeepMIMO export and got stuck. #206 and #202 also need this before they can be closed.