Fork de SPLAT! (John A. Magliacane, KD2BD, 2002-2014), bajo GPLv2 heredada del original. El núcleo de cálculo de propagación (ITWOM v3.0, Sid Shumate) se mantiene sin modificar en cpp/itwom3.0.cpp, llamado vía FFI. El resto del pipeline (lectura de formatos SPLAT!, reportes, mapas, KML, gnuplot) se reescribió en Rust, con paralelización nativa vía rayon reemplazando el paralelismo por múltiples procesos del original. Validado contra el corpus golden de SPLAT! con drift < 0.02 dB en cálculos ITWOM.
19 KiB
splat-rs — Manual del Usuario
splat-rs es un port en Rust de SPLAT! (Signal Propagation, Loss, And Terrain analysis tool) con paralelización nativa, salidas en español y una nueva funcionalidad de tabla de intensidad de campo.
Compatible con los formatos .qth, .lrp, .az, .el, .udt, .cities y
.sdf[.bz2] del SPLAT! 1.4.x original. Validado contra el corpus golden de
SPLAT! con drift inferior a 0.02 dB en cálculos ITWOM.
1. Inicio rápido
# Análisis de enlace TX → RX
splat-rs -t txenlace.qth -r rxenlace.qth -d sdf/
# Cobertura completa de un sitio
splat-rs -t tx_site.qth -d sdf/ -L 10 -R 80
# Tabla de intensidad de campo (alternativa al script perfiles.sh)
splat-rs -t tx_site.qth -d sdf/ --sft atenuaciones.txt --sft-max-km 150
Salidas en --out-dir (default: directorio actual):
| Archivo | Cuándo se genera |
|---|---|
<TX>-site_report.txt |
si no hay -r |
<TX>-to-<RX>.txt |
si hay -r (path report) |
coverage.ppm/.png/.kml |
si no está --no-map |
<archivo>.txt + .csv |
si está --sft <archivo> |
splat.gp + *.gp |
si está -h/-H/-p/-e/-l <archivo> |
2. Construcción
cd splat-rs
cargo build --release
# Binario: ./target/release/splat-rs
Requiere:
- Rust ≥ 1.80 (probado con 1.92)
libbz2.soaccesible (sistema o miniconda)gnuplot(opcional, para renderizar los.gp)convertde ImageMagick (opcional, para PPM → PNG transparente)
Para ejecutar con libbz2 desde miniconda:
LD_LIBRARY_PATH=$HOME/miniconda3/lib ./target/release/splat-rs ...
3. Formatos de entrada
.qth — Sitio (TX o RX)
Cuatro líneas:
Nombre del sitio
-1 -01 -23.54 ; latitud (DMS o decimal)
79 27 31.21 ; longitud (DMS o decimal, positiva = oeste)
14 m ; altura antena AGL (metros con 'm', si no = pies)
.lrp — Parámetros Longley-Rice
Una constante numérica por línea, comentarios con ;:
15.000 ; Constante dieléctrica de la Tierra
0.005 ; Conductividad (S/m)
301.00 ; Refracción atmosférica (N-units)
426 ; Frecuencia (MHz)
5 ; Clima radioeléctrico (1-7)
1 ; Polarización (0=H, 1=V)
0.50 ; Fracción de situaciones
0.50 ; Fracción de tiempo
2000 ; ERP en watts (línea 9, opcional)
Climas: 1=Ecuatorial, 2=Subtropical Continental, 3=Subtropical Marítimo,
4=Desértico, 5=Templado Continental, 6=Templado Marítimo (tierra),
7=Templado Marítimo (mar).
.az — Patrón azimutal de antena (autodetectado al lado del .qth del TX)
345.0 ; rotación en grados (clockwise desde el norte verdadero)
0 0.49 ; az amplitud (0..1)
1 0.50
2 0.51
...
.el — Patrón vertical de antena
8.0 345.0 ; tilt mecánico, azimuth del tilt
-10 0.04
-9.5 0.06
...
Usa --no-pattern para ignorar archivos .az/.el aun si existen.
.udt — Terreno definido por usuario
CSV de obstáculos puntuales:
; Edificios y antenas en Quevedo
-1.0250, 79.4587, 50 m
-1.0260, 79.4590, 80 m
Cada feature se "pinta" en un radio de ~5 píxeles (~465 m a 1200 ppd) sobre el DEM. El máximo entre la base SDF y el UDT prevalece.
.cities o -s <file> — Sitios de interés
CSV name, lat, lon:
Quito, -0.18, 78.47
Guayaquil, -2 10 0, 79 53 0
.sdf[.bz2] — Datos de elevación digital
Archivos SPLAT! estándar. Nombres minlat:maxlat:minlon:maxlon.sdf.bz2 cubriendo
1° × 1° cada uno. Carpeta vía -d sdf/. Soporta tanto 3 arc-sec (1200 ppd) como
1 arc-sec (3600 ppd, modo HD).
4. Referencia de banderas
Entrada
| Bandera | Descripción |
|---|---|
-t, --tx FILE |
TX .qth (obligatorio) |
-r, --rx FILE |
RX .qth (opcional; si está → path report; si no → site report) |
-d, --sdf DIR |
Directorio con tiles .sdf[.bz2] (obligatorio) |
--lrp FILE |
.lrp (default: splat.lrp junto al TX) |
--erp WATTS |
Override del ERP del .lrp |
-s, --cities FILE |
Archivo de sitios; repetible |
--udt FILE |
Terreno definido por usuario; repetible |
Modelo y geometría
| Bandera | Descripción | Default |
|---|---|---|
--olditm |
Usa Longley-Rice clásico en vez de ITWOM v3.0 | off (ITWOM) |
-L, --rx-altitude-m M |
Altura RX AGL para sweep de cobertura | 10 m |
-R, --range-km KM |
Radio de cobertura | 40 km |
--metric |
Unidades métricas en reportes | true |
--gc M |
Altura de clutter (obstáculos urbanos) | 0 |
--fz F |
Clearance Fresnel (F ∈ [0,1]) |
0.6 (60%) |
--no-pattern |
Ignora .az/.el autodetectados |
off |
Salidas — gráficas (cada una toma un nombre de archivo)
| Bandera | Genera | Equivale a SPLAT |
|---|---|---|
-h FILE |
Gráfico de alturas (perfil + LOS + curvatura + Fresnel) | -h |
-H FILE |
Versión normalizada del anterior | -H |
-p FILE |
Perfil del terreno (elevación vs distancia) | -p |
-e FILE |
Perfil de ángulo de elevación | -e |
-l FILE |
Gráfico de pérdida del trayecto | -l |
--plot-format FMT |
Tipo de salida gnuplot (png, svg, ps...) | — |
Convención
-h/-H: el plot de alturas usa el marco de tierra aplanada (4/3): el perfil del terreno se eleva pord·(D-d)/(2·R')para que la LOS sea una recta y la zona de Fresnel se dibuje como su geometría natural debajo. Si el terreno aparente toca la Fresnel en el gráfico, es una obstrucción real — convención estándar en ingeniería de RF (ATDI, Pathloss, etc.). Además se superpone una referencia de curvatura (parábola en el mismo eje Y, anclada al fondo del plot) cuyo peak coincide con el terreno más bajo y endpoints sit enmin(terreno)−max_drop. La magnitud del hump es D²/(8·R'): ~150 m para D=100 km, ~180 m para D=110 km. Si quieres ver el perfil geográfico crudo (sin corrección de curvatura), usa-p.
Auto-render: si
gnuplotestá en el PATH, splat-rs lo invoca automáticamente tras escribir los.gpy produce directamente el PNG/SVG/PS. Cada flag escribe a su propio subdirectorio (h-<stem>/,p-<stem>/, etc.) para que no colisionen al pedir varios plots a la vez:splat-rs -t tx.qth -r rx.qth -d sdf/ --no-map \ -h alturas.png -p terreno.png -e elevacion.png # → out_dir/h-alturas/alturas.png # → out_dir/p-terreno/terreno.png # → out_dir/e-elevacion/elevacion.pngSi no hay
gnuplot, splat-rs deja los.gppara que el usuario los renderice manualmente (cd subdir && gnuplot splat.gp).
Salidas — mapa de cobertura (sweep 360°)
| Bandera | Descripción |
|---|---|
-c, --los-coverage |
Modo LOS (sin ITWOM, ~75× más rápido) |
--no-map |
No generar mapa (solo reporte) |
--no-kml |
No generar KML |
--no-link-line |
Omitir línea TX→RX en KML |
--max-loss-db DB |
Umbral de pérdida (mayor → blanco transparente). FM: 110-120, UHF: 125-135, microondas: 160+ |
--min-loss-db DB |
Pérdida mínima (mapea al color más fuerte) |
Leyenda automática: cada vez que se genera un mapa de cobertura, splat-rs también escribe
legend.svg(con texto) ylegend.ppm(barra de colores sola) que muestran el mapeo color↔pérdida↔intensidad de campo del run.
Tabla de intensidad de campo (-sft)
| Bandera | Descripción |
|---|---|
--sft FILE |
Genera tabla en <FILE> (texto) y <FILE>.csv |
--sft-max-km KM |
Extiende a KM (default 90; pasos de 10 km después de 90) |
--sft-azimuths LISTA |
Azimuts custom CSV (default: 0,30,60,...,330) |
--sft-distances LISTA |
Distancias custom CSV (override total) |
Sistema
| Bandera | Descripción | Default |
|---|---|---|
--threads N |
Hilos para rayon (0 = todos los cores) | 0 |
--out-dir DIR |
Directorio de salida | . |
--help |
Ayuda completa | — |
-V, --version |
Versión | — |
5. Flujos de trabajo comunes
5.1 Análisis rápido de sitio (qué tan elevada está la antena vs el terreno)
splat-rs -t mi_sitio.qth -d sdf/ --no-map
Genera mi_sitio-site_report.txt con: ubicación, elevación del terreno,
altura de la antena, HAAT (altura sobre terreno promedio según FCC
Part 73.313(d)) y promedios de terreno en 8 azimuts cardinales.
5.2 Estudio de enlace punto-a-punto
splat-rs -t tx.qth -r rx.qth -d sdf/ --no-map -h alturas.png
Genera:
- Path report en español con pérdida ITWOM, modo de propagación, análisis de obstrucciones y zonas de Fresnel
alturas.png(víagnuplot splat.gp) con perfil + LOS + curvatura 4/3 + Fresnel del 60% y 100%
5.3 Mapa de cobertura ITWOM completo (lento pero preciso)
splat-rs -t tx.qth -d sdf/ -L 10 -R 80 \
--max-loss-db 120 \
-s ciudades.cities --out-dir cobertura/
Genera en cobertura/:
coverage.ppm(raster PPM puro)coverage.png(con blanco → transparente)coverage.kml(Google Earth con TX, ciudades y línea de enlace)<TX>-site_report.txt
Tiempo típico: ~2-4 min en 16 cores para 80 km de radio.
5.4 Mapa LOS rápido (bosquejo previo)
splat-rs -t tx.qth -d sdf/ -L 10 -R 80 -c
Mismo formato de salidas pero ~75× más rápido (sin llamadas a ITWOM). Útil para identificar primero qué zonas tienen línea de vista antes de hacer el sweep ITWOM completo. Tiempo típico: 1-3 s.
5.5 Tabla de intensidad de campo (alternativa a perfiles.sh)
Default: 12 azimuts × 13 distancias (5–40 km en pasos de 5, luego 50/60/70/80/90):
splat-rs -t tx.qth -d sdf/ --olditm --no-map --sft atenuaciones.txt
Extendida hasta 150 km:
splat-rs -t tx.qth -d sdf/ --olditm --no-map \
--sft atenuaciones.txt --sft-max-km 150
Grid completamente personalizado (8 cardinales × distancias específicas):
splat-rs -t tx.qth -d sdf/ --olditm --no-map \
--sft puntos.txt \
--sft-azimuths 0,45,90,135,180,225,270,315 \
--sft-distances 1,2,5,10,20,50,100,150,200
Salida en atenuaciones.txt:
# Tabla de intensidad de campo (dBµV/m) — splat-rs
# TX: Cerro Cochabamba @ (-1.6986, 79.1072), 91.5 MHz, ERP 2000 W, modelo LongleyRice
Dist/Az 0° 30° 60° 90° 120° ...
5 km 95.08 69.96 43.30 35.89 35.82 ...
10 km 24.62 47.60 30.73 33.41 13.03 ...
...
Y CSV atenuaciones.csv para Excel/LibreOffice. Si necesitas decimal coma
para es_EC:
sed 's/\./,/g' atenuaciones.csv > atenuaciones-es.csv
Tiempo típico: <10 ms por tabla (compute) más 6-7 s de carga SDF (una vez).
5.6 Estudio con patrón de antena directiva
Si junto al .qth del TX están <base>.az y <base>.el, splat-rs los detecta
automáticamente y aplica el patrón en la pérdida y la tabla SFT:
ls
# tx_site.qth tx_site.az tx_site.el splat.lrp
splat-rs -t tx_site.qth -d sdf/ --sft tabla.txt
# stderr: "Patrón de antena cargado (rotación 345.0°, tilt 8.0°)"
El reporte incluye Patrón de antena de TX hacia RX: 0.562 (-5.00 dB) cuando
hay -r.
5.7 Override de ERP para análisis A/B
splat-rs -t tx.qth -r rx.qth -d sdf/ --erp 2000 --no-map
splat-rs -t tx.qth -r rx.qth -d sdf/ --erp 5000 --no-map # comparar
5.8 UDT — terreno con edificios sintéticos
cat > obstaculos.udt <<EOF
; Torre de transmisión vecina
-1.0245, 79.4593, 50 m
EOF
splat-rs -t tx.qth -r rx.qth -d sdf/ --udt obstaculos.udt --no-map
El reporte detectará obstrucciones en cada UDT y recomendará la altura mínima de antena RX para librar terreno y zonas de Fresnel.
6. Salidas explicadas
Path report (<TX>-to-<RX>.txt)
Bloques en orden:
- Sitio transmisor: nombre, ubicación DMS, elevación, altura antena, HAAT, distancia, azimut, ángulo elevación al RX
- Sitio receptor: idem desde el RX
- Parámetros del modelo: dieléctrica, conductividad, refracción, frecuencia,
clima, polarización, fracciones, ERP/EIRP (si está en
.lrp) - Resumen del enlace: pérdida en espacio libre, pérdida ITWOM (o LR), atenuación por terreno, modo de propagación (LDV / horizonte simple / horizonte doble + difracción/troposcatter)
- Análisis de obstrucciones: lista de cada punto bloqueante y recomendaciones de altura para librar LOS / 60% Fresnel / Fresnel completa
Site report (<TX>-site_report.txt)
Cuando solo se da -t. Estructura:
- Sitio (DMS), elevación del terreno, altura de la antena
- HAAT (altura sobre promedio del terreno)
- Promedio del terreno en 8 azimuts (0°, 45°, 90°, ..., 315°) entre 2 y 10 millas
Coverage map
coverage.ppm: raster PPM/P6, dimensiones automáticas según los radialescoverage.png: misma imagen conconvert -transparent whitesi está ImageMagick disponiblecoverage.kml: Google Earth conGroundOverlayapuntando al PNG, línea amarilla TX→RX (si hay -r), Placemarks para TX/RX/ciudadeslegend.svg: leyenda con barra de colores y dos ejes: pérdida en dB a la izquierda, intensidad de campo (dBµV/m) a la derecha. Calculada con la frecuencia y ERP del.lrp. Renderiza en cualquier navegador o se convierte conconvert legend.svg legend.png.legend.ppm: solo la barra de color (sin texto), 30×280 px. Útil para componer con el mapa viaconvert +append coverage.png legend.ppm composite.pngo similar.
Tabla SFT
<archivo>: tabla de texto formateada con encabezado descriptivo<archivo>.csv: para importar en hojas de cálculo
Gráficos gnuplot
Cada vez que se pide -h/-H/-p/-e/-l, se genera un set en el subdirectorio:
splat.gp: orquestador (set title/xlabel/ylabel + plot)profile.gp: terrenoreference.gp: línea de vistacurvature.gp: curvatura 4/3 Tierrafresnel.gp,fresnel_pt_6.gp: zonas de Fresnel (cuando aplica)
Para renderizar: cd subdir && gnuplot splat.gp produce el archivo de imagen.
7. Rendimiento
Hardware típico (16 cores)
| Operación | Tiempo |
|---|---|
| Carga 54 tiles SDF (Ecuador completo) | 6-7 s |
| Path report TX↔RX (1 enlace) | <1 s |
| Site report (HAAT + 8 radiales) | <1 s |
| Tabla SFT 12×13 | 1-2 ms |
| Tabla SFT 12×19 (hasta 150 km) | 2-3 ms |
| Mapa LOS coverage 80 km | 1-3 s |
| Mapa ITWOM coverage 40 km | 1-2 min |
| Mapa ITWOM coverage 80 km | 3-5 min |
Speedup típico paralelo / secuencial: 10-12× (eficiencia 65-75% de los 16 cores).
Comparación con SPLAT! original (single-thread)
| SPLAT! v1.4.2 | splat-rs | |
|---|---|---|
| Mapa ITWOM 40 km | ~25 min | ~2 min (12.5× más rápido) |
Tabla SFT (perfiles.sh) |
~30 min | ~7 s (260× más rápido) |
| Path report único | ~2 s | <1 s |
8. Diagnóstico de problemas
"El TX (X, Y) no está cubierto por los tiles SDF de ..."
El TX cae fuera de los tiles cargados. Verifica los nombres de archivo en
sdf/: deben tener formato minlat:maxlat:minlon:maxlon.sdf.bz2 y cubrir
el rango de coordenadas del TX/RX.
Mapa de cobertura completamente blanco
--max-loss-db demasiado bajo para el escenario:
- Microondas en LOS limpio: pérdida 100-130 dB →
--max-loss-db 130o más - Microondas con obstrucciones: hasta 180 dB →
--max-loss-db 180
Patrón de antena no aplicado
splat-rs busca <base>.az y <base>.el donde <base> es el nombre del .qth
sin extensión. Si tu antena se llama tx.qth, debe haber tx.az / tx.el en
el mismo directorio. Verifica con --no-pattern para forzar isotrópica y
comparar.
Aliasing radial visible en mapa de cobertura
Es inherente al método radial-sweep. Mitigaciones:
- Subir umbral con
--max-loss-dbpara mostrar solo cobertura útil - Usar HD mode (DEM 1 arc-sec, ppd=3600) — 9× más lento pero menos artifacts
- El renderer ya aplica supercover 2×2 + dilatación; resta es físicamente irreducible
gnuplot: not found al pedir -h/-H/-p/-e/-l
splat-rs solo escribe los .gp data y orquestador. Para rasterizar necesitas
gnuplot instalado:
sudo apt install gnuplot
Ejecución más lenta de lo esperado
Verifica que rayon esté usando todos los cores:
splat-rs ... 2>&1 | head -1
# "Hilos disponibles: 16 (de 16 lógicos)"
Si aparece menos, usa --threads N para forzar.
9. Diferencias con SPLAT! original
Mejoras
- Reentrancia thread-safe:
static → thread_localen ITWOM permite N hilos sin contención - Paralelización nativa: rayon distribuye radiales/SFT-celdas entre todos los cores
- Carga DEM única: vs SPLAT que recarga en cada invocación
- Salida en español (homologada por Charles)
- Sintaxis CLI compatible + extensiones (
--sft,--threads,--max-loss-db) - CSV de la tabla SFT además del texto
- Mensajes de error útiles con contexto
No implementado
-ano/-ani: input/output alfanumérico (uso poco común)-b: archivos de fronteras cartográficas (overlay PPM)-dbm: paleta dBm en vez de dBµV/m (existe en código pero no expuesto en CLI)-geo: archivo georreferencia Xastir (formato muy específico)-log: registro del comando (uso>&2redirect en shell)-ngs: topografía gris como blanco (escala de grises)-sc: contornos suaves (paleta lineal vs cuantizada)-db: contornos por encima de un umbral-nf: omitir Fresnel en height plot (siempre se grafica)
Drift numérico
- ITWOM path loss: ±0.02 dB vs SPLAT golden (floating-point con
-ffast-mathactivado en C++ original) - HAAT: ±0.1 m por sampling de los 8 radiales
- Ángulos: bit-idénticos (mismo radio terrestre 20902230.97 ft)
10. Estructura del proyecto
splat-rs/
├── Cargo.toml
├── MANUAL.md # este archivo
├── build.rs # compila itwom + linkea libbz2
├── cpp/
│ ├── itwom3.0.cpp # vendored, static→thread_local
│ └── itwom_ffi.cpp # C ABI wrapper (refs → pointers)
└── src/
├── lib.rs # mod tree
├── itwom.rs # FFI seguro a ITWOM/Longley-Rice
├── sdf.rs # loader .sdf[.bz2] + DemTileSet indexado
├── qth.rs # parser .qth (DMS o decimal)
├── lrp.rs # parser .lrp (tolerante a prosa)
├── pat.rs # parser .az/.el + AntennaPattern
├── udt.rs # parser .udt + DemWithUdt overlay
├── cities.rs # parser .cities
├── geo.rs # Distance, Azimuth, step_great_circle
├── radial.rs # compute_radial + plot_lr_map_parallel
├── coverage.rs # CoverageMap + rasterize_line + fill_holes
├── ppm.rs # writer PPM con paleta signal-strength
├── kml.rs # writer KML GroundOverlay + LineString
├── gnuplot.rs # writers .gp (perfil/altura/elevación)
├── report.rs # PathReport + SiteReport + ObstructionAnalysis
├── sft.rs # Strength Field Table (paralelo)
├── types.rs # Site, Config
└── bin/
├── splat-rs.rs # CLI principal
└── bench.rs # microbenchmark ser vs par
26 tests unitarios + 4 ignored (end-to-end con DEM real).
11. Créditos
- SPLAT! original: John A. Magliacane, KD2BD (2002–2014)
- ITWOM v3.0: Sid Shumate (2010)
- Adaptación al español + variante 1.4.2-Charles: Ing. Charles Escobar
- Port a Rust + paralelización: 2026
Bajo licencia GPLv2 (heredada del SPLAT! original).