Машинный перевод, не проверенный носителем языка. Эталоном служит английская версия.

Доверие к приёмнику и морская отрасльПравить на GitHubИсточник: docs/RECEIVER-TRUST.md

Доверие к приёмнику: оценка журнала реального приёмника ГНСС

kshana receiver-trust читает журнал реального приёмника глобальной навигационной спутниковой системы (ГНСС), прогоняет по нему мониторы доверия эпоха за эпохой и сообщает, когда и почему решение приёмника перестало быть заслуживающим доверия. Он может также оценить журнал по событиям и предсказаниям, которые вы задаёте до прогона: было ли каждое событие обнаружено вовремя и согласуется ли смоделированное падение отношения мощности несущей к спектральной плотности шума (C/N0) с тем, что измерил приёмник.

Наблюдаемые величины — собственные величины приёмника; мониторы и их пороги — это пороги движка и ваши. Каждый результат несёт эту пометку, SHA-256 журнала и все пороги, с которыми он выполнялся, так что прогон можно повторить и проверить.

Для судна на ходу, с оценкой в реальном времени и необязательным шлюзом перед картографической системой, см. MARITIME-TRUST.md.

Собственный флаг спуфинга приёмника в сопоставлении с тревогами Kshana, по реальному прогону движка v0.40.0 на вымышленном журнале UBX (200 эпох с частотой 1 Гц). C/N0 падает на 20 дБ на 80–90 с и 120–130 с; приёмник поднимает флаг на 120–130 с и 160–165 с. Из 140 эпох, сравнённых после калибровки, в 11 флаг подняли оба, в 6 — только приёмник, в 11 — только Kshana и в 112 — ни один. Ни одна из сторон не является эталоном истины, а согласие не является проверкой. Носит консультативный характер: результат анализа исследовательского и инженерного инструмента; это не навигационное оборудование с утверждённым типом и не служба обеспечения безопасности жизни.Собственный флаг спуфинга приёмника в сопоставлении с тревогами Kshana, по реальному прогону движка v0.40.0 на вымышленном журнале UBX (200 эпох с частотой 1 Гц). C/N0 падает на 20 дБ на 80–90 с и 120–130 с; приёмник поднимает флаг на 120–130 с и 160–165 с. Из 140 эпох, сравнённых после калибровки, в 11 флаг подняли оба, в 6 — только приёмник, в 11 — только Kshana и в 112 — ни один. Ни одна из сторон не является эталоном истины, а согласие не является проверкой. Носит консультативный характер: результат анализа исследовательского и инженерного инструмента; это не навигационное оборудование с утверждённым типом и не служба обеспечения безопасности жизни.

Запустите#

kshana receiver-trust examples/receiver-trust/session.toml

Это записывает session.result.json, session.trust.csv (одна строка на эпоху) и session.trust.svg (среднее C/N0 во времени с состоянием доверия под ним) рядом со сценарием и выводит однострочный вердикт. Пример журнала синтетический, сделан для демонстрации формата; направьте path на журнал вашего собственного приёмника.

Оценка доверия за один синтетический переход по реальному прогону движка v0.40.0 на examples/maritime-trust (вымышленный журнал NMEA парома на маршруте Таллин — Хельсинки, 3001 эпоха с частотой 1 Гц). Три дорожки имеют общую ось времени. Сверху: расстояние между положением, которое сообщает приёмник, и настоящим положением судна; оно остаётся в пределах нескольких метров примерно до 1576 с, когда запланированный в журнале увод положения смещает его, тогда как приёмник продолжает сообщать действительное определение места. В середине: оценка доверия от 0 до 100, не вычисляемая в первые 300 с калибровки, в полосе nominal (не менее 90) до увода, затем падающая через полосу degraded (не менее 55) в полосу untrusted. Снизу: полоса каждой эпохи в виде цветной ленты с её названием. Оценка носит консультативный характер. Программное обеспечение консультативного характера, не навигационное оборудование с утверждённым типом (IEC 61108, IEC 61162): ответственность за судовождение остаётся на операторе. Журнал придуман, чтобы показать формат и мониторы; это не измерение, и он ничего не говорит о том, как работал бы какой-либо приёмник.Оценка доверия за один синтетический переход по реальному прогону движка v0.40.0 на examples/maritime-trust (вымышленный журнал NMEA парома на маршруте Таллин — Хельсинки, 3001 эпоха с частотой 1 Гц). Три дорожки имеют общую ось времени. Сверху: расстояние между положением, которое сообщает приёмник, и настоящим положением судна; оно остаётся в пределах нескольких метров примерно до 1576 с, когда запланированный в журнале увод положения смещает его, тогда как приёмник продолжает сообщать действительное определение места. В середине: оценка доверия от 0 до 100, не вычисляемая в первые 300 с калибровки, в полосе nominal (не менее 90) до увода, затем падающая через полосу degraded (не менее 55) в полосу untrusted. Снизу: полоса каждой эпохи в виде цветной ленты с её названием. Оценка носит консультативный характер. Программное обеспечение консультативного характера, не навигационное оборудование с утверждённым типом (IEC 61108, IEC 61162): ответственность за судовождение остаётся на операторе. Журнал придуман, чтобы показать формат и мониторы; это не измерение, и он ничего не говорит о том, как работал бы какой-либо приёмник.

Рисунок выше — демонстрация судна из MARITIME-TRUST.md: выдуманный журнал, консультативная оценка.

Тот же сценарий запускается из Python (kshana.receiver_trust(toml)), из пакета WebAssembly (receiver_trust(toml), с журналом внутри) и из инструмента assess_receiver_log сервера Model Context Protocol (MCP).

Журналы, которые он читает#

format Источник Что читается
ubx Двоичный формат u-blox NAV-SAT: C/N0, угол места и азимут по каждому спутнику, NAV-PVT: положение, MON-RF: автоматическая регулировка усиления (АРУ) и индикатор подавления jamInd, NAV-TIMEGPS: неделя, а также собственные флаги приёмника: NAV-STATUS spoofDetState и SEC-SIG (self_report, см. RECEIVER-PROTOCOLS.md)
rinex Файл наблюдений RINEX 3 коды S (C/N0) по каждому спутнику и сигналу; при наличии бортового навигационного файла (nav) — псевдодальности для собственного решения движка
android CSV Android GnssLogger строки Raw: C/N0 и AgcDb по каждому спутнику; строки Fix: положение
nmea Текст NMEA 0183 GSV: отношение сигнал/шум по каждому спутнику (дБ-Гц, с идентификатором сигнала 4,10 или без него) и его угол места и азимут, GSA: геометрический фактор DOP и используемые спутники, GGA и RMC: время и положение
sbf Двоичный формат Septentrio (0,38, только чтение) PVTGeodetic: положение, MeasEpoch: C/N0 по каждому спутнику и сигналу, RFStatus: число диапазонов; см. RECEIVER-PROTOCOLS.md
rtklib-pos Файл решения RTKLIB (.pos) время, положение (широта/долгота или ECEF, с преобразованием) и используемые спутники по каждой эпохе; файлы в градусах-минутах-секундах отклоняются с сообщением
novatel Журналы NovAtel OEM7 в формате ASCII или двоичном положение BESTPOS (только вычисленное решение), C/No RANGE по каждому спутнику и сигналу; флаги подавления RXSTATUS становятся self_report эпохи (состояние подавления 2, когда приёмник отмечает постановщик помех, 1, когда нет; флага спуфинга в RXSTATUS нет) и сопоставляются с тревогами Kshana в сравнении receiver-self-report
gnss-sdr Потоки GNSS-SDR Monitor и PVT-monitor в виде строк JSON C/N0 по каждому каналу, решение, используемые спутники, HDOP, азимут и угол места; создаётся integrations/gnss-sdr/relay.py

Подробности, источники и ограничения этих трёх форматов: MACHINE-INPUTS.md.

Каждая эпоха, которую создают читатели, также несёт source_span, смещения в байтах [start, end) во входных данных: наименьший непрерывный диапазон, содержащий записи, которые в неё вошли, так что точный необработанный фрагмент можно хешировать для окна события. NMEA: каждая строка, прочитанная, пока эпоха текущая (надмножество, если между её предложениями стоит посторонняя строка). UBX: кадры эпохи. RINEX 3: записи эпохи от каждой строки > до следующей. Android: строки Raw эпохи (строка Fix — только для эпохи без строк Raw). Отсутствует для живого потока и везде, где не определено однозначно (RINEX 2).

Файл передаётся через path (разрешается относительно папки самого сценария) либо внутри как text или base64 (единственные варианты в браузере).

Журнал ИНС как второй датчик#

[log.imu] добавляет инерциальный журнал на тех же часах:

[log.imu]
format = "csv"          # or "euroc"
path = "imu.csv"        # or text / base64
heading0_deg = 90.0     # heading at the first IMU sample
yaw_axis_down = true    # body z down (NED/FRD); false for z up (ENU/FLU)
time_offset_s = 0.0     # added to the IMU clock to reach the log's t_s

Скорость рыскания интегрируется от heading0_deg и заполняет курс везде, где в журнале приёмника его нет, поэтому монитор heading-course (которому нужен heading_sensor = true на платформе) сравнивает курс по гироскопу с путевым углом. Гироскоп дрейфует: курс точен лишь настолько, насколько хорошо окно интегрирования, а hdg_cog_tol_deg решает, что считать расхождением. Раскладка csv описана в MACHINE-INPUTS.md. Байты ИНС входят в хеш сценария и в хеш сценария пакета доказательств. Встроенная передача (text/base64) работает на любой поверхности; path отклоняется там, где файлы не читаются.

Платформа: стационарная или судно#

Описанные выше мониторы предполагают неподвижную антенну. Сценарий задаёт платформу до прогона в таблице верхнего уровня [platform]; без неё платформа — static, и ничего не меняется.

[platform]
kind = "vessel"            # "static" (default) or "vessel"
max_speed_kn = 30.0        # default 30
max_accel_mps2 = 0.5       # default 0.5
max_turn_rate_dps = 6.0    # default 6
antenna_height_m = 18.0    # no default: without it the sea-level monitor does not run
heading_sensor = false     # true: the stream carries a gyro heading (HDT or THS)
Ключ По умолчанию Почему такое значение
max_speed_kn 30 Выше эксплуатационной скорости торговых судов и большинства пассажирских судов, так что реальное судно её никогда не достигает; намного ниже скорости, которую подразумевает увод положения. Более медленное судно должно задать меньший предел: чем меньше предел, тем раньше замечается увод.
max_accel_mps2 0.5 Большое судно меняет скорость значительно меньше чем на 0,1 м/с²; небольшое быстрое судно при резком изменении тяги достигает нескольких десятых.
max_turn_rate_dps 6 Большое судно поворачивает значительно медленнее 1 град/с, а небольшое судно в крутом повороте — на несколько градусов в секунду.
antenna_height_m нет Свойство установки; догадка стала бы скрытым порогом.
heading_sensor false Если объявлено true, а в журнале его нет, это ошибка, поэтому отсутствующий датчик не может сойти за чистую проверку.

Ключи судна отклоняются при kind = "static". Для судна монитор position-jump не работает, потому что он измеряет расстояние от среднего значения калибровки, а это неверно для движущейся антенны; monitor_config результата записывает платформу и использованные пределы.

Синтетические потоки NMEA из kshana nmea-scenario, созданные для обучения экипажа, принимаются читателем NMEA и могут быть пропущены через этот вид, как любой журнал NMEA.

Мониторы#

Каждому монитору нужны его данные; монитор, данных которого нет в журнале, не запускается и не указывается в monitors_run. Первые calibration_s секунд (по умолчанию 60) задают базовый уровень и никогда не вызывают тревогу.

Монитор Тревога, когда Требуется
cn0-drop среднее C/N0 по спутникам, общим с базовым уровнем, падает на cn0_drop_db или более (по умолчанию 6 дБ) C/N0
loss-of-lock число отслеживаемых спутников оказывается на sats_lost или более ниже медианы базового уровня (по умолчанию 4) C/N0
agc показание АРУ отклоняется от базового уровня более чем на agc_k_sigma стандартных отклонений (по умолчанию 5) UBX MON-RF или Android AgcDb
jam-ind u-blox jamInd превышает jam_ind_threshold (по умолчанию 80 из 255) UBX MON-RF
position-jump собственное решение приёмника удалено более чем на position_jump_m от его среднего значения калибровки (по умолчанию 50 м) сообщённое положение
raim контроль целостности по чётности в приёмнике (RAIM) на собственном одиночном решении движка отвергает набор измерений журнал RINEX с nav
solve-failure одиночное решение движка не удаётся при пяти или более спутниках журнал RINEX с nav
clock часы приёмника отклоняются от своей предсказанной прямой больше чем на минимальное обнаруживаемое смещение шума его калибровки журнал RINEX с nav

Калиброванный профиль (по выбору). [monitors] profile = "jammertest-2024-calibrated" заменяет три порога помех значениями, калиброванными на открытом наборе данных JammerTest 2024 (один u-blox ZED-F9P на сценарий; Zenodo 15911589): cn0_drop_db = 1.5, agc_k_sigma = 3.5, jam_ind_threshold = 29. Калибровка была зарегистрирована заранее (xval/jammertest-trust/PREREGISTRATION.md), а профиль имеет статус MODELLED, подогнан по одной открытой кампании: его порог cn0_drop_db не выполнил заранее зарегистрированный порог ложных тревог на отложенной выборке (0,0231 против 0,02); пороги АРУ и индикатора подавления и объединённый порог (ложные тревоги 0,033, обнаружение 0,81) были выполнены (xval/jammertest-trust/RESULTS.md). Он справедлив для этого типа приёмника на этом событии, против запланированных слотов атак, и был откалиброван по журналам CSV набора данных (его журналы UBX дают больше ложных тревог в двух папках). Результат прогона с профилем повторяет применённые пороги и несёт profile_note со всем сказанным выше. Без профиля действуют значения по умолчанию, указанные выше, и все выходные данные не меняются.

Для платформы vessel дополнительно работают мониторы движущейся платформы (пороги — в таблице верхнего уровня [maritime], каждое значение по умолчанию описано в src/receiver_trust/maritime.rs); каждому нужны его данные, и он указывается в monitors_run только тогда, когда принял решение:

Монитор Тревога, когда Требуется
kinematic изменение положения расходится с сообщённой скоростью и курсом либо подразумевает скорость, ускорение или скорость поворота выше пределов судна решения; скорость и курс (RMC или VTG) для части счисления
heading-course курс по гироскопу и путевой угол различаются более чем на hdg_cog_tol_deg (скорость выше min_speed_kn) курс HDT, THS или VHW, heading_sensor = true
speed-log скорость относительно воды и скорость относительно грунта различаются более чем на stw_sog_tol_kn VHW или VBW и скорость относительно грунта
sea-level высота GGA отличается от antenna_height_m более чем на sea_level_tol_m высота GGA, antenna_height_m
cn0-spread разброс C/N0 по спутникам снижается до cn0_spread_frac от его калибровочной медианы GSV
cn0-rise C/N0 общих спутников выросло в среднем на cn0_rise_db, и у cn0_rise_frac из них оно выросло GSV
time-consistency последовательные времена эпох идут неравномерно, идут назад или расходятся с часами хоста время NMEA; часы хоста для последней части
osnma приёмник сообщает о сбое аутентификации OSNMA (читается сообщённый статус; ничего не проверяется; о проверке OSNMA по самим страницам I/NAV см. docs/OSNMA.md) Предложение состояния $PKSOS

Эпоха считается недоверенной (untrusted), когда тревогу подают RAIM, монитор часов, сбой решения или скачок положения либо падение C/N0 достигает удвоенного порога; ухудшенной (degraded) — при любой другой тревоге; и номинальной (nominal) в остальных случаях.

Путь движка (raim, solve-failure, clock) — это то же вычисление, что и оракул спуфинга JammerTest 2024 в tests/jammertest_spoof_oracle_support: tests/receiver_trust_jammertest.rs проверяет, что на всех восьми опубликованных началах первая тревога приходится на ту же эпоху и относится к тому же виду.

Оценка доверия (платформа-судно)#

Для судна каждая эпоха после калибровки также получает оценку от 0 до 100 и причины, по которым она не равна 100. Отображение детерминировано и зафиксировано до оценки любого журнала: без обучения, ничего не подгоняется под события. Каждый монитор сводит свои доказательства к отношению statistic / threshold (тревога при 1 и более); отношение стоит оценке

points = weight * clamp((ratio - onset_ratio) / (full_ratio - onset_ratio), 0, 1)
score  = round_to_0.1( clamp(100 - sum of points, 0, 100) )

поэтому монитор ничего не стоит, пока он ниже onset_ratio своего порога (по умолчанию 0,5), стоит половину своего веса на пороге и весь вес при пороге, умноженном на full_ratio (по умолчанию 1,5); оценка не возрастает ни по одной статистике. Источник, который не сообщает данных в эпохе, сохраняет свою последнюю статистику в течение evidence_hold_s (по умолчанию 10 с: два цикла предложения C/N0, отправляемого каждые 5 с), как в виде тревоги, так и в виде вычета.

Округление — вверх от половины (floor(x * 10 + 0.5) / 10), полоса определяется округлённой оценкой, а ограничение применяется до округления. В вычетах перечисляются только мониторы, баллы которых больше нуля, с их отношением и неокруглёнными баллами, сначала с наибольшими баллами; вычеты с равными баллами перечисляются в порядке имён мониторов cn0-drop, agc, jam-ind, loss-of-lock, position-jump, raim, clock, solve-failure, kinematic, heading-course, speed-log, sea-level, cn0-spread, cn0-rise, time-consistency, osnma, geometry. Монитору, чей тест даёт результат «пройден/не пройден» (неудачное решение, неудачная аутентификация), присваивается отношение 1,5, то есть полный вес. Перечисленные баллы — это баллы до ограничения на 0.

[score]
nominal_min = 90.0         # score >= 90: nominal
degraded_min = 55.0        # 55 <= score < 90: degraded; below 55: untrusted
onset_ratio = 0.5
full_ratio = 1.5
evidence_hold_s = 10.0
[score.weights]            # overrides; a monitor not listed keeps its default
sea-level = 30.0
Вес (баллы) Мониторы Почему
70 osnma сбой аутентификации — это утверждение, а не статистика
60 kinematic, raim, clock, position-jump, time-consistency поддельное положение должно противоречить физике или собственным проверкам приёмника, а время не может идти назад; одно нарушение в полную силу (1,5 порога: 60 баллов, оценка 40) даёт состояние untrusted, а на пороге (30 баллов, оценка 70) — degraded
40 heading-course, speed-log, solve-failure один независимый датчик с расхождением даёт degraded; два в полную силу — untrusted
30 sea-level, cn0-spread, cn0-rise, cn0-drop сигнальная обстановка или слабо информативная проверка
25 loss-of-lock, agc, jam-ind обстановка враждебна; одно это не показывает, что решение неверно
до 15, [geometry] geometry (0.36) не вес на этой шкале: ничего, если небо не плохое, тогда до cap_points (15); одна геометрия доходит до degraded, но никогда до untrusted и не подаёт тревоги (см. «Геометрия» ниже)

Границы полос следуют из весов: самый лёгкий монитор, подающий тревогу, стоит 12,5 балла на своём пороге, поэтому любая тревога выводит оценку из номинальной полосы; ни один монитор с весом менее 60 баллов не может в одиночку довести оценку до полосы untrusted (несколько вместе могут). Результат записывает модель оценки (score_model), а каждая эпоха несёт score.deductions: какие мониторы сделали вычет, их отношения и их баллы. Для судна состояние эпохи — это полоса её оценки.

Геометрия: естественная деградация отдельно от атакоподобной несогласованности#

Приёмник у портального крана, в шлюзе или под высоким корпусом теряет спутники, и его решение ухудшается по причине, не являющейся атакой. Начиная с 0,36, по проектной записке о геометрии (docs/design/2026-10-12-geometry-and-skyline-design.md, раздел 4), каждая эпоха несёт свою геометрию спутников, у оценки судна есть монитор геометрии, а каждый вычет несёт класс: geometry (небо), environment (сигнальная обстановка) или inconsistency (атакоподобные свидетельства).

Откуда берётся геометрия. Угол места и азимут каждого отслеживаемого спутника (NMEA GSV, UBX NAV-SAT), по которым единственным ядром DOP движка (orbit::dop, VALIDATED по gnss_lib_py) выводится геометрический фактор DOP (ухудшение точности): source = derived. Если это невозможно, используются DOP и число используемых спутников, которые сообщает приёмник (NMEA GSA): source = reported. Положения на небе и GSA действуют в течение evidence_hold_s оценки (по умолчанию 10 с), потому что многие шины отправляют GSV раз в несколько секунд; неполный в эпохе набор GSV (конец фрагмента, потерянное предложение) не используется. Журнал, в котором нет ни того ни другого, например NMEA только с GGA и RMC, а сегодня также журналы Android и RINEX, получает статус unknown с примечанием «geometry unknown»: движок не угадывает геометрию, которую ему не дали, и монитор геометрии не запускается.

geometry.status Значение
good достаточно спутников, а DOP в пределах порогов
degraded одна или несколько причин: few-satellites, high-pdop, high-hdop и (информационно, не входит в оценку) sky-blocked, когда все отслеживаемые спутники находятся в одной половине горизонта
unknown журнал не даёт геометрии спутников для этой эпохи

Статус носит описательный характер, а для оценки важна метка (ниже): degraded включает информационный случай sky-blocked, который может сочетаться с хорошим или удовлетворительным DOP, и оценивается только метка poor.

Метка (всегда сообщается, никогда не оценивается). geometry.label каждой эпохи — это good (HDOP не более hdop_good 2, PDOP не более pdop_good 3 и не менее used_good 6 спутников), poor (HDOP выше hdop_max 4, PDOP выше pdop_max 6 или менее min_used 5 спутников), fair между этими двумя, либо unknown. Примечание эпохи всегда начинается с метки и чисел, например «geometry fair: HDOP 1,105, PDOP 1,831, VDOP 1,410, 8 satellites; not scored (the score deducts only above HDOP 4, PDOP 6 or below 5 satellites)», и добавляет «, signal loss», когда это имеет место.

Монитор геометрии (оценка судна). Он ничего не вычитает, если геометрия не плохая: его отношение равно max(hdop / hdop_max, pdop / pdop_max, min_used / n_used), и пока оно не превышает 1 (хорошая или удовлетворительная геометрия, включая открытое небо только для GPS с 8 спутниками), он ничего не стоит, так что такой журнал оценивается точно так же, как в 0,35. Выше 1 вычет линейно нарастает до cap_points (15) при full_ratio (1,5 порога: HDOP 6, PDOP 9, 3 спутника): 7,5 балла при отношении 1,25 (HDOP 5 или 4 спутника), 15 начиная с 1,5 (оценка 85, degraded). Сам по себе он никогда не опускает оценку ниже floor (по умолчанию: degraded_min оценки, 55), поэтому одна геометрия доводит оценку самое большее до полосы degraded и никогда до untrusted: действующее ограничение равно min(cap_points, 100 - floor), поэтому таблица [score], повышающая degraded_min (например, таблица 0,35 с degraded_min 90), снижает ограничение до 100 - degraded_min, а не отклоняется, а floor, заданный ниже degraded_min, отклоняется. Монитор не подаёт тревоги; он указывается последним в monitors_run, когда принял решение хотя бы в одной эпохе (для судна, чей журнал даёт геометрию), и не учитывается в тождестве 0,35; он не является элементом [score.weights] (его задание отклоняется), а действующие ограничение и нижняя граница записываются в geometry_model.cap_in_force и geometry_model.floor_in_force. Примечание плохой эпохи объясняет причину, например «geometry poor: HDOP 5,200, PDOP 7,900, VDOP 4,100, 7 satellites; degraded by geometry: HDOP 5,200 above 4 (derived from 7 logged satellites); not an inconsistency».

Классы. Каждый вычет (score.deductions[].class):

Класс Мониторы
geometry geometry (плохая геометрия); loss-of-lock, когда каждый спутник, потерянный относительно калибровочного базового уровня, при последнем сообщении был ниже low_elev_deg
environment cn0-drop, cn0-spread, cn0-rise, agc, jam-ind, в остальных случаях loss-of-lock; geometry при потере сигнала (ниже)
inconsistency kinematic, heading-course, speed-log, sea-level, time-consistency, clock, raim, position-jump, osnma, solve-failure; и geometry, когда геометрия плохая, а заявленный HDOP расходится с заявленным небом (ниже)

Защита от подделанного плохого неба. Спуфер мог бы отправить скудный GSV или сообщить большой HDOP, чтобы купить допуск. Движок помечает и не прощает: никакая другая проверка не расширяется из-за плохой геометрии, а HDOP, который заявляет приёмник (GSA в пределах удержания, иначе GGA), сравнивается с HDOP, выведенным из заявленного им неба. Когда они различаются более чем на max(reported_vs_derived_tol, reported_vs_derived_rel × derived HDOP) (1,0 или 25 % выведенного HDOP, в зависимости от того, что больше), эпоха несёт reported_derived_mismatch: true и, когда геометрия плохая, её вычет относится к классу inconsistency. При хорошей или удовлетворительной геометрии расхождение сообщается и не оценивается: ничего не вычитается, если геометрия не плохая. Это осознанный выбор (0,36): «ноль, если не плохая» справедливо для каждой эпохи, поэтому хорошая и удовлетворительная геометрия оцениваются точно так же, как в 0,35, а поддельный HDOP никогда не остаётся молчаливым. Флаг (reported_derived_mismatch) стоит в каждой эпохе результата и в каждой живой строке, примечание гласит «…beyond the tolerance: reported, not scored (the geometry is not poor)» или «…: the geometry deduction is classed an inconsistency», geometry_summary.hdop_mismatch считает такие эпохи, а страница сводки доказательств показывает число, включая ноль. Значение 25 % — это REPRESENTATIVE инженерный выбор, а не измеренная или стандартная величина: приёмник, который решает по одним часам на каждую орбитальную группировку или по подмножеству отслеживаемых спутников (маска угла места, отсечка по C/N0, исключение по RAIM), заявляет HDOP, который отличается от HDOP по одним часам для всех отслеживаемых спутников на величину, растущую с HDOP, поэтому один лишь фиксированный допуск принимал бы естественную плохую геометрию за несогласованность. Большая разница по-прежнему считается несогласованностью. Вычет класса geometry — это утверждение о небе, а не вывод о том, что атаки не было.

Потеря сигнала — это не небо. Когда спутники, которые приёмник перечисляет как находящиеся в зоне видимости (выше low_elev_deg), не отслеживаются, движок также выводит DOP для каждого спутника в зоне видимости. Если этот DOP в зоне видимости в пределах порогов и либо ничего не отслеживается, либо неотслеживаемые спутники распределены по горизонту (не сосредоточены в одной его половине), то небо было на месте, а сигнала не было: эпоха несёт signal_loss: true, примечание «satellites in view but not tracked: signal environment, not geometry», а вычет геометрии относится к классу environment. Неотслеживаемые спутники, сосредоточенные в одной половине горизонта, по-прежнему считаются заблокированным небом (стена, скала, причал). Двустороннее препятствие (каньон или узкий фьорд) считается потерей сигнала, пока не настроена линия горизонта. Сентинелы «нет решения» у приёмника — это не DOP: заявленный HDOP или PDOP 99 и более, качество GGA 0 и режим GSA 1 не дают заявленного DOP.

Ключ [geometry] По умолчанию Происхождение
hdop_max 4 выбрано (проектная записка 4,2): при sigma_uere_m 1,5 м HDOP 4 соответствует 2drms в 12 м, то есть kin_pos_tol_m кинематической проверки. IEC 61108-1 обычно цитируют как стандарт, задающий требования к точности при HDOP ≤ 4 (или PDOP ≤ 6): это вспомнено по вторичной литературе о приёмниках ГНСС, конкретный вторичный источник не рассматривался, а сам стандарт не читался; подлежит сверке с текстом стандарта
pdop_max 6 порог доступности по PDOP scenarios/polar-arctic-leo-coverage.toml; GPS SPS Performance Standard, 5-е изд. (2020), PDOP ≤ 6, как цитируется в tests/constellation_availability_published_spec_oracle.rs; подлежит сверке с текстом стандарта
min_used 5 на один больше, чем нужно для решения, поэтому одиночная потеря не прекращает решение; наименьшее число с одним избыточным измерением для RAIM
sigma_uere_m 1.5 заданный вход для связи 2drms в hdop_max; задайте собственное значение приёмника
reported_vs_derived_tol 1,0 HDOP выбрано (проектная записка 4,2): абсолютная часть допуска; расхождение за пределами допуска — не геометрия
reported_vs_derived_rel 0.25 REPRESENTATIVE инженерный выбор: относительная часть, для приёмников с часами по каждой орбитальной группировке или с решениями по подмножеству
low_elev_deg 15 проектная записка 4,3: спутники, потерянные ниже него, потеряны для неба; линия горизонта (0,36, пункт 1) расширит это
azimuth_gap_deg 180 геометрический, информационный: все спутники в одной половине горизонта
cap_points 15 проектная записка 4,2: одна геометрия переводит оценку из номинальной в degraded и никогда в untrusted; вычитается, только когда геометрия плохая (решение основателя B1, 0,36)
full_ratio 1.5 собственный full_ratio оценки: шкала достигает cap_points при 1,5 порога плохой геометрии
floor degraded_min оценки (55) одна геометрия никогда не доходит до полосы untrusted; действующее ограничение равно min(cap_points, 100 - floor); нижняя граница ниже degraded_min отклоняется
hdop_good 2 REPRESENTATIVE, только для метки: половина hdop_max (начальное отношение оценки 0,35)
pdop_good 3 REPRESENTATIVE, только для метки: половина pdop_max
used_good 6 REPRESENTATIVE, только для метки: на один больше min_used, так что одиночная потеря всё ещё оставляет избыточное измерение

Что изменилось в выходных данных, а что нет. Ни одна оценка не меняется, если геометрия не плохая; метка сообщается всегда. Добавленные ключи: epochs[].geometry (status, reasons, source, n_used, hdop, pdop, vdop, округлённые до 0,001, open_fraction (null, пока не настроена линия горизонта), max_azimuth_gap_deg, age_s, reported_hdop, reported_derived_mismatch, note, in_view (спутники, перечисленные выше low_elev_deg), signal_loss, label), class в каждом вычете, а в конце geometry_model (пороги, удержание, действующие ограничение и нижняя граница, происхождение, правила классов) и geometry_summary (эпохи по статусу, по метке и по классу вычета, а также эпохи с расхождением заявленного HDOP); score_model.weights — как в 0,35. Два теста удерживают это на уровне байтов базового уровня «0,36 portable» после удаления этих ключей (хеши 0.35.0, пересчитанные, когда движок перешёл на переносимую математику, и сохранённые в тестах как запись того, что выпустила 0.35.0: docs/revisions/STUDIO15-portable-math-rebaseline.md). tests/geometry_trust_no_change.rs фиксирует каждый выходной файл обоих примеров сессий (JSON результата, CSV, SVG, вердикт, воспроизведённые живые строки, $PKSHT, Prometheus, CEF, LEEF). tests/geometry_corpus_identity.rs фиксирует пакетный результат, сводку, живые строки и $PKSHT каждого зафиксированного корпуса NMEA судна (демонстрация и морские эталонные данные, каждый также как открытое небо только для GPS с 8 спутниками, и учебные сценарии для экипажа, сгенерированные из их начальных значений); в двух учебных сценариях, где подавление делает геометрию плохой, каждая эпоха, не являющаяся плохой, идентична, а каждая плохая отличается только вычетом геометрии. Журнал, геометрия которого плохая, теряет до 15 баллов, которые он не терял в 0,35; это задуманное изменение. В демонстрации увод положения читается как inconsistency; tests/geometry_trust.rs правит журнал так, чтобы замаскировать западное небо (удовлетворительно: помечается, не оценивается), замаскировать его и два восточных спутника (плохо: вычеты геометрии, потеря захвата — environment, поскольку высокие спутники тоже пропали), потерять только спутники ниже 15° (потеря захвата — geometry) и заявить HDOP, которого небо не объясняет (вычет за плохую геометрию становится inconsistency). Пороги имеют статус MODELLED: выбраны, а не подогнаны ни под какой журнал; ничто здесь не утверждает, как часто естественную деградацию и атаки удаётся различить на реальных данных.

Задание событий и предсказаний#

[[events]]
label = "jammer on"
kind = "jamming"            # jamming, spoofing or other
onset_s = 120.0             # seconds since the first epoch of the log
end_s = 240.0               # optional; otherwise onset_s + compare.horizon_s
predicted_cn0_drop_db = 12.0  # optional, e.g. from a `jamming` scenario's link budget

[compare]
detect_tol_s = 10.0         # an alarm this soon after onset counts as a detection
cn0_tol_db = 3.0            # predicted and observed drops agree within this

Для каждого события результат даёт ложные тревоги между калибровкой и началом, первую тревогу и её мониторы, задержку, исход (detected, late, missed или not-evaluable) и наблюдаемое медианное падение C/N0 в сравнении с предсказанием (agree, disagree, no-prediction или not-evaluable). Расхождение сообщается и никогда не «подгоняется»: допуски находятся в сценарии, а сценарий входит в хеш результата.

Чего это не утверждает#

Мониторы доверия имеют статус MODELLED: их пороги — входные данные, а их эффективность обнаружения на реальных помехах — это то, что измеряет оценка событий, а не то, что утверждает этот вид. На сессиях спуфинга JammerTest 2024 мониторы пути движка ловят одни начала в пределах 10 с и опаздывают на других; числа см. в строке «Clock-aided χ², RAIM, AGC, SQM fused per-epoch security FoM» в VERIFICATION-MATRIX.md. Предсказание, согласующееся с одним журналом, — это одна точка данных, а не валидация.