Traducción automática, no revisada por un hablante nativo. El inglés es la referencia.

Confianza del receptor y ámbito marítimoEditar en GitHubFuente: docs/SKYLINE.md

Máscaras de horizonte y geometría del cielo

Hasta la 0,36, toda prueba de visibilidad terrestre del motor era un único umbral de elevación, mask_deg: un satélite contaba cuando estaba a ese número o por encima de él, fuera cual fuese su dirección. Una calle, un muelle, un fiordo o una grúa tapan una parte del cielo y dejan libre el resto. Un horizonte (skyline) es un corte de elevación que depende del acimut; las funciones de geometría del cielo convierten un emplazamiento, un instante, unas constelaciones y un horizonte en el conjunto visible, la dilución de la precisión (DOP), la covarianza de la geometría, la elipse de error horizontal, una nube de Monte Carlo y un gráfico del cielo (skyplot). El diseño es docs/design/2026-10-12-geometry-and-skyline-design.md (puntos 1 y 2).

Evidencia: la decisión de visibilidad con máscara (ángulos de observación geodésicos, la máscara de tabla, el conjunto visible y su DOP) está VALIDATED frente a Orekit 12,2 y gnss_lib_py 1.0.4 en una comparación preregistrada, y también lo están la elipse de error y el cofactor por sistema (numpy, preregistrado); las reducciones de cañón urbano, de sector y de terreno, los perfiles y el resto de las funciones de geometría del cielo son MODELLED (Evidencia). Un horizonte elimina satélites con línea de visión y nada más. Los perfiles son geometrías REPRESENTATIVE, no levantamientos.

El horizonte (skyline)#

Un horizonte es el máximo punto a punto de cuatro descripciones. El acimut son grados en sentido horario desde el norte verdadero en el sistema local este-norte-arriba del emplazamiento; las elevaciones están en [0, 90).

Descripción TOML Qué es
Tabla table = [[az, el], ...] Elevaciones interpoladas linealmente en acimut, con cierre cíclico en 360°. Acimuts estrictamente crecientes en [0, 360); como máximo 3600 filas.
Cañón urbano street_az_deg, width_m, height_left_m, height_right_m, antenna_m, length_m Dos paredes rectas y paralelas (más abajo).
Sectores sectors = [{ az_from, az_to, el_deg }] Intervalos de bordes netos, en sentido horario desde az_from (incluido) hasta az_to (excluido), que pueden pasar por el norte; como máximo 360.
Terreno terrain = [{ az, distance_m, height_m }] Puntos reducidos a una tabla: elevación atan((height_m − d²/(2 R_eff)) / d), con la caída por curvatura para R_eff = 4/3 × 6371 km (una constante elegida y declarada). height_m está por encima de la antena.

preset = "<name>" parte de una geometría con nombre; los campos del cañón sustituyen entonces el cañón del perfil campo a campo, y table, sectors y terrain se suman a él.

El mask_deg del receptor conserva su significado: es un horizonte plano. El corte efectivo en el acimut az es max(mask_deg, skyline(az)), y un satélite es visible cuando su elevación está en ese valor o por encima: el mismo >= que siempre usó la máscara plana. Un satélite por encima de mask_deg pero por debajo del horizonte queda enmascarado por el horizonte.

El cañón urbano#

Una calle discurre según el acimut s, a w metros entre las paredes, con la antena a a metros de altura sobre el eje. En el acimut az, con θ = az − s, el rayo alcanza una pared tras d(az) = (w/2) / |sin θ|, de modo que una pared de altura h corta a

cut(az) = atan( (h − a) / d(az) )          (the right wall for sin θ > 0, the side at s + 90°)

La franja abierta se extiende a lo largo de la calle. Con length_m = L (la antena en el centro), un rayo sale por un extremo abierto, y la pared no corta nada, cuando d(az) · |cos θ| > L/2. Una pared que no sea más alta que la antena no corta nada. antenna_m vale por defecto 1,5 m (el techo de un coche o un receptor de mano, una elección declarada); un cañón nuevo necesita width_m.

Preajustes#

Dimensiones elegidas y declaradas (las de la nota de diseño), no levantamientos de ningún lugar real. kshana skyline --preset <name> imprime una con su procedencia y su corte cada grado.

Preajuste Geometría Procedencia
city-street Cañón: calle 0°, anchura 20 m, paredes de 30 m y 30 m, antena 1,5 m. 70,7° en sentido transversal a la calle, abierto a lo largo de ella. Relación altura/anchura 1,5, dentro de la banda de 0,5–2 de relaciones de aspecto de cañones urbanos en Oke (1988), Street design and urban canopy layer climate, Energy and Buildings 11, 103–113.
harbour-approach Sectores: abierto hacia el mar entre 090–270 (180°); edificios del muelle de 8° entre 315–045 (90°); el rompeolas y su luz de 3° entre 045–090 y 270–315. Elecciones declaradas (por ejemplo, almacenes de 40 m a unos 285 m).
fjord Terreno: paredes de 600 m sobre la antena a 1500 m a cada lado de un canal norte-sur, un punto por grado (a 1500/|sin az| m), con la caída por curvatura aplicada. 21,8° en sentido transversal, por debajo de 1° a lo largo del eje. El orden de magnitud de la anchura y del relieve de los profundos fiordos del oeste de Noruega (Holtedahl (1967), Notes on the formation of fjords and fjord-valleys, Geografiska Annaler 49A, 193–203).
container-terminal Sectores: pilas de contenedores de 12° sobre la mitad de tierra 000–180; plumas de grúa de 35° entre 245–255 y de 25° entre 285–295; abierto al agua en el resto. Cinco contenedores high-cube ISO 668 (14,5 m) a unos 60 m de una antena situada a 2 m de altura; una pluma a unos 50 m sobre el muelle a 70 m y 100 m, del orden de una grúa post-Panamax. Un sector de grúa es opaco.

En un escenario#

Una sección [skyline] opcional, sobre el umbral de corte propio del tipo:

Tipo Qué cambia el horizonte Por muestra
orbit (con user_site) La cronología de visibilidad (disponibilidad de GNSS) y el resumen de DOP. —
integrity (con user_site) Los satélites que usan RAIM y ARAIM (araim_dual). epochs[].masked_by_skyline
gnss-sim Los satélites cuyas pseudodistancias se simulan y usa RAIM. gnss_measurements[].masked_by_skyline
jamming Los satélites que el receptor puede seguir, antes del inhibidor. epochs[].masked_by_skyline
leo-pvt El conjunto en vista de cada modo sobre la máscara propia de cada sistema, incluido el barrido polar (el mismo horizonte en cada emplazamiento). La envolvente del modo Doppler y el paso único diseñado son cotas de lo que un receptor debe manejar y conservan la máscara de cada sistema. joint.epochs[].masked_by_skyline
leo-pass Qué épocas cuentan como por encima de la máscara, el tiempo por encima de ella, los satélites de la comparación con GNSS. satellites[].pass.epochs_masked_by_skyline
nmea-scenario (NMEA de entrenamiento) Los satélites en GSV y en la solución de posición. Los acimuts son rumbos verdaderos fijos respecto al terreno, no el rumbo del buque. —

El emplazamiento terrestre de orbit y integrity. Ambos tipos modelan un usuario en órbita con una elevación geocéntrica y sin acimut fijo a la Tierra, por lo que solo admiten un horizonte con el modo de emplazamiento terrestre de la nota de diseño: user_site = { lat_deg, lon_deg, height_m } en lugar de user (exactamente uno de los dos). Cada muestra es un instante absoluto, epoch + t: cada satélite se propaga hasta él (un conjunto de elementos, unas efemérides de difusión o un archivo SP3 avanza desde su propia época; un satélite Walker cuenta t desde epoch) y se rota a coordenadas fijas a la Tierra en ese instante, de modo que el emplazamiento ve un único cielo coherente. epoch es el del propio escenario de órbita; sin él (y siempre para integrity, que no lo tiene) es la época más reciente entre los satélites que la llevan, o J2000 solo para patrones Walker. La elevación y el acimut son geodésicos (WGS-84). La división horizontal y vertical de los niveles de protección ARAIM del tipo integrity y del error vertical de Stanford usa la misma vertical geodésica (la normal al elipsoide WGS-84, a unos 0,19° de la radial geocéntrica a 45° de latitud); el RAIM instantáneo ya la usaba. Un user_site sin horizonte es un usuario terrestre normal; la ruta del user en órbita no cambia.

kind = "integrity"
mask_deg = 5.0
# ...
[user_site]
lat_deg = 59.45
lon_deg = 24.75
height_m = 15.0

[skyline]
preset = "harbour-approach"

Compatibilidad hacia atrás. Sin una sección [skyline], un escenario es idéntico byte a byte a lo que daba el mismo motor antes de que existiera el código de horizonte: tests/skyline_scenarios.rs comprueba el JSON, el gráfico y el resumen de cada escenario distribuido que ejecuta uno de estos tipos, y el texto NMEA de cada escenario de entrenamiento, frente a los resúmenes SHA-256 de la línea base «0,36 portable» (los resúmenes de la confirmación de la versión 0.35.0, rebasados cuando el motor pasó a matemática portable y conservados en la prueba como registro: docs/revisions/STUDIO15-portable-math-rebaseline.md). Una sección [skyline] vacía no corta nada; el resultado es entonces igual a la ejecución sin ella salvo por el bloque skyline añadido y los recuentos masked_by_skyline a cero.

Qué se rechaza, con el motivo en el mensaje: un horizonte en un user en órbita; user y user_site a la vez; un perfil desconocido; un cañón sin width_m; una tabla que no es estrictamente creciente en [0, 360); una elevación fuera de [0, 90); un sector vacío; una distancia de terreno no positiva; una entrada que supera los límites; cualquier clave desconocida.

El resultado incorpora skyline: { source, open_fraction, provenance } cuando la sección está presente: source es preset:<name>, preset:<name>+overrides o custom; open_fraction es la fracción del ángulo sólido del hemisferio superior por encima de max(mask_deg, skyline) (la media de 1 − sin(cut) en acimut: 1 para una máscara nula, 0,5 para un corte plano de 30°); provenance es la línea del perfil o la del propio escenario. Las unidades y las clases de procedencia están en el bloque units.

Ejemplos incluidos (kshana example <name>):

Ejemplo Qué muestra (retire la sección [skyline] para comparar)
orbit-urban-canyon-skyline GPS + Galileo en el fondo de city-street: 17 de 289 muestras mantienen una solución de posición, PDOP mediano 70,5 (1,14 con cielo abierto).
integrity-harbour-approach ARAIM con doble constelación al entrar en un puerto: 70 épocas de satélite ocultas, disponibilidad sin cambios (145 de 145).
gnss-sim-container-terminal-skyline Pseudodistancias y RAIM en un muelle: 44 épocas de satélite ocultas; disponibilidad de RAIM 0,59 (0,85 con cielo abierto).
jamming-harbour-approach-skyline Un inhibidor de 2 W en un puerto: 83 épocas de satélite ocultas, seguimiento sin cambios (el inhibidor ya los había anulado).
leo-pvt-fjord-skyline GPS + Galileo + una capa LEO en un fiordo: PDOP fusionado mediano 1,14 (0,93 con cielo abierto).
leo-vertical-autonomous-vehicle-skyline El cañón de leo-vertical-autonomous-vehicle como un corte plano de 35° y como city-street girado según el rumbo del coche: 8 frente a 4 a 5 satélites GPS y Galileo; error medio fusionado 2,1 m en ambos; la alarma de 1,5 m en el 51 % y el 59 % de las épocas.

Geometría del cielo#

Cinco funciones del motor (src/geometry.rs), iguales en todas las superficies:

Función Entrada → salida
sky_geometry(request) por satélite id, system, az_deg, el_deg, cut_deg, visible, masked; n_visible, n_masked, open_fraction; dop (GDOP, PDOP, HDOP, VDOP, TDOP, tdop_by_clock); states, cofactor_enu (varianza unitaria), covariance_enu_m2; ellipse_1sigma y ellipse con la confianza solicitada; label, statement, advisory
sky_monte_carlo(request, seed, n) n (como máximo 5000, un límite de entrada) errores de mínimos cuadrados con errores de distancia gaussianos independientes de σ_UERE a partir de un flujo ChaCha8 con semilla seed: samples_enu_m, la covarianza empírica, el RMS horizontal y 3-D junto a σ_UERE × HDOP y σ_UERE × PDOP, la fracción dentro de la elipse del 95 %
skyline_presets() los perfiles con etiqueta, descripción, procedencia, el corte cada grado y open_fraction
skyline_profile(skyline) una sección de horizonte reducida a la tabla de 360 entradas que usa el motor, con source, provenance, open_fraction
skyplot_svg(request) el gráfico del cielo (skyplot) como SVG estático (sin script, sin referencia externa, con todas las cadenas de entrada escapadas)

Una solicitud:

{
  "site": {"lat_deg": 59.91, "lon_deg": 10.75, "height_m": 5},
  "time_s": 3600,
  "systems": ["gps-baseline", "galileo", "beidou-meo", "xona-pulsar"],
  "mask_deg": 5,
  "skyline": {"preset": "fjord"},
  "clock": "per-system",
  "sigma_uere_m": 1.5,
  "confidence": 0.95
}

systems son los perfiles de constelación del motor (gps-baseline, galileo, beidou-meo, glonass, ... de constellation-design; los perfiles LEO xona-pulsar, iridium-stl, ...) o una tabla [[system]] completa de leo-pvt, propagados hasta time_s (kepleriano con J2 secular, las órbitas del modo conjunto leo-pvt). Sus fases relativas proceden de épocas de referencia distintas, por lo que el cielo es una geometría representativa, no el cielo de esa fecha en ese lugar. Los satélites pueden darse en su lugar (o además) directamente: "satellites": [{"id": "G05", "system": "GPS", "az_deg": 120, "el_deg": 35}, ...], o con "ecef_m": [x, y, z] y un site o user_ecef_m. Valores predeterminados: la máscara propia de cada sistema (5° GNSS, 10° LEO; 5° para los satélites dados directamente), un reloj de receptor por sistema ("common" para un reloj compartido), σ_UERE 1 m (una línea de presupuesto representativa de doble frecuencia), confianza 0,95. Límites de entrada: como máximo 2000 satélites por encima del horizonte (una tabla de sistema que pida más se rechaza antes de construirse) y 16 relojes de receptor.

Un cielo fechado a partir de conjuntos de elementos. "tle" toma texto de conjuntos de elementos de dos líneas (una línea de nombre antes de cada conjunto es opcional; como máximo 4 MiB, un límite de entrada), propagados con el SGP4 del motor (WGS-72, el propagador que usa orbit-real-tle) hasta "epoch" más time_s, rotados a coordenadas fijas a la Tierra con UT1 tomado como UTC y sin movimiento polar, y vistos desde site. "epoch" es UTC ISO 8601 (2026-06-07T12:00:00Z) y fecha solo los conjuntos de elementos (los perfiles no llevan fecha, por lo que un epoch sin tle se rechaza); "tle_system" los etiqueta para el reloj de cada sistema (valor predeterminado TLE). Cada resultado indica qué cielo es en sky_source y sky_statement:

sky_source El cielo
presets los perfiles de constelación del motor: una geometría representativa
tle-at-epoch los conjuntos de elementos propagados hasta la época indicada: el cielo que esos conjuntos dan en esa época, no una medición de él (su propia precisión es la de SGP4 y la antigüedad de los conjuntos)
tle-at-element-epoch sin epoch: los conjuntos de elementos propagados hasta la época propia del conjunto más reciente más time_s, un cielo representativo
satellites satélites dados directamente, tal como se indican

Una mezcla los une con +. epoch_utc es el instante hasta el que se propagaron los conjuntos de elementos, y tle_skipped nombra qué parte del texto no se usó, con el motivo: líneas de conjuntos de elementos mal formadas o huérfanas (con sus números de línea), conjuntos que el analizador rechazó y conjuntos que SGP4 no pudo propagar hasta ese instante (decaídos o fuera de sus límites); como máximo 100 entradas en total y después una última entrada que cuenta el resto. Una lista systems vacía no añade ningún perfil. El gráfico del cielo indica la base en su pie.

Un único núcleo. La matriz de cofactores es orbit::cofactor_from_los, el ensamblado de la matriz de diseño y la inversa de Gauss-Jordan que hay tras orbit::dop. orbit::dop_cov expone la covarianza y orbit::dop es su reducción, de modo que los escalares VALIDATED no cambian y siguen fijados por tests/dop_reference.rs. No hay una segunda implementación de DOP.

La elipse. Semiejes k σ √λ para los autovalores λ del bloque este-norte, con k = √(−2 ln(1 − p)) (√5.991 = 2.448 al 95 %, 1 al 39,35 %), y el acimut del eje mayor ½ atan2(2 Q_EN, Q_NN − Q_EE) en [0, 180) desde el norte.

Superficie Puntos de entrada
CLI kshana sky <request.json|toml> o kshana sky --site <lat,lon[,h]> (--systems <a,b> | --tle <file>) [--epoch] [--tle-system] [--time] [--mask] [--skyline-preset] [--uere] [--clock], con --monte-carlo <n> --seed <s> o --svg; kshana skyline --preset <name> | --presets | --profile <file>
Rust kshana::geometry::{sky_geometry, sky_monte_carlo, skyline_presets, skyline_profile, skyplot_svg}, kshana::surface::* con los mismos nombres
Python kshana.sky_geometry, sky_monte_carlo, skyline_presets, skyline_profile, skyplot_svg
WASM sky_geometry(request_json), sky_monte_carlo(request_json, seed, n), skyline_presets(), skyline_profile(skyline_json), skyplot_svg(request_json)
MCP sky_geometry (request, o skyline_preset); sin Monte Carlo por MCP (muestras en bloque que un agente debe resumir, no transportar)

Todo resultado lleva label (MODELLED), el statement (geometría bajo un modelo de error declarado: errores de distancia gaussianos independientes, iguales y de media cero; no es una predicción del error real de un receptor, que además tiene sesgos, multitrayecto y errores sin línea de visión; no es una cota de integridad) y el aviso consultivo: Software de carácter consultivo, no un equipo de navegación homologado (IEC 61108, IEC 61162): el operador sigue siendo responsable de la navegación del buque.

Evidencias#

Cinco filas del registro (docs/VERIFICATION-MATRIX.md), tal como las delimita la nota de diseño:

Fila Estado Referencia independiente
Máscara de horizonte dependiente del acimut: conjunto visible, acimut y elevación, y DOP bajo la máscara VALIDATED Orekit 12,2 (ángulos de observación de TopocentricFrame, ElevationMask) con gnss_lib_py 1.0.4 (get_dop y la matriz de DOP), preregistrado en xval/orekit-skyline/PREREGISTRATION.md (commit e4b9d912) antes de ejecutar ninguno de los dos: 1173 ángulos de observación en seis emplazamientos y cuatro épocas, seis tablas, 142 conjuntos visibles; peor elevación 1.3e-13°, acimut 2.3e-13°, máscara 1.1e-12° frente a 1e-9°; conjuntos visibles idénticos, ninguno en la banda del límite; DOP 3.3e-13 relativo y cofactor 6.6e-13 de la escala de la matriz frente a 1e-9. Alcance: máscaras de tabla, ángulos de observación geodésicos, el conjunto enmascarado y su DOP y cofactor con reloj común.
Reducciones de cañón urbano, sectores y terreno a una tabla MODELLED valores calculados a mano; la comparación anterior lee sus tablas como entradas y no las comprueba
Perfiles de horizonte MODELLED ninguna: dimensiones elegidas y declaradas (comprobadas solo consigo mismas)
Elipse de error horizontal y cofactor por sistema a partir de la covarianza de la geometría VALIDATED numpy 2.5.4 linalg.inv y linalg.eigh (P2 estándar) sobre los 142 conjuntos visibles confirmados, reloj común y un reloj por sistema, preregistrado en xval/numpy-ellipse/PREREGISTRATION.md (commit 0bbeeaee): 270 casos, ejes 5.1e-13 relativo, orientación 5.3e-12°, cofactor 1.0e-12 de la escala de la matriz, frente a 1e-9 y 1e-6°
Funciones de geometría del cielo MODELLED la composición: propagación de perfiles hasta un cielo representativo, la nube de Monte Carlo (comprobada frente a la geometría que muestrea) y las superficies

La ausencia de una sección [skyline] se comprueba byte a byte frente a las salidas de la 0.35.0, lo que demuestra la compatibilidad hacia atrás y no valida nada. gnss_lib_py aplica solo una máscara de elevación única, por lo que aquí es la referencia independiente del DOP, no la de la máscara.

Lo que un horizonte no modela: la difracción en el borde de una pared, las reflexiones y las señales sin línea de visión que un receptor real aún puede seguir (se descartan, no se degradan), la transparencia parcial (una grúa de celosía es opaca en todo su sector), el terreno más allá de los puntos dados y la curvatura terrestre dentro de las formas de cañón y de sector.