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

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

Карта помех и воздействие на маршрут

kshana interference-map строит посуточную картину того, где воздушные и морские суда сообщали о деградированных навигационных данных, по открыто лицензируемым файлам слежения, которые вы предоставляете. kshana route-exposure затем сообщает, какая часть запланированного маршрута в каждый из дней лежала внутри затронутых ячеек.

Что это такое: описание того, как выглядели опубликованные сообщения о положении в прошлые дни. Чем это не является: измерением какого-либо приёмника, прогнозом или доказательством помех. У деградированной или аномальной ячейки может быть несколько возможных причин (перечислены ниже), а ячейка без записи просто не наблюдалась.

Всё работает на локальных файлах. Команды по умолчанию никогда не открывают сетевое соединение; единственный необязательный помощник загрузки требует явного флага.

Два синтетических дня карты помех из зафиксированных образцов в examples/interference-map/output, у каждого свой синтетический маршрут и доли подверженности маршрута, которые сообщает движок. Слева день ADS-B: 1 деградированная ячейка, 6 не деградированных, 4 неоценённых (штриховка) и ячейки без записи, нарисованные пустыми, которые не наблюдались. Маршрут длиной 525 км: 11,5% его протяжённости в деградированных ячейках, 38,3% в не деградированных, 9,1% в неоценённых и 41,1% в ненаблюдавшихся. Справа день AIS: ячейки, помеченные детекторами круга и «на суше», неаномальные ячейки и пустые ячейки; маршрут длиной 454 км, из них 10,5% в аномальных ячейках, 21,2% в неаномальных, 0,0% в неоценённых и 68,3% в ненаблюдавшихся. ADS-B и AIS — отдельные слои, они никогда не объединяются. Помеченная ячейка не является выводом о наличии помех, а ячейка без цвета не наблюдалась, что не равно «чисто». Все данные вымышлены, расположены в открытой средней части Атлантики и не являются измерением.Два синтетических дня карты помех из зафиксированных образцов в examples/interference-map/output, у каждого свой синтетический маршрут и доли подверженности маршрута, которые сообщает движок. Слева день ADS-B: 1 деградированная ячейка, 6 не деградированных, 4 неоценённых (штриховка) и ячейки без записи, нарисованные пустыми, которые не наблюдались. Маршрут длиной 525 км: 11,5% его протяжённости в деградированных ячейках, 38,3% в не деградированных, 9,1% в неоценённых и 41,1% в ненаблюдавшихся. Справа день AIS: ячейки, помеченные детекторами круга и «на суше», неаномальные ячейки и пустые ячейки; маршрут длиной 454 км, из них 10,5% в аномальных ячейках, 21,2% в неаномальных, 0,0% в неоценённых и 68,3% в ненаблюдавшихся. ADS-B и AIS — отдельные слои, они никогда не объединяются. Помеченная ячейка не является выводом о наличии помех, а ячейка без цвета не наблюдалась, что не равно «чисто». Все данные вымышлены, расположены в открытой средней части Атлантики и не являются измерением.

Оба дня — синтетические образцы в examples/interference-map/output/, у каждого свой синтетический маршрут в examples/interference-map/input/; доли подверженности маршрута — это то, что kshana route-exposure выводит для них. Ничто в них не является реальным местом, воздушным или морским судном.

Данные и лицензии#

Встроены только источники, проверенные в data/INTERFERENCE-DATA-SOURCES.md; они выбираются с помощью --dataset:

--dataset Вид Лицензия Поле лицензии в выходных данных
adsb-lol ADS-B ODbL 1,0 ODbL-1.0
noaa-marinecadastre AIS Общественное достояние США (CC0 1,0 по данным издателя) общественное достояние
kystverket AIS NLOD 2,0 NLOD-2.0
custom любая то, что вы укажете требуются --licence, --licence-url, --attribution

Каждый выходной файл содержит встроенные лицензию, URL лицензии, требуемое указание авторства и ограничения охвата источника. Встроенных записей для источников, условия которых запрещают коммерческое использование или распространение производных карт, нет.

ODbL и редакции Kshana. Файл ADS-B по ячейкам, созданный из данных adsb.lol, является Derived Database и выпускается под ODbL 1,0; его поле licence сообщает об этом. Собственный код Kshana остаётся под AGPL-3.0. Коммерческие редакции Kshana, записывающие такие файлы, несут то же уведомление, и всякий, кто повторно публикует файл, обязан его сохранить. Результаты ADS-B и AIS никогда не объединяются в одном файле, поэтому ODbL не распространяется на слой AIS.

Смещение охвата. Карты наследуют охват своего источника. adsb.lol опирается на приёмники добровольцев, поэтому открытое море и слабо охваченные районы представлены скудно. Открытые данные Kystverket не включают рыболовные суда короче 15 м и прогулочные суда короче 45 м, поэтому его карта отражает более крупные суда. MarineCadastre охватывает только воды США.

Идентификаторы. Входные строки содержат идентификатор воздушного или морского судна. Он хешируется в памяти со случайной солью на каждый прогон, используется только для подсчёта различных воздушных или морских судов и никогда не записывается, не заносится в журнал и не выводится. Выходные данные сводные: ни один идентификатор в них нигде не появляется.

Подавление. Ячейка, в которой за день наблюдалось менее 5 различных воздушных или морских судов, не публикуется вообще, а для AIS число помеченных, равное одному или двум, в опубликованной ячейке сообщается как null. Никакое опубликованное значение не позволяет выделить отдельное воздушное или морское судно.

Форматы входных данных#

Обычный CSV со строкой заголовка, значения через запятую, без кавычек, время в UTC. Порядок столбцов свободный; неизвестные столбцы игнорируются. Отметка времени — секунды Unix или YYYY-MM-DDTHH:MM:SS[.fff]Z. Строки, которые не удаётся разобрать, учитываются в метаданных вывода, а не отбрасываются молча.

ADS-B (kshana interference-map adsb):

Столбец Обязателен Значение
timestamp да время сообщения
aircraft_id да непрозрачный идентификатор (хешируется, никогда не сохраняется)
lat, lon да градусы
alt_baro_ft да высота в футах; ground или пустое значение исключает строку
nic, nacp хотя бы один целые числа от 0 до 11; пустое значение означает «не сообщено»
source_type нет если присутствует, используются только значения, начинающиеся с adsb (MLAT, TIS-B и подобные исключаются)

История readsb от adsb.lol, чтение напрямую. Ежедневные архивы — это разделённый tar файлов трасс readsb. Распакуйте один из них так, как описывает издатель (соедините части, tar -xf -), затем передайте Kshana распакованный каталог, один файл trace_full_*.json или CSV:

kshana interference-map adsb extracted-day/ --dataset adsb-lol --out maps/

Каталог просматривается рекурсивно в поисках файлов trace_full_* (другие файлы, например trace_recent_*, игнорируются, а символические ссылки не раскрываются). Каждый файл либо сжат gzip, либо является обычным JSON; это определяется по первым байтам, потому что readsb записывает gzip под именем .json; распаковка ограничена 512 МиБ на файл. Из каждой записи трассы Kshana считывает смещение времени, широту, долготу, высоту (маркер ground исключает запись), поля nic и nac_p объекта сведений о воздушном судне и строку источника положения (записи не из ADS-B исключаются). Запись без объекта сведений не имеет полей точности и учитывается как исключённая. Файл, который не удаётся прочитать, учитывается в trace_files_unreadable в метаданных вывода и пропускается.

AIS (kshana interference-map ais):

Столбец Обязателен Значение
timestamp да время сообщения
vessel_id да непрозрачный идентификатор (хешируется, никогда не сохраняется)
lat, lon да градусы; значения AIS «недоступно» и (0, 0) отбрасываются
sog_kn нет скорость относительно грунта в узлах; 102,3 («недоступно») трактуется как пропуск

Детектору «на суше» нужен файл полигонов суши (--land): GeoJSON Polygon или MultiPolygon. Предполагается слой суши Natural Earth в масштабе 1:10 млн (общественное достояние). Без --land детектор выключен, и вывод сообщает об этом. Чтобы загрузить его (единственный сетевой доступ в этой функции):

kshana interference-map fetch-land --out ne_10m_land.geojson --allow-network

Это запускает системную программу curl (списком аргументов, не через оболочку) по HTTPS, один раз, для одного файла по фиксированному адресу, который указывает на один коммит исходного репозитория. Загруженный файл сохраняется, только если его SHA-256 равен значению, зафиксированному в коде; при несовпадении он удаляется, и команда завершается ошибкой. Если curl не установлена, команда сообщает об этом и выводит адрес и контрольную сумму, чтобы файл можно было получить вручную. В репозиторий ничего не включено.

Вывод#

Один файл GeoJSON на каждый источник за каждые сутки UTC, в каталоге --out, с именем <source>-<dataset>-<YYYY-MM-DD>.geojson, где <source> — это adsb или ais, <dataset> — ключ --dataset (adsb-lol, noaa-marinecadastre, kystverket или custom), а дата — сутки UTC, к которым относятся сообщения: adsb-adsb-lol-2026-03-01.geojson, ais-kystverket-2026-03-01.geojson. Набор данных указан в имени, чтобы два набора данных одного вида не могли занять один файл, и Kshana никогда не перезаписывает файл: если любая из целей существует, команда завершается ошибкой до того, как что-либо будет записано. CLI выводит по одной строке на каждый записанный файл. Три части можно восстановить с помощью ^(adsb|ais)-(.+)-(\d{4}-\d{2}-\d{2})\.geojson$; та же информация есть внутри файла (source_kind, data.dataset, date). Синтетические файлы-образцы, по одному на источник, находятся в examples/interference-map/output/ вместе с командой, которая их воссоздаёт, — для разработки и проверки программы просмотра. Элемент верхнего уровня kshana_interference_map содержит название схемы, дату, сетку, идентификатор метода и все параметры, показатели за сутки, лицензию на данные и указание авторства, а также уведомление. Каждый объект (feature) — это полигон ячейки с cell_i, cell_j, status, degraded и сводными счётчиками.

Сетка фиксирована: квадратные ячейки по --cell-deg градуса (по умолчанию 0,5) по широте и долготе, индексируемые от (-90, -180). Ширина с востока на запад уменьшается с широтой: при значении по умолчанию около 55 км на экваторе и около 28 км на 60 градусах. Широта 90 относится к последней строке, а долгота 180 — тот же меридиан, что и -180. Предварительно зарегистрированные методы определены на сетке 0,5 градуса. Другое значение --cell-deg работает, но не является предварительно зарегистрированным методом: CLI сообщает об этом, а файл записывает grid.preregistered_cell_deg и grid.is_preregistered (false), чтобы программа просмотра могла сообщить об этом тоже.

Скрытые счётчики. Каждый счётчик по ячейке ниже минимума для публикации (5) равен null, включая нулевые, поэтому null никогда не означает «ноль», и никакое опубликованное число не позволяет выделить отдельное воздушное или морское судно. Поля status и degraded по-прежнему выдаются. В файлах ADS-B aircraft_observed для опубликованной ячейки всегда не меньше 5; aircraft_sampled и aircraft_affected равны null при значениях ниже 5, а affected_share равно null, если оба не меньше 5 (доля раскрыла бы скрытый счётчик). В файлах AIS каждый элемент vessels_flagged равен null при значении ниже 5.

Версия формата и поля#

Формат установлен как schema: "kshana-interference-map/v1", format_version: 1. Изменение, которое удаляет или переименовывает поле либо меняет его смысл, повышает версию; добавление поля версию не повышает, поэтому считывающая программа должна игнорировать неизвестные ей поля. Счётчик, равный null, скрыт, а не равен нулю.

FeatureCollection
  kshana_interference_map
    schema, format_version, kshana_version, notice
    source_kind            "adsb" | "ais"
    date                   "YYYY-MM-DD" (UTC)
    grid                   { type: "fixed_lat_lon", cell_deg, preregistered_cell_deg, is_preregistered }
    method                 { id, summary, parameters{...}, guards[], input_stats{...}, caveats[] }
    day                    day-level figures (ADS-B: background_evaluated, background_share,
                           day_confounded, cells_published, cells_suppressed_below_min_distinct;
                           AIS: on_land_detector, cells_published, cells_suppressed_...)
    data                   { dataset, name, licence, licence_url, attribution, coverage_notes[] }
  features[]               Polygon (one closed ring, [lon, lat], counter-clockwise)
    properties             cell_i, cell_j, status, degraded,
                           ADS-B: aircraft_observed, aircraft_sampled|null, aircraft_affected|null,
                                  affected_share|null
                           AIS:   vessels_observed, vessels_flagged{detector: n|null}, detectors[]

Значения status: ADS-B — degraded, not_degraded, insufficient_sample, withheld_day_confounded, withheld_no_background; AIS — anomalous, not_anomalous. degraded истинно только для degraded и anomalous. У отчёта о подверженности маршрута своя схема kshana-route-exposure/v1.

Обсерватория: ежедневный продукт#

kshana interference-map daily превращает одни сутки UTC сообщений ADS-B в три файла обсерватории (GeoJSON, PNG и JSON отчёта об инцидентах) и манифест хешей, а kshana interference-map sensitivity воспроизводит эти сутки при поочерёдном изменении предварительно зарегистрированных параметров. Команда обеспечивает соблюдение правил выпуска: не менее 24 ч после окончания суток (--delay-hours можно только увеличивать), ячейки не мельче 0,5 градуса, только три состояния (degraded, not_degraded, not_observed), причины не приписываются, данные OpenSky не используются. Описание метода, форматы файлов и ограничения приведены в observatory/METHOD.md. Региональный прогон (--bbox south,west,north,east) считывает только сообщения из заданной рамки и указывает это в файле.

Спецификация страницы карты в Studio#

Для страницы карты в Studio, которая загружает файл GeoJSON, предоставленный пользователем (страница создаётся в исходном коде Studio, а не в этом репозитории). Ничего не выгружается: файл читается в браузере, и страница не отправляет запросов ни к какому источнику данных.

Чтение файла. Принимайте файл, только если kshana_interference_map.schema начинается с kshana-interference-map/, а format_version — число, которое страница поддерживает; иначе сообщите, что файл не является поддерживаемой картой помех Kshana. Читайте поля, перечисленные выше; неизвестные игнорируйте. Файл содержит один источник и одни сутки. Можно загрузить несколько файлов.

Ячейки. Рисуйте полигон каждого объекта (feature). Раскрашивайте по status и всегда поясняйте в легенде, что означает каждый цвет. Четыре состояния, ни одно из которых нельзя скрывать или объединять:

Состояние Значение Откуда берётся
Деградированная (или аномальная для AIS) в этот день высокая доля воздушных судов сообщала низкую точность или у морских судов наблюдались аномалии degraded истинно
Чистая наблюдалась при достаточном числе воздушных или морских судов и не помечена status равно not_degraded или not_anomalous
Неоценённая наблюдалась, но выборка воздушных судов недостаточна, день был искажён влиянием общего фона либо фон дня не удалось оценить status равно insufficient_sample, withheld_day_confounded или withheld_no_background
Не наблюдалась слишком мало воздушных или морских судов, поэтому ячейка не опубликована для этой ячейки нет объекта (feature)

«Не наблюдалась» означает отсутствие объекта (feature): рисуйте её нейтрально (без заливки или со штриховкой), но никогда цветом «чистой». Используйте цветовую схему, которая не опирается только на красный и зелёный. Сведения о ячейке при наведении или выборе показывают счётчики из properties как есть, а каждый счётчик null — как «скрыто (менее 5)», но никогда не как ноль. Программа просмотра также должна сообщать, когда grid.is_preregistered равно false, что в файле использован размер ячейки вне предварительно зарегистрированного метода.

Слои. ADS-B и AIS — отдельные слои со своими переключателями, легендами и строками лицензий. Никогда не объединяйте их в одно цветовое поле или один счётчик, никогда не рисуйте их как одну общую ячейку и никогда не суммируйте их ячейки. Разные дни — разные файлы: показывайте по одному дню с помощью выбора дня, а не накладывайте дни друг на друга.

Лицензия и указание авторства. Для каждого видимого слоя показывайте на карте, а также в любом экспорте или снимке экрана текст data.attribution дословно, data.licence со ссылкой на data.licence_url и data.coverage_notes. Если показан слой ADS-B, его лицензия ODbL распространяется только на этот слой. Не предлагайте экспорт объединённого файла.

Метод. Предоставьте на панели сведений идентификатор метода и параметры (method.id, method.parameters) и показатели day, включая day_confounded (прямо сообщите, что ни одна ячейка не названа деградированной из-за фона дня) и input_stats.

Оговорки, которые нужно сохранить (показывайте собственные method.caveats и notice файла и держите эту формулировку или её эквивалент видимой рядом с картой):

  • Деградированная или аномальная ячейка не является выводом о наличии помех. Существуют и другие причины.
  • Ячейка без цвета не наблюдалась. Это не свидетельство того, что район был чистым.
  • Это описание прошлых сообщений о положении, а не прогноз и не измерение какого-либо приёмника.
  • Охват определяется источником данных; прочитайте примечания об охвате.

Страница не должна называть или показывать какое-либо отдельное воздушное или морское судно, и файлы таких сведений не содержат.

Подверженность маршрута. Если страница загружает и отчёт kshana-route-exposure/v1, показывайте его строки по дням и источникам вместе со всеми четырьмя долями (деградированные, чистые, неоценённые, ненаблюдавшиеся), а не только долю деградированных, а также caveats отчёта и map_licence и map_attribution каждой строки.

Область излучателя (interference-map locate)#

kshana interference-map locate оценивает, где может находиться источник помех, по тем же открытым сообщениям ADS-B или AIS: область на заданных уровнях доверия, центроид с эллипсом неопределённости и сеансы во времени. Она никогда не возвращает одну точку. MODELLED (строка реестра VM-bc70cb57): физика — это учебниковая радиопрямая видимость, достоверность областей зависит от модели, а единственная внешняя проверка — сравнение Jammertest, заранее зарегистрированное в emitter-region/PREREGISTRATION.md. Публикация областей излучателей в публичной обсерватории — отдельное решение владельца и отключена: обсерватория не читает этот модуль, и тест это закрепляет.

kshana interference-map locate input.csv --source adsb --dataset adsb-lol --out regions/
kshana interference-map locate day-dir/  --source adsb --dataset adsb-lol --out regions/     # readsb traces
kshana interference-map locate ais.csv   --source ais  --dataset kystverket --land land.geojson --out regions/

Параметры: --levels 0.5,0.9,0.95, --cell-km, --session-gap-min, --emitter-height-m min,max, --ship-height-m (AIS), --tdoa fix.json (объединить с определением TDOA; ниже). Любой параметр, отличающийся от заранее зарегистрированного значения, объявляется как «не заранее зарегистрированный метод». Файлы никогда не перезаписываются.

Модель. Сообщения собираются в единицы (одно воздушное или морское судно в одном 2-минутном интервале), каждая из которых затронута (ADS-B: не менее половины её сообщений имеют низкий NIC или NACp по собственному правилу карты; AIS: неправдоподобная скорость или скачок, либо положение на суше за береговой линией) или нет. Воздушное или морское судно учитывается, только если во входных данных у него не менее 5 хороших сообщений (та же защита по базовому уровню оборудования, что и у карты). Затронутые единицы группируются в сеансы: промежуток более 30 минут разделяет их во времени, а внутри временного кластера две единицы относятся друг к другу, когда один излучатель мог быть в зоне досягаемости обеих. Для одного сеанса каждая возможная позиция на сетке получает правдоподобие:

  • Прямая видимость. Воздушное судно на высоте h и антенна излучателя на высоте h_e видят друг друга на расстоянии до 4.122 (sqrt h + sqrt h_e) км на радиусе Земли 4/3. h_e маргинализируется по диапазону от 2 до 100 м (8 значений с логарифмическим шагом).
  • Мощность. Мощность излучателя неизвестна, поэтому эффективная дальность от 12 км до 426 км (шаг 1,25) или «только прямая видимость» ограничивает зону досягаемости; маргинализация выполняется с равным весом. Край зоны досягаемости мягкий: единица считается находящейся в зоне досягаемости с весом, линейно убывающим на 15% зоны досягаемости с каждой стороны (не менее трёх четвертей ячейки сетки), потому что рельеф, диаграмма направленности антенны и трасса неизвестны точнее.
  • Затронутые и незатронутые. Воздушное или морское судно восприимчиво с вероятностью 0,6 (оборудование приёмника, антенна и тракт сигнала различаются), единица восприимчивого судна в зоне досягаемости затронута с вероятностью 0,9, а любая другая единица затронута с фоновой вероятностью 0,03 (другие причины). Восприимчивость — скрытое свойство воздушного судна, суммируемое по каждому судну, как и устойчивая неисправность оборудования (вероятность 0,03: все единицы этого воздушного судна затронуты, где бы оно ни было), что не даёт неисправному транспондеру тянуть область за собой. Поэтому затронутое воздушное судно смещает апостериорное распределение к позициям, которые его охватывают, а незатронутое воздушное судно с прямой видимостью смещает его от позиций, которые охватили бы его, не более чем на вес одного воздушного судна, сколько бы сообщений оно ни отправило, тогда как незатронутое воздушное судно без прямой видимости ничего не говорит.
  • Без учёта рельефа. Рельеф, волноводное распространение и дифракция не моделируются (рельеф может только убрать прямую видимость).
  • Темперирование. Логарифм правдоподобия умножается на 0,3 (степенное апостериорное распределение). Свидетельства модели более зависимы, а её края менее резки, чем независимость, которую предполагает правдоподобие, и на синтетических данных с такими недостатками нетемперированные области содержали истинный источник заметно реже заявленной доли; 0,3 выбрано только на синтетических данных (вывод находится в docs/emitter-region/PREREGISTRATION.md, раздел 8).

Апостериорное распределение даёт области наибольшей плотности на заданных уровнях (вложенные; многоугольники с отверстиями там, где нужно), взвешенный по апостериорному распределению центроид и его эллипс моментов (масштабированный до 95% при гауссовом законе; калиброванные уровни — это многоугольники, а не эллипс). Сетка грубая (около 5–10 км) по всей области, и каждая грубая ячейка, содержащая массу, делится 4 на 4. Смещение покрытия учитывается условием по тому, где есть сообщения: область широка там, где было мало воздушных судов, а сеанс, область 95% которого занимает более 80% его области поиска, сообщается как unconstrained без центроида.

Сеансы и профиль. Каждый опубликованный сеанс имеет начало, конец, длительность, площадь ячеек 0.5 градуса, содержащих затронутую единицу и не менее 5 различных воздушных или морских судов, числа различных затронутых и наблюдавшихся единиц и контрдоказательства (различные незатронутые с прямой видимостью до центроида; скрываются ниже 5). Сеанс с менее чем 5 различными затронутыми воздушными или морскими судами скрывается и учитывается. Профиль даёт для каждого часа суток UTC число различных затронутых воздушных судов (null ниже 5) и минуты времени сеанса, а также часы времени сеанса за сутки UTC.

Необязательное объединение с TDOA. --tdoa fix.json принимает {"receivers": [[lat, lon, height_m], ...], "tdoa_s": [...], "sigma_s": 1e-7, "start": [lat, lon]} (не менее трёх приёмников; tdoa_s[i] — это приёмник i+1 минус приёмник 0). Позиция решается на поверхности Земли методом Гаусса–Ньютона по geolocation::tdoa_predict, её ковариация равна geolocation::tdoa_crlb_on_sphere, и гауссиан умножается в апостериорное распределение. Граница Крамера–Рао — нижняя граница для модели, а не измеренная точность.

Выходные данные (emitter-<source>-<dataset>-<day>): .geojson (область MultiPolygon на каждый уровень и сеанс, многоугольник эллипса, точка центроида, несущая свой эллипс), -summary.json, -sessions.csv, -figure.svg. Каждый встраивает лицензию набора данных, URL лицензии и указание авторства, идентификатор метода kshana-interference-map/emitter-region/v1, каждый параметр, допущения и ограничения и несёт свою метку доказательств (уровень MODELLED, идентификаторы строк реестра; полоса под рисунком, строки # evidence в таблице, элемент evidence в JSON). Машинные файлы остаются на английском независимо от языка; текст рисунка и строки командной строки локализуются. Синтетический пример находится в examples/emitter-region/ (regenerate.sh).

Конфиденциальность и формулировки. Ни один идентификатор воздушного или морского судна не появляется в выходных данных; положение ни одного сообщения не рисуется; сеансу нужно не менее 5 различных воздушных или морских судов; счётчики ниже 5 равны null. Формулировки нейтральны: «оценка области излучателя», «область источника помех». Область — это не положение кого-либо, не вывод о причине и ничего не говорит о том, кто или что стоит за источником; расширенный тест формулировок проверяет выходные данные на слова о действующих лицах, намерениях и конфликте.

Ограничения. Достоверность зависит от модели. На синтетических данных (генератор, использующий ту же модель, с краями зоны досягаемости, неопределёнными на 15% для каждого воздушного судна, устойчивыми неисправностями оборудования, неизвестной мощностью и неизвестной высотой антенны) область 95% содержала истинный источник в долях сеансов 0,99 (геометрия воздушных судов) и 1,00 (геометрия судов), а область 50% — примерно 0,77: уровни консервативны за счёт показателя правдоподобия 0,3, а ошибка центроида сеанса имеет медиану 23 км (воздушные суда) и 12 км (суда). Калибровка проверяется только на синтетических данных; сравнение Jammertest было зарегистрировано заранее (emitter-region/PREREGISTRATION.md) и выполнено один раз: NOT ASSESSABLE для обоих источников (emitter-region/RESULTS.md). Один источник на сеанс; несколько близких источников выглядят как один. Региональная причина (отказ орбитальной группировки, космическая погода) даёт область, которая не является источником. ADS-B даёт мало сообщений вне загруженного воздушного пространства, и только около четверти точек трасс readsb несут NIC и NACp, поэтому разреженные районы дают широкие области или не дают их вовсе (31% сеансов с геометрией судов были unconstrained).

Метод ADS-B (kshana-interference-map/adsb/v2)#

Каждое сообщение ADS-B с борта несёт два самооцениваемых показателя навигационного качества: NIC (радиус содержания положения) и NACp (граница точности на уровне 95%, EPU). Когда установленный на борту приёмник теряет спутниковое решение или перестаёт ему доверять, оба показателя падают, поэтому они несут признак помех. Они падают и по другим причинам.

Предварительно зарегистрированные параметры (константы в коде, записываются в каждый файл; изменение означает новую версию метода):

Параметр Значение
Нижняя граница высоты барометрическая высота не менее 5000 футов
Сообщение с низкой точностью NACp не выше 6 (заявленная граница EPU составляет 556 м, 0,3 NM, или больше, либо неизвестна) или NIC не выше 5 (заявленная граница радиуса содержания составляет 1852 м, 1 NM, или больше, либо неизвестна)
Хорошее сообщение NACp не ниже 8 и NIC не ниже 7; оценивается по полям, которые есть во входных данных (по обоим, если присутствуют оба, иначе по имеющемуся)
Базовый уровень оборудования воздушное судно учитывается в ячейке, только если в тот же день у него есть не менее 5 хороших сообщений вне этой ячейки
Воздушные суда в выборке по ячейке не менее 3 сообщений в ячейке
Затронутые воздушные суда в ячейке не менее 50% его сообщений в ячейке имеют низкую точность
Минимальная выборка не менее 10 воздушных судов в выборке
Деградированная не менее 5 затронутых воздушных судов, доля затронутых среди воздушных судов в выборке не менее 0,30 и не менее чем на 0,15 выше медианной доли по ячейкам за день
День, искажённый общим фоном медианная доля по ячейкам (не менее чем по 5 ячейкам с полной выборкой) не менее 0,15: ни одна ячейка не объявляется деградированной
Нет фона менее 5 ячеек с полной выборкой, поэтому медиану оценить нельзя: ячейка, удовлетворяющая порогам, получает withheld_no_background, а не статус деградированной
Минимум для публикации не менее 5 различных наблюдавшихся воздушных судов

Эти значения были установлены по определениям кодов NIC и NACp и по правилу конфиденциальности до того, как были просмотрены какие-либо данные. Они не подбирались по реальным данным, а тесты используют только синтетические данные. Статусы ячеек: degraded, not_degraded, insufficient_sample (опубликована, но в выборке менее 10 воздушных судов), withheld_day_confounded и withheld_no_background.

Версия 2. Версия 1 объявляла ячейку деградированной уже при 3 затронутых воздушных судах, то есть меньше минимума публикации в 5 воздушных судов, и без уведомления пропускала проверку фона дня, если полностью в выборку попадало менее 5 ячеек. Версия 2 требует не менее 5 затронутых воздушных судов и воздерживается от вывода (withheld_no_background), когда фон оценить нельзя. Оба изменения ужесточают метод (ничто, не бывшее деградированным в версии 1, не является деградированным в версии 2); причина в том, что опубликованный вывод никогда не должен опираться на меньшее число воздушных судов, чем число, ниже которого счётчики скрываются. Файлы версии 1 не выпускались.

Почему поля точности падают по причинам, не связанным с помехами, и чем защищён метод в каждом случае:

Причина Защита
Старые транспондеры, которые никогда не сообщают NIC или NACp либо сообщают их с постоянным низким значением базовый уровень оборудования: воздушное судно, у которого в тот день нет хороших сообщений в других ячейках, не учитывается
Малая высота и операции на поверхности (маскирование рельефом, другие форматы положения) нижняя граница высоты; строки с пометкой ground исключаются
Положения MLAT или иные положения не от GNSS фильтр source_type, когда столбец присутствует
Одно воздушное судно, передающее много сообщений считаются различные воздушные суда, а не сообщения
Сбой орбитальной группировки или системы функционального дополнения либо событие космической погоды, снижающие точность на обширной территории ячейка должна превышать медианную долю по ячейкам за день с запасом, а день, у которого сама медиана высока, помечается как искажённый общим фоном, и ни одна ячейка не объявляется
Неисправность оборудования одного воздушного судна требуется доля многих различных воздушных судов при минимальной выборке
Мало воздушных судов в ячейке минимальная выборка; при наблюдении менее 5 ячейка не публикуется

Что остаётся: метод не может отличить помехи от региональной причины, которая локальна, а не широкомасштабна (локальный эффект геометрии спутников или сбой наземного функционального дополнения выглядел бы так же), и он консервативен в одну сторону: воздушное судно, которое весь день летает только внутри затронутого района, не имеет базового уровня и не учитывается, поэтому устойчивые обширные районы недооцениваются.

Метод AIS (kshana-interference-map/ais/v2)#

На каждое морское судно за каждые сутки — пять детекторов. Детектор срабатывает в ячейке, когда он помечает не менее 5 различных судов, составляющих не менее 20% судов, наблюдавшихся там; ячейка получает статус anomalous, если срабатывает любой детектор.

Детектор Правило (предварительно зарегистрировано)
on_land не менее 3 сообщений в ячейке дальше 2000 м вглубь суши от предоставленной береговой линии
circle окно из 20 последовательных сообщений (шаг 10, разрывы не более 30 мин), у которого средний радиус от центроида составляет от 500 м до 20 км, коэффициент вариации радиуса не более 0,15, одно направление движения с суммарным поворотом не менее 270 градусов (шаги не более 90 градусов, итоговый не менее 80% от суммарного) и средняя скорость не менее 2 уз
implausible_jump не менее 2 скачков, пришедших в ячейку: последовательные донесения с интервалом не менее 1000 м между позициями и не более 1 ч между донесениями, что подразумевает скорость более 70 kn (подразумеваемая скорость считается с интервалом между донесениями не менее 1 с, поэтому два донесения с одинаковой отметкой времени и позициями на расстоянии 1 км или более считаются скачком)
implausible_speed не менее 3 донесений о скорости выше 70 kn в ячейке
same_position не менее 5 различных судов, сообщающих одну и ту же позицию (с округлением до 1e-4 градуса, около 11 м) в одном и том же 10-минутном окне

Ячейки, где менее 5 различных судов, не публикуются; каждое число отмеченных судов ниже 5, включая ноль, указывается как null.

Версия 2. Версия 1 квалифицировала детектор по 3 отмеченным судам и опубликовала значения 3 и 4. Версия 2 повышает минимум отмеченных судов до 5, то есть до минимума для публикации, так что заключение anomalous никогда не опирается на меньшее число судов, чем то, ниже которого значения скрываются. Это ужесточает метод; ни один файл версии 1 не был выпущен.

Известные причины ложных срабатываний, которые правило доли и пороги уменьшают, но не устраняют: суда в реках, каналах и портах вблизи грубой береговой линии (для этого служит внутренний буфер в 2 км); два судна с одним идентификатором, что выглядит как скачки; транспондеры с плохим определением места или испытательное оборудование; а также лёжка в дрейфе лоцманских и поисковых судов, которая может напоминать окружности. Файл береговой линии масштаба 1:10 миллионов точен примерно до километра.

Детекторы скачков и скорости сравнивают донесения в пределах одного входного файла; трек, разделённый между файлами или днями, не склеивается.

Память. Входные данные читаются построчно и никогда не хранятся целиком. ADS-B хранит только счётчики по воздушным судам и ячейкам. AIS хранит каждое принятое донесение в компактной записи (около 40 байт) до оценки его дня, поэтому память растёт с числом донесений AIS во входных данных; очень большой вход AIS разбивайте по дням.

Подверженность маршрута#

kshana route-exposure --route route.geojson --map out/ [--map other/] \
    [--from 2026-03-01] [--to 2026-03-07] [--out report.json] [--json]

--route — это GeoJSON LineString (отдельный, внутри Feature или внутри FeatureCollection) или CSV из строк lat,lon. --map принимает файлы карт или каталоги с ними и может повторяться. Путевые точки соединяются прямыми линиями по широте и долготе, поэтому длинные участки следует уплотнять; участок идёт кратчайшим путём по долготе, так что участок, пересекающий антимеридиан (179,5 E — 179,5 W), пересекает его. Маршрут делится на отрезки около 250 м. Каждый отрезок измеряется между собственными конечными точками на сфере радиусом 6371,0088 км (тем же путём, который определяет, в какой ячейке он лежит) и относится к ячейке, содержащей его середину.

За каждый день и по каждому источнику отчёт даёт длину маршрута и четыре доли, сумма которых равна 1: share_degraded, share_not_degraded, share_unassessed (ячейка есть, но заключения по ней нет) и share_not_observed (ячейки нет на карте). Последние две читайте вместе с первой: маршрут с низкой долей деградации и высокой долей ненаблюдавшихся ячеек не показан как чистый. Строки ADS-B и AIS приводятся отдельно и никогда не объединяются. Каждая строка несёт лицензию и указание авторства карты.

Функции библиотеки#

Для вызывающих сторон, которые держат данные в памяти (привязки, сервер MCP, сборка для браузера), модуль kshana::interference_map::api предоставляет те же методы с текстом или байтами на входе и JSON на выходе, без обращения к файлам и сети:

Функция Вход Выход
adsb_maps_from_csv(csv, &DatasetSpec, cell_deg) текст CSV ADS-B Vec<DayMap>
adsb_maps_from_readsb_traces(&[&[u8]], &DatasetSpec, cell_deg) один срез байтов на каждый файл трассы readsb (gzip или простой) Vec<DayMap>
ais_maps_from_csv(csv, &DatasetSpec, cell_deg, Option<&str>) текст CSV AIS, необязательный текст GeoJSON суши Vec<DayMap>
route_exposure(route_text, &[&str], from, to) текст маршрута, тексты GeoJSON карт, необязательные границы YYYY-MM-DD JSON kshana-route-exposure/v1

У DayMap есть file_name (<source>-<dataset>-<date>.geojson), date и geojson (документ v1). DatasetSpec — это Preset("adsb-lol" | "noaa-marinecadastre" | "kystverket") или Custom { licence, licence_url, attribution }; DEFAULT_CELL_DEG равно 0,5. Ошибки имеют тип MapError. Правила для наборов данных (утверждённые пресеты, обязательный текст лицензии, соответствие вида) действуют точно так же, как в командной строке.

Внешняя валидация#

tests/interference_map_reference.rs сравнивает Kshana с независимыми реализациями на синтетических входных данных, с допусками, предварительно зарегистрированными в tests/fixtures/interference_map_ref/PREREGISTRATION.md: значения кодов NIC и NACp — с декодированием синтетических кадров ADS-B в pyModeS, значения «недоступно» AIS — с декодированием синтетических предложений AIVDM в pyais, назначение ячеек сетки — с shapely, длина маршрута по состояниям ячеек — с shapely и GeographicLib, а маскирование суши — с shapely и pyproj. Фикстуры создаются с помощью scripts/gen_interference_map_ref.py; для CI Python не нужен. Это проверяет, как читаются коды и значения полей, а также геометрию. Это не проверяет, что отмеченная ячейка действительно означает помехи.

Тесты#

Все тесты используют синтетические сгенерированные данные и не обращаются к сети: модульные тесты в src/interference_map/ и tests/interference_map_cli.rs для сквозной проверки команд, включая то, что ни один идентификатор не попадает в вывод и что отказы не оставляют файлов.