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
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
|