Skylinemasker och himlageometri
Fram till 0,36 var varje sikttest mot jorden i motorn en enda elevationsgräns, mask_deg: en satellit räknades när den låg vid eller över ett tal, oavsett riktning. En gata, en kaj, en fjord eller en kran blockerar en del av himlen och lämnar resten öppen. En skyline är en elevationsgräns som beror på azimut; himlageometrifunktionerna gör en plats, en tid, konstellationer och en skyline till den synliga mängden, dilution of precision (DOP), geometrikovariansen, den horisontella felellipsen, ett Monte Carlo-moln och ett skyplot. Designen är docs/design/2026-10-12-geometry-and-skyline-design.md (punkterna 1 och 2).
Evidens: beslutet om maskerad sikt (geodetiska siktvinklar, tabellmasken, den synliga mängden och dess DOP) är VALIDATED mot Orekit 12,2 och gnss_lib_py 1.0.4 i en förhandsregistrerad jämförelse, och det är även felellipsen och kofaktorn per system (numpy, förhandsregistrerade); reduktionerna för kanjon, sektor och terräng, förinställningarna och resten av himlageometrifunktionerna är MODELLED (Evidens). En skyline tar bort satelliter med fri sikt och ingenting mer. Förinställningarna är REPRESENTATIVE geometrier, inga uppmätningar.
Skyline#
En skyline är det punktvisa maximumet av fyra beskrivningar. Azimut är grader medurs från sann nord i platsens lokala öst-nord-upp-system; elevationer anges i [0, 90).
| Beskrivning | TOML | Vad det är |
|---|---|---|
| Tabell | table = [[az, el], ...] |
Elevationer interpolerade linjärt i azimut, med wrap vid 360°. Azimut strikt växande i [0, 360); högst 3600 rader. |
| Gatukanjon | street_az_deg, width_m, height_left_m, height_right_m, antenna_m, length_m |
Två raka parallella väggar (nedan). |
| Sektorer | sectors = [{ az_from, az_to, el_deg }] |
Skarpkantade intervall, medurs från az_from (inkluderad) till az_to (exkluderad), som kan passera genom norr; högst 360. |
| Terräng | terrain = [{ az, distance_m, height_m }] |
Punkter reducerade till en tabell: elevation atan((height_m − d²/(2 R_eff)) / d), med krökningsfallet för R_eff = 4/3 × 6371 km (en vald, angiven konstant). height_m är ovanför antennen. |
preset = "<name>" utgår från en namngiven geometri; kanjonfälten åsidosätter sedan förinställningens kanjon fält för fält, och table, sectors och terrain läggs till den.
Mottagarens mask_deg behåller sin betydelse: den är en platt skyline. Det effektiva gränsvärdet vid azimut az är max(mask_deg, skyline(az)), och en satellit är synlig när dess elevation är vid eller över det: samma >= som den platta masken alltid använde. En satellit över mask_deg men under skylinen är maskerad av skylinen.
Gatukanjonen#
En gata löper längs azimut s, w meter mellan väggarna, med antennen a meter upp på mittlinjen. Längs azimut az, med θ = az − s, når strålen en vägg efter d(az) = (w/2) / |sin θ|, så en vägg med höjden h skär vid
cut(az) = atan( (h − a) / d(az) ) (the right wall for sin θ > 0, the side at s + 90°)
Den öppna remsan ligger längs gatan. Med length_m = L (antennen i mitten) lämnar en stråle genom en öppen ände, och väggen skär ingenting, när d(az) · |cos θ| > L/2. En vägg som inte är högre än antennen skär ingenting. antenna_m är som standard 1,5 m (ett biltak eller en handhållen mottagare, ett angivet val); en ny kanjon kräver width_m.
Förinställningar#
Valda och angivna dimensioner (designanteckningens), inte mätningar av någon verklig plats. kshana skyline --preset <name> skriver ut en med dess proveniens och dess skärning för varje grad.
| Förinställning | Geometri | Ursprung |
|---|---|---|
city-street |
Kanjon: gata 0°, bredd 20 m, väggar 30 m och 30 m, antenn 1,5 m. 70,7° tvärs gatan, öppen längs den. | Höjd-till-bredd 1,5, inom bandet 0,5–2 för gatukanjoners proportioner i Oke (1988), Street design and urban canopy layer climate, Energy and Buildings 11, 103–113. |
harbour-approach |
Sektorer: öppet mot havet över 090–270 (180°); kajbyggnader 8° över 315–045 (90°); vågbrytaren och dess fyr 3° över 045–090 och 270–315. | Angivna val (till exempel lagerbyggnader på 40 m cirka 285 m bort). |
fjord |
Terräng: väggar 600 m över antennen på 1500 m avstånd på vardera sidan av en nord-sydlig kanal, en punkt per grad (på 1500/|sin az| m), krökningsfall tillämpat. 21,8° tvärs, under 1° längs axeln. |
Av samma storleksordning som bredden och reliefen hos de djupa västnorska fjordarna (Holtedahl (1967), Notes on the formation of fjords and fjord-valleys, Geografiska Annaler 49A, 193–203). |
container-terminal |
Sektorer: containerstaplar 12° över den landvända halvan 000–180; kranarmar 35° över 245–255 och 25° över 285–295; i övrigt öppet mot vattnet. | Fem high-cube-containrar enligt ISO 668 (14,5 m) cirka 60 m bort från en antenn 2 m upp; en kranarm cirka 50 m över kajen på 70 m och 100 m, av storleksordningen hos en post-Panamax-kran. En kransektor är ogenomskinlig. |
I ett scenario#
En valfri sektion [skyline], över typens egen gränsvinkel:
| Typ | Vad skylinen ändrar | Per sampel |
|---|---|---|
orbit (med user_site) |
Synlighetstidslinjen (GNSS-tillgänglighet) och DOP-sammanfattningen. | — |
integrity (med user_site) |
De satelliter som RAIM och ARAIM (araim_dual) använder. |
epochs[].masked_by_skyline |
gnss-sim |
De satelliter vars pseudoavstånd simuleras och används av RAIM. | gnss_measurements[].masked_by_skyline |
jamming |
De satelliter som mottagaren kan spåra, före jammern. | epochs[].masked_by_skyline |
leo-pvt |
Varje lägesuppsättning av satelliter i sikte över varje systems egen mask, inklusive polarsvepet (samma skyline på varje plats). Envelopen för Doppler-läget och den utformade enskilda passagen är gränser för vad en mottagare måste hantera och behåller varje systems mask. | joint.epochs[].masked_by_skyline |
leo-pass |
Vilka epoker som räknas som över masken, tiden över den, GNSS-jämförelsens satelliter. | satellites[].pass.epochs_masked_by_skyline |
nmea-scenario (tränings-NMEA) |
Satelliterna i GSV och i positionsbestämningen. Azimut är sanna bäringar fixerade mot marken, inte fartygets kurs. | — |
Markplatsen för orbit och integrity. Båda typerna modellerar en kretsande användare med geocentrisk elevation och utan jordfast azimut, så de tar en skyline bara med designanteckningens markplatsläge: user_site = { lat_deg, lon_deg, height_m } i stället för user (exakt en av de två). Varje sampel är ett absolut ögonblick, epoch + t: varje satellit propageras till det (en elementuppsättning, sänd efemerid eller SP3-fil förs fram från sin egen epok, en Walker-satellit räknar t från epoch) och roteras till jordfast vid det, så att platsen ser en enhetlig himmel. epoch är omloppsscenariots egen; saknas en (och alltid för integrity, som inte har någon) är det den nyaste epoken bland de satelliter som har en, eller J2000 enbart för Walker-mönster. Elevation och azimut är geodetiska (WGS-84). Den horisontella och vertikala uppdelningen av integritetstypens ARAIM-skyddsnivåer och Stanfords vertikalfel använder samma geodetiska uppåt (WGS-84-ellipsoidens normal, cirka 0,19° från den geocentriska radialen vid 45° latitud); ögonblicks-RAIM använde den redan. En user_site utan skyline är en vanlig markanvändare; vägen för den kretsande user är oförändrad.
kind = "integrity"
mask_deg = 5.0
# ...
[user_site]
lat_deg = 59.45
lon_deg = 24.75
height_m = 15.0
[skyline]
preset = "harbour-approach"
Bakåtkompatibilitet. Utan en sektion [skyline] är ett scenario byte-identiskt med vad samma motor gav innan skylinekod fanns: tests/skyline_scenarios.rs kontrollerar JSON, diagram och sammanfattning för varje medföljande scenario som kör någon av dessa typer, och NMEA-texten för varje träningsscenario, mot SHA-256-digest av baslinjen "0,36 portable" (digesten från utgåvans 0.35.0 commit, ombaserade när motorn gick över till portabel matematik och bevarade i testet som referens: docs/revisions/STUDIO15-portable-math-rebaseline.md). En tom sektion [skyline] skär ingenting; resultatet är då lika med körningen utan den, förutom det tillagda blocket skyline och nollräknarna masked_by_skyline.
Vad som avvisas, med skälet i meddelandet: en skyline på en kretsande user; user och user_site tillsammans; en okänd förinställning; en kanjon utan width_m; en tabell som inte är strikt växande i [0, 360); en elevation utanför [0, 90); en tom sektor; ett icke-positivt terrängavstånd; indata över gränserna; varje okänd nyckel.
Resultatet får skyline: { source, open_fraction, provenance } när sektionen finns: source är preset:<name>, preset:<name>+overrides eller custom; open_fraction är andelen av den övre halvsfärens rymdvinkel över max(mask_deg, skyline) (medelvärdet av 1 − sin(cut) över azimut: 1 för noll mask, 0,5 för en platt skärning vid 30°); provenance är förinställningens rad eller scenariots egen. Enheter och proveniensklasser finns i blocket units.
Medföljande exempel (kshana example <name>):
| Exempel | Vad det visar (ta bort sektionen [skyline] för att jämföra) |
|---|---|
orbit-urban-canyon-skyline |
GPS + Galileo längst ned i city-street: 17 av 289 sampel behåller en positionsbestämning, median-PDOP 70,5 (1,14 under öppen himmel). |
integrity-harbour-approach |
ARAIM med två konstellationer vid infart till en hamn: 70 satellitepoker dolda, tillgängligheten oförändrad (145 av 145). |
gnss-sim-container-terminal-skyline |
Pseudoavstånd och RAIM på en kaj: 44 satellitepoker dolda; RAIM-tillgänglighet 0,59 (0,85 under öppen himmel). |
jamming-harbour-approach-skyline |
En jammer på 2 W i en hamn: 83 satellitepoker dolda, spårningen oförändrad (jammern hade redan tagit dem). |
leo-pvt-fjord-skyline |
GPS + Galileo + ett LEO-lager i en fjord: median för sammansatt PDOP 1,14 (0,93 under öppen himmel). |
leo-vertical-autonomous-vehicle-skyline |
Kanjonen i leo-vertical-autonomous-vehicle som en platt skärning vid 35° och som city-street vriden längs bilens kurs: 8 mot 4 till 5 GPS- och Galileo-satelliter; sammansatt medelfel 2,1 m i båda; larmet på 1,5 m vid 51 % respektive 59 % av epokerna. |
Himmelsgeometri#
Fem motorfunktioner (src/geometry.rs), desamma på varje yta:
| Funktion | Indata → utdata |
|---|---|
sky_geometry(request) |
per satellit 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 (enhetsvarians), covariance_enu_m2; ellipse_1sigma och ellipse vid begärd konfidens; label, statement, advisory |
sky_monte_carlo(request, seed, n) |
n (högst 5000, en indatagräns) minsta-kvadratfel under oberoende gaussiska avståndsfel på σ_UERE från en ChaCha8-ström med fröet seed: samples_enu_m, den empiriska kovariansen, horisontell och 3D-RMS bredvid σ_UERE × HDOP och σ_UERE × PDOP, andelen innanför ellipsen på 95 % |
skyline_presets() |
förinställningarna med etikett, beskrivning, proveniens, skärningen för varje grad och open_fraction |
skyline_profile(skyline) |
en skylinesektion reducerad till den tabell med 360 poster som motorn använder, med source, provenance, open_fraction |
skyplot_svg(request) |
skyploten som statisk SVG (inget skript, ingen extern referens, varje indatasträng escape-kodad) |
En begäran:
{
"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 är motorns konstellationsförinställningar (från constellation-design: gps-baseline, galileo, beidou-meo, glonass, ...; LEO-förinställningarna xona-pulsar, iridium-stl, ...) eller en fullständig tabell leo-pvt [[system]], propagerad till time_s (keplersk med sekulär J2, de gemensamma lägesbanorna i leo-pvt). Deras relativa faser kommer från olika referensepoker, så himlen är en representativ geometri, inte himlen det datumet på den platsen. Satelliter kan i stället (eller dessutom) anges direkt: "satellites": [{"id": "G05", "system": "GPS", "az_deg": 120, "el_deg": 35}, ...], eller med "ecef_m": [x, y, z] och en site eller user_ecef_m. Standardvärden: varje systems egen mask (5° GNSS, 10° LEO; 5° för direkt angivna satelliter), en mottagarklocka per system ("common" för en gemensam klocka), σ_UERE 1 m (en representativ budgetrad för dubbla frekvenser), konfidens 0,95. Indatagränser: högst 2000 satelliter över horisonten (en systemtabell som begär fler avvisas innan den byggs) och 16 mottagarklockor.
En daterad himmel från elementuppsättningar. "tle" tar text för tvåradiga elementuppsättningar (en namnrad före varje uppsättning är valfri; högst 4 MiB, en indatagräns), propagerad av motorns SGP4 (WGS-72, propagatorn som orbit-real-tle använder) till "epoch" plus time_s, roterad till jordfast med UT1 satt lika med UTC och utan polrörelse, och sedd från site. "epoch" är UTC ISO 8601 (2026-06-07T12:00:00Z) och daterar bara elementuppsättningarna (förinställningarna har inget datum, så en epoch utan tle avvisas); "tle_system" märker dem för klockan per system (standard TLE). Varje resultat anger vilken himmel det är i sky_source och sky_statement:
sky_source |
Himlen |
|---|---|
presets |
motorns konstellationsförinställningar: en representativ geometri |
tle-at-epoch |
elementuppsättningarna propagerade till den angivna epoken: den himmel som dessa elementuppsättningar ger vid den epoken, inte en mätning av den (deras egen noggrannhet är SGP4:s och uppsättningarnas ålder) |
tle-at-element-epoch |
ingen epoch: elementuppsättningarna propagerade till den nyaste uppsättningens egen epok plus time_s, en representativ himmel |
satellites |
satelliter angivna direkt, som de anges |
En blandning förenar dem med +. epoch_utc är det ögonblick elementuppsättningarna propagerades till, och tle_skipped anger vilken del av texten som inte användes, med skälet: felformade eller föräldralösa rader för elementuppsättningar (med radnummer), uppsättningar som parsern avvisade, och uppsättningar som SGP4 inte kunde propagera till det ögonblicket (förfallna, eller bortom sina gränser); högst 100 poster totalt, därefter en sista post som räknar resten. En tom lista systems lägger inte till några förinställningar. Skyploten anger underlaget i sin fot.
En kärna. Kofaktormatrisen är orbit::cofactor_from_los, sammansättningen av designmatrisen och Gauss-Jordan-inversen bakom orbit::dop. orbit::dop_cov exponerar kovariansen och orbit::dop är dess reduktion, så de skalärer som är VALIDATED är oförändrade och fortfarande fastlåsta av tests/dop_reference.rs. Det finns ingen andra DOP-implementation.
Ellipsen. Halvaxlar k σ √λ för egenvärdena λ i öst-nord-blocket, med k = √(−2 ln(1 − p)) (√5.991 = 2.448 vid 95 %, 1 vid 39,35 %), och huvudaxelns azimut ½ atan2(2 Q_EN, Q_NN − Q_EE) i [0, 180) från norr.
| Yta | Ingångspunkter |
|---|---|
| CLI | kshana sky <request.json|toml> eller kshana sky --site <lat,lon[,h]> (--systems <a,b> | --tle <file>) [--epoch] [--tle-system] [--time] [--mask] [--skyline-preset] [--uere] [--clock], med --monte-carlo <n> --seed <s> eller --svg; kshana skyline --preset <name> | --presets | --profile <file> |
| Rust | kshana::geometry::{sky_geometry, sky_monte_carlo, skyline_presets, skyline_profile, skyplot_svg}, kshana::surface::* med samma namn |
| 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, eller skyline_preset); ingen Monte Carlo över MCP (bulkprover som en agent bör sammanfatta, inte bära) |
Varje resultat bär label (MODELLED), statement (geometri under en angiven felmodell: oberoende, lika, gaussiska avståndsfel med medelvärde noll; inte en förutsägelse av en mottagares verkliga fel, som också har bias, flervägsfel och fel utan siktlinje; inte en integritetsgräns) och den rådgivande texten: Rådgivande programvara, inte typgodkänd navigationsutrustning (IEC 61108, IEC 61162): operatören är fortsatt ansvarig för fartygets navigering.
Evidens#
Fem registerrader (docs/VERIFICATION-MATRIX.md), som designanteckningen avgränsar dem:
| Rad | Status | Oberoende referens |
|---|---|---|
| Azimutberoende skylinemask: synlig mängd, azimut och elevation samt DOP under masken | VALIDATED | Orekit 12,2 (lookvinklar via TopocentricFrame, ElevationMask) med gnss_lib_py 1.0.4 (get_dop och DOP-matrisen), förhandsregistrerad i xval/orekit-skyline/PREREGISTRATION.md (commit e4b9d912) innan någondera kördes: 1173 lookvinklar på sex platser och fyra epoker, sex tabeller, 142 synliga uppsättningar; sämsta elevation 1.3e-13°, azimut 2.3e-13°, mask 1.1e-12° mot 1e-9°; identiska synliga uppsättningar, ingen i gränsbandet; DOP 3.3e-13 relativt och kofaktor 6.6e-13 av matrisskalan mot 1e-9. Omfattning: tabellmasker, geodetiska lookvinklar, den maskerade mängden och dess DOP och kofaktor med gemensam klocka. |
| Reduktioner av gatukanjon, sektor och terräng till en tabell | MODELLED | handberäknade värden; jämförelsen ovan läser deras tabeller som indata och kontrollerar dem inte |
| Skylineförinställningar | MODELLED | ingen: valda, angivna dimensioner (kontrollerade endast mot sig själva) |
| Horisontell felellips och kofaktor per system från geometrikovariansen | VALIDATED | numpy 2.5.4 linalg.inv och linalg.eigh (standard P2) på de 142 incheckade synliga uppsättningarna, gemensam klocka och en klocka per system, förhandsregistrerad i xval/numpy-ellipse/PREREGISTRATION.md (commit 0bbeeaee): 270 fall, axlar 5.1e-13 relativt, orientering 5.3e-12°, kofaktor 1.0e-12 av matrisskalan, mot 1e-9 och 1e-6° |
| Funktioner för himmelsgeometri | MODELLED | sammansättningen: förinställningspropagering till en representativ himmel, Monte Carlo-molnet (kontrollerat mot den geometri det samplar), ytorna |
Att en sektion [skyline] saknas kontrolleras byte för byte mot utdata från 0.35.0, vilket visar bakåtkompatibilitet och validerar ingenting. gnss_lib_py tillämpar bara en enda elevationsmask, så den är den oberoende DOP-referensen här, inte den oberoende maskreferensen.
Vad en skyline inte modellerar: diffraktion över en väggkant, reflektioner och signaler utan siktlinje som en verklig mottagare ändå kan spåra (de tas bort, försämras inte), partiell transparens (en gallerkran är ogenomskinlig över sin sektor), terräng bortom de angivna punkterna och jordkrökning inom kanjon- och sektorformerna.