For the complete documentation index, see llms.txt. This page is also available as Markdown.

Ranging and remote-sensing kernels

LiDAR point-cloud geometry and vegetation-return metrics, LiDAR-derived terrain/canopy mapping and survey QA, and the core radar range/Doppler/link-budget equations.

Three modules for active-sensing systems that measure distance by timing a reflected pulse. lidar is airborne-survey geometry — footprint size, swath width, ground sampling distance, point density, and the vegetation and terrain metrics (canopy height, slope, a Beer-Lambert-style vegetation index) derived from first and last laser returns. lidar_mapping covers the same physical instrument from the production/QA side of turning a point cloud into a terrain model — a ground-classification threshold, a canopy-height model, leaf area index from gap fraction, and the vertical-accuracy and surface-roughness checks a mapping deliverable is validated against; it is not a SLAM or pose-graph module — there is no odometry, no scan matching, and no pose state anywhere in it. radar is the separate physics of a radio-frequency system: the range equation, Doppler frequency and velocity, range/velocity ambiguity limits, range resolution, antenna gain, radar cross section, thermal noise power, and signal-to-clutter ratio. All three take and return plain Float64 scalars — no structs, no status-code vocabulary — so every function is a single formula that either lands on the right number or a visibly wrong one.

from algenta import MojoRuntime

runtime = MojoRuntime(mode="local")

# range_from_tof: 1 microsecond round-trip time-of-flight, default c = 3.0e8 m/s
runtime.execute("lidar", "range_from_tof", [1.0e-6, 3.0e8])["result"]
# 150.0 -- R = c*t/2, pinned in tests/mojo/test_wave87.mojo

# doppler_frequency: a 300 m/s closing target at a 0.03 m (X-band-ish) wavelength
runtime.execute("radar", "doppler_frequency", [300.0, 0.03])["result"]
# 20000.0 -- fd = 2*v/lambda, pinned in tests/mojo/test_wave86.mojo

The modules

Module
What it does

lidar

Airborne LiDAR survey geometry and return-based metrics: range from time-of-flight, point density, scan swath width, laser footprint diameter, point rate from PRF, ground sampling distance, a first/last-return vegetation index, canopy height (DSM − DTM, unclamped), slope from elevation difference, range-squared intensity normalization, strip overlap percentage, and estimated point-cloud file size.

lidar_mapping

The point-cloud-to-terrain-model production and QA side of the same instrument: range from time-of-flight (nanosecond input, fixed c), point density, a canopy-height model (DSM − DTM, clamped at 0), leaf area index from gap fraction via Beer-Lambert, a ground-filter height threshold, laser beam footprint diameter, pulse return intensity from reflectance and range, along-track scan-line spacing, a vertical-accuracy residual against check points, and RMS surface roughness from plane-fit residuals.

radar

Core radar-systems equations: a simplified radar-range factor, Doppler frequency and velocity, range from round-trip time, pulse repetition interval, maximum unambiguous range and velocity, range resolution from bandwidth, antenna gain from aperture area, the radar cross section of a sphere, thermal noise power, and signal-to-clutter ratio.

Three ways to turn a travel time into a range, three different signatures

All three modules include a "range from a pulse's round-trip time" function, and the three do not agree on units, on where c comes from, or on whether it is even a parameter:

Function

Input unit

Speed of light

How c is supplied

lidar.range_from_tof(time_of_flight, c)

seconds

3.0e8

optional parameter, defaults to 3.0e8

lidar_mapping.range_from_time_of_flight(time_ns)

nanoseconds

2.998e8

hardcoded inside the function, not a parameter at all

radar.range_from_time(round_trip_time, c)

seconds

caller's choice

required parameter, no default

For the same physical round-trip time — one microsecond, i.e. 1000 ns — the three calls diverge by exactly the precision of c each one uses, not by any difference in the underlying formula (all three compute R = c·t / 2):

A caller reading lidar_mapping's number as "the same range lidar.range_from_tof would give for the same instant" is off by 0.1 m at this range purely from 2.998e8 vs 3.0e8 — small at 150 m, but the gap scales linearly with range, and passing seconds instead of nanoseconds into lidar_mapping.range_from_time_of_flight (there is no unit tag on the argument) produces a number nine orders of magnitude wrong with no error raised.

lidar: survey geometry and return-based metrics

Function
What it does

range_from_tof(time_of_flight, c=3.0e8)

c * t / 2.

point_density(n_points, area)

n_points / area; 0.0 if area <= 0.

scan_swath_width(altitude, scan_angle_deg)

2 * altitude * tan(scan_angle_deg / 2) — scan_angle_deg is the full scan angle, so the function halves it before converting to radians and taking the tangent.

footprint_diameter(altitude, divergence_mrad)

altitude * divergence_mrad / 1000.

pulse_repetition_frequency_to_point_rate(prf, scan_lines_per_sec=1.0, points_per_line=1.0)

Returns prf unchanged. Its own docstring calls this "simplified" — scan_lines_per_sec and points_per_line are accepted and defaulted but never referenced in the function body.

ground_sampling_distance(altitude, focal_length, pixel_size)

altitude * pixel_size / focal_length; 0.0 if focal_length <= 0.

vegetation_index_from_returns(first_returns, last_returns)

(first - last) / first * 100; 0.0 if first_returns <= 0.

canopy_height(dsm, dtm)

dsm - dtm — not clamped; a DTM point above the matched DSM point returns a negative canopy height.

slope_from_elevation(dz, dx)

atan(dz / dx) * 180 / pi; returns 90.0 (vertical) if |dx| < 1e-15 rather than dividing by zero.

intensity_normalization(intensity, range_val, ref_range)

intensity * (range_val / ref_range)^2; 0.0 if ref_range <= 0.

overlap_percentage(swath_width, strip_spacing)

(swath_width - strip_spacing) / swath_width * 100; 0.0 if swath_width <= 0.

point_cloud_file_size_mb(n_points, bytes_per_point)

n_points * bytes_per_point / 1e6.

All twelve values below are pinned exactly (not merely range-checked) in tests/mojo/test_wave87.mojo, so they are load-bearing, reproducible numbers rather than illustrations:

lidar_mapping: terrain-model production and survey QA

Function
What it does

range_from_time_of_flight(time_ns)

2.998e8 * time_ns * 1e-9 / 2 — see the range-from-time comparison above for how this differs from lidar.range_from_tof.

point_density(n_points, area_m2)

n_points / area_m2; 0.0 if area_m2 <= 0 — identical formula to lidar.point_density, just a renamed second parameter.

canopy_height_model(dsm_elevation, dtm_elevation)

dsm_elevation - dtm_elevation, clamped to 0.0 if negative — the opposite edge-case behavior from lidar.canopy_height, which returns the raw negative value.

leaf_area_index_from_lidar(gap_fraction, zenith_angle_rad)

-cos(zenith_angle_rad) * ln(gap_fraction) / 0.5 (Beer-Lambert, G ≈ 0.5 for a spherical leaf-angle distribution); 0.0 if gap_fraction <= 0 or >= 1.

ground_filter_threshold(scan_angle_rad, point_spacing)

point_spacing * tan(scan_angle_rad) — a simplified stand-in for the height threshold a progressive-TIN ground filter would use.

beam_divergence_footprint(range_m, beam_divergence_mrad)

range_m * beam_divergence_mrad * 1e-3 — same small-angle footprint formula as lidar.footprint_diameter, with the arguments swapped in order (range first here, altitude first there) and renamed.

pulse_return_intensity(reflectance, range_m, system_constant)

system_constant * reflectance / range_m^2; 0.0 if range_m <= 0.

scan_line_spacing(altitude, scan_rate, flight_speed)

flight_speed / scan_rate; 0.0 if scan_rate <= 0. altitude is accepted — the docstring says so explicitly ("unused in simplified model") — but never appears in the function body.

vertical_accuracy(z_lidar, z_check)

z_lidar - z_check — despite the function's name, this returns the raw signed residual for one point, not an accuracy statistic; the docstring calls it exactly that ("Point-wise vertical error: residual"). A caller wanting an actual accuracy figure (e.g. RMSE) has to square, average, and root this across a set of check points itself.

surface_roughness_lidar(mean_residual, n_points)

sqrt(mean_residual); 0.0 if mean_residual < 0. n_points is accepted and documented ("RMS of plane fit residuals... approximated from mean_residual") but does not appear anywhere in the arithmetic — the caller is expected to have already divided the sum of squared residuals by n_points before calling this.

None of lidar_mapping's ten functions are pinned by a hand-verified wave suite (see Verification below), so the worked examples here were computed independently in plain Python against the module's own formulas, not copied from a test file:

Function
What it does

radar_range_equation(pt, gt, gr, wavelength, sigma, noise_factor, bandwidth, snr_min)

pt*gt*gr*wavelength^2*sigma / (snr_min*noise_factor*bandwidth). Its own module docstring labels this a "Simplified R⁴ computation" — it is not the classical Rmax = [Pt·Gt·Gr·λ²·σ / ((4π)³·Pmin·SNRmin)]^(1/4): there is no (4π)³ term, Pmin is replaced by the product noise_factor * bandwidth, and the result is never raised to the 1/4 power, so the return value is a dimensionless intermediate factor, not a range in meters.

doppler_frequency(velocity, wavelength)

2 * velocity / wavelength.

doppler_velocity(fd, wavelength)

fd * wavelength / 2.

range_from_time(round_trip_time, c)

c * round_trip_time / 2 — c is required here, unlike lidar.range_from_tof's defaulted c.

pulse_repetition_interval(prf)

1 / prf.

max_unambiguous_range(prf, c)

c / (2 * prf).

max_unambiguous_velocity(prf, wavelength)

prf * wavelength / 4.

range_resolution(bandwidth)

C_LIGHT / (2 * bandwidth), where C_LIGHT = 3.0e8 is a module-level constant, not a parameter — unlike range_from_time and max_unambiguous_range a few lines above it in the same file, which both take c explicitly.

antenna_gain_from_area(aperture_area, wavelength)

4 * pi * aperture_area / wavelength^2.

radar_cross_section_sphere(radius)

pi * radius^2 — the optical-region RCS of a large conducting sphere.

noise_power(bandwidth, noise_figure, temperature)

K_BOLTZMANN * temperature * bandwidth * noise_figure, with K_BOLTZMANN = 1.38e-23 likewise hardcoded rather than passed in.

signal_to_clutter(signal_power, clutter_power)

signal_power / clutter_power.

Every one of these twelve is pinned exactly in tests/mojo/test_wave86.mojo, including a round-trip consistency check that composes two of them:

The test file also checks doppler_velocity(doppler_frequency(150.0, 0.05), 0.05) == 150.0 — a velocity converted to a Doppler shift and back recovers the original value, since the two functions are exact algebraic inverses of each other (fd = 2v/λ and v = fd·λ/2).

Verification

mojo/tests/libs/test_lidar.mojo, test_lidar_mapping.mojo, and test_radar.mojo are all auto-generated coverage smoke tests: each calls every function once with the literal argument 0.5 (or 0.5 repeated for multi-argument functions) and checks only that the Float64 result is finite — none of the three pins a specific numeric output.

Real, hand-worked numeric assertions exist for two of the three modules, in the wave-suite files instead: tests/mojo/test_wave87.mojo ("Wave 87 — Distillation, LiDAR, Archaeology") pins all twelve lidar functions across 26 assertions, and tests/mojo/test_wave86.mojo ("Wave 86 — Paper Science, Radar, Veterinary") pins all twelve radar functions, including the Doppler round-trip check above. lidar_mapping has no equivalent wave-suite coverage anywhere in tests/mojo/ — grepping the whole directory for lidar_mapping turns up only its own auto-generated smoke test — so every worked example for that module on this page was computed independently in plain Python against the formulas read directly out of mojo/engine/lidar_mapping.mojo, not copied from a pinned assertion.

gitbook/engine/nutrition-and-veterinary-science-kernels.md also references tests/mojo/test_wave86.mojo by name, since that file's third module is veterinary. That page documents veterinary's own functions and does not describe any radar function — the two pages draw worked examples from the same wave file without duplicating each other's coverage.

Next: see Nutrition and veterinary science kernels for the other two modules pinned in test_wave86.mojo/test_wave87.mojo, or browse the Runtime library catalog for every module and function.

Last updated

Was this helpful?