You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

85 lines
3.0 KiB
Python

import math
from triangulation import (
PropagationModel,
ReceiverSignal,
SPEED_OF_LIGHT_M_S,
build_result_payload,
rssi_to_distance_m,
solve_from_signal_amplitudes,
solve_three_sphere_intersection,
Sphere,
)
def _distance_to_rssi(distance_m: float, frequency_hz: float, model: PropagationModel) -> float:
fspl_ref_db = 20.0 * math.log10(
4.0 * math.pi * model.reference_distance_m * frequency_hz / SPEED_OF_LIGHT_M_S
)
rx_power_at_ref_dbm = (
model.tx_power_dbm + model.tx_gain_dbi + model.rx_gain_dbi - fspl_ref_db
)
return rx_power_at_ref_dbm - 10.0 * model.path_loss_exponent * math.log10(
distance_m / model.reference_distance_m
)
def test_exact_three_sphere_intersection():
true_point = (2.0, 3.0, 4.0)
spheres = [
Sphere(center=(0.0, 0.0, 0.0), radius=math.dist(true_point, (0.0, 0.0, 0.0))),
Sphere(center=(10.0, 0.0, 0.0), radius=math.dist(true_point, (10.0, 0.0, 0.0))),
Sphere(center=(0.0, 8.0, 0.0), radius=math.dist(true_point, (0.0, 8.0, 0.0))),
]
result = solve_three_sphere_intersection(spheres=spheres, z_preference="positive")
assert result.exact
assert math.isclose(result.point[0], true_point[0], abs_tol=1e-9, rel_tol=0.0)
assert math.isclose(result.point[1], true_point[1], abs_tol=1e-9, rel_tol=0.0)
assert math.isclose(result.point[2], true_point[2], abs_tol=1e-9, rel_tol=0.0)
def test_frequency_affects_distance_conversion():
model = PropagationModel(tx_power_dbm=20.0)
amplitude = -60.0
low_freq_distance = rssi_to_distance_m(amplitude, 433e6, model)
high_freq_distance = rssi_to_distance_m(amplitude, 2.4e9, model)
assert high_freq_distance < low_freq_distance
def test_pipeline_from_signal_to_payload():
model = PropagationModel(tx_power_dbm=18.0, path_loss_exponent=2.0)
true_point = (3.0, 2.0, 1.5)
receiver_centers = [
("r0", (0.0, 0.0, 0.0)),
("r1", (8.0, 0.0, 0.0)),
("r2", (0.0, 7.0, 0.0)),
]
freqs = [915e6, 920e6, 930e6]
signals = []
for (receiver_id, center), freq in zip(receiver_centers, freqs):
distance = math.dist(true_point, center)
amplitude = _distance_to_rssi(distance, freq, model)
signals.append(
ReceiverSignal(
receiver_id=receiver_id,
center=center,
amplitude_dbm=amplitude,
frequency_hz=freq,
)
)
result, spheres = solve_from_signal_amplitudes(
signals=signals, model=model, z_preference="positive"
)
payload = build_result_payload(signals, spheres, result, model)
assert result.exact
assert math.isclose(result.point[0], true_point[0], abs_tol=1e-7, rel_tol=0.0)
assert math.isclose(result.point[1], true_point[1], abs_tol=1e-7, rel_tol=0.0)
assert math.isclose(result.point[2], true_point[2], abs_tol=1e-7, rel_tol=0.0)
assert "position" in payload
assert len(payload["receivers"]) == 3