干扰地图与航线暴露
kshana interference-map根据您提供的开放许可的跟踪文件,逐日构建一幅图景,显示飞机和船舶在何处报告了降级的导航数据。随后kshana route-exposure说明每一天计划航线有多大部分位于受影响的单元格之内。
它是什么:对过去几天已发布的位置报告是什么样子的描述。它不是什么:对任何接收机的测量、预测,或存在干扰的证明。降级或异常的单元格有多种可能原因(见下),而没有条目的单元格只是没有被观测到。
所有内容都在本地文件上运行。默认命令从不打开网络连接;唯一可选的下载辅助工具需要明确的标志。
两天的数据都是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 |
每个输出文件都嵌入许可、许可网址、所要求的署名以及来源的覆盖范围限制。对于条款禁止商业使用或禁止再分发衍生地图的来源,没有任何内置条目。
ODbL与Kshana各版本。由adsb.lol数据制成的逐单元格ADS-B文件属于衍生数据库,按ODbL 1.0发布;其licence字段会注明这一点。Kshana自己的代码仍为AGPL-3.0。写出此类文件的Kshana商业版本带有相同的声明,任何再次发布该文件的人都必须保留它。ADS-B和AIS的结果绝不合并在同一个文件中,因此ODbL不会附着到AIS图层上。
覆盖偏差。地图继承其来源的覆盖范围。adsb.lol依靠志愿者的接收机,因此开阔海域和覆盖稀疏的地区数据稀少。Kystverket的开放数据不含15 m以下的渔船和45 m以下的游艇,因此其地图反映的是较大的船舶。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等被排除) |
直接读取adsb.lol的readsb历史数据。每日档案是readsb轨迹文件的分卷tar。按发布方的说明解压其中一个(把各部分拼接起来,tar -xf -),然后把解压后的目录、单个trace_full_*.json文件或一个CSV交给Kshana:
kshana interference-map adsb extracted-day/ --dataset adsb-lol --out maps/
对于目录,会递归搜索其中的trace_full_*文件(其他文件,例如trace_recent_*,会被忽略,也不跟随符号链接)。每个文件是经gzip压缩的或普通的JSON,按其起始字节判断,因为readsb会在名为.json的文件中写入gzip;每个文件的解压大小上限为512 MiB。对每条轨迹条目,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(以参数列表而不是通过shell的方式)通过HTTPS下载一次,针对一个固定地址上的一个文件,该地址指向上游仓库的某一个提交。只有当下载内容的SHA-256等于代码中固定的值时才会保留;不一致则删除并失败。如果未安装curl,命令会如实说明,并给出地址和校验和,以便手动获取该文件。仓库中不附带任何内容。
输出#
每个来源每个UTC日一个GeoJSON文件,位于--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成员保存模式名、日期、网格、方法标识和每个参数、日级数据、数据许可与署名,以及一条声明。每个要素是一个单元格多边形,带有cell_i、cell_j、status、degraded和汇总计数。
网格是固定的:纬度和经度方向上边长为--cell-deg度(默认0.5)的方形单元格,从 (-90, -180) 起编号。东西向宽度随纬度缩小:按默认值,赤道处约55 km,60度处约28 km。纬度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在低于5时为null,而affected_share除非两者都至少为5,否则为null(占比会暴露被隐去的计数)。在AIS文件中,vessels_flagged的每一项在低于5时为null。
格式版本与字段#
格式已确定为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为true。航线暴露报告有自己的kshana-route-exposure/v1模式。
观测站:每日成果#
kshana interference-map daily把一个UTC日的ADS-B报告转换成观测站的三个文件(GeoJSON、PNG和一份事件报告JSON)以及一份哈希清单,而kshana interference-map sensitivity则在每次只改变一个预先登记参数的情况下重放该日。该命令强制执行发布规则:在该日结束至少24 h之后(--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干扰地图。读取上面列出的字段;忽略未知字段。一个文件只包含一个来源和一天。可以加载多个文件。
单元格。绘制每个要素的多边形。按status着色,并始终在图例中说明每种颜色的含义。共四种状态,任何一种都不得隐藏或合并:
| 状态 | 含义 | 来源依据 |
|---|---|---|
| 降级(AIS则为异常) | 当天报告低精度的飞机占比很高,或船舶表现出异常 | degraded为true |
| 无异常 | 观测到足够多的飞机或船舶,且未被标记 | status为not_degraded或not_anomalous |
| 未评估 | 有观测,但抽样的飞机不足,或当天受到混杂影响,或无法估计当天的背景 | status为insufficient_sample、withheld_day_confounded或withheld_no_background |
| 未观测到 | 飞机或船舶太少,因此该单元格未发布 | 该单元格没有要素 |
“未观测到”即没有要素:以中性方式绘制(不填充,或用斜线填充),绝不使用“无异常”的颜色。使用不单靠红绿对比的配色方案。悬停或选中时的单元格详情,按properties原样显示其中的计数,并把每个null计数显示为“已隐去(少于5)”,绝不显示为零。当grid.is_preregistered为false时,查看器还应说明该文件所用的单元格大小超出了预先登记的方法。
图层。ADS-B和AIS是各自独立的图层,各有自己的开关、图例和许可说明行。绝不把它们合并为同一个颜色字段或同一个计数,绝不把它们绘制成一个合并的单元格,也绝不把它们的单元格相加。不同的日期是不同的文件:一次显示一天,配以日期选择器,而不是把多天叠加。
许可与署名。对每个可见的图层,在地图上以及任何导出或截图中,原文显示data.attribution文本、带有指向data.licence_url链接的data.licence,以及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(台账(ledger)行VM-bc70cb57):其物理是教科书式的无线电视距,区域的可信度以模型为条件,唯一的外部检验是emitter-region/PREREGISTRATION.md中预先登记的Jammertest比较。在公开观测站发布发射源区域是一项单独的所有者决定,目前为关闭:观测站不读取该模块,并有一项测试固定了这一点。
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定位解;见下)。任何与预先登记的值不同的选项,都会被宣告为“not the pre-registered method”。文件绝不会被覆盖。
模型。报告被归入单元(一架飞机或一艘船在一个2分钟区间内),每个单元要么受影响(ADS-B:按该地图自身的规则,其至少一半的报告具有低NIC或NACp;AIS:速度或跳变不合理,或位置在海岸线以内的陆地上),要么不受影响。一架飞机或一艘船只有在输入中至少有5条良好报告时才计入(与地图相同的设备基线保护)。受影响的单元被归并为时段:间隔超过30分钟就在时间上分开,而在一个时间簇内,当一个发射源可能同时触及两个单元时,二者归为一组。对于一个时段,网格上的每个候选位置都获得一个似然:
- 视距。高度为
h的飞机与天线位于h_e的发射源,在4/3地球半径下,相互可见的距离可达4.122 (sqrt h + sqrt h_e)km。h_e在2至100 m之间作边缘化(8个对数等间隔的值)。 - 功率。发射源的功率未知,因此以从12 km到426 km的有效距离(步进因子1.25)或“仅视距”来限定可达范围,并以相等权重作边缘化。可达边缘是柔性的:一个单元被计为在可达范围之内时,其权重在可达范围两侧各15%的范围内线性下降(至少为四分之三个网格单元),因为地形、天线方向图和路径的已知程度都达不到比这更好。
- 受影响与未受影响。一架飞机或一艘船以概率0.6为易感(接收设备、天线和信号路径各不相同),易感者在可达范围之内的一个单元以概率0.9受影响,任何其他单元以背景概率0.03受影响(其他原因)。易感性是飞机的一种潜在属性,按每架飞机求和消去,持续性设备故障也是如此(概率0.03:该飞机的所有单元无论在何处都受影响),这样可避免有故障的应答机把区域拖偏。因此,一架受影响的飞机把后验拉向能够触及它的位置,而一架具有视距的未受影响飞机则最多按一架飞机的分量把后验推离本应触及它的位置,无论它发送了多少报告;而没有视距的未受影响飞机则不说明任何问题。
- 不含地形。地形、波导传播和衍射均未建模(地形只会去除视距)。
- 回火(tempering)。对数似然乘以0.3(幂后验)。该模型的证据比似然所假设的独立性更相关,其边缘也不那么锐利;在带有这些缺陷的合成数据上,未回火的区域包含真实来源的比例远低于其所述比率;0.3仅在合成数据上选定(推导见
docs/emitter-region/PREREGISTRATION.md第8节)。
后验给出所述水平下的最高密度区域(嵌套;必要时为带孔多边形)、按后验加权的质心及其矩椭圆(在高斯假设下按95%缩放;经校准的水平是多边形,而不是椭圆)。网格在整个区域内较粗(约5至10 km),每个含有质量的粗单元都按4乘4细分。覆盖偏差通过以报告存在之处为条件来处理:在飞机很少的地方区域较宽;如果一个时段的95%区域占满其搜索范围的80%以上,则报告为unconstrained,且不给出质心。
时段与分布。每个已发布的时段都有起始、结束、持续时间、包含受影响单元且至少有5架不同飞机或船舶的0.5度单元的面积、不同的受影响数和被观测数,以及反证(对质心有视距的不同未受影响数;低于5时予以隐去)。不同的受影响飞机或船舶少于5的时段会被隐去并计数。分布按每个UTC小时给出不同的受影响飞机数(低于5时为null)和时段的分钟数,以及每个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上的Gauss-Newton求解,其协方差为geolocation::tdoa_crlb_on_sphere,该高斯分布被乘入后验。Cramer-Rao界是对一个模型的下界,不是实测精度。
输出(emitter-<source>-<dataset>-<day>):.geojson(每个水平和每个时段的MultiPolygon区域、椭圆多边形、带有其椭圆的质心点)、-summary.json、-sessions.csv、-figure.svg。每一项都嵌入数据集许可、许可URL和署名、方法标识kshana-interference-map/emitter-region/v1、每个参数、假设和局限,并带有其证据标签(等级 MODELLED,台账行标识;图下的色条、表中的# evidence行、JSON中的evidence成员)。无论使用何种语言,机器文件均为英文;图中文字和命令行各行会本地化。合成示例见examples/emitter-region/(regenerate.sh)。
隐私与措辞。任何输出中都不出现飞机或船舶标识;不绘制报告位置;一个时段至少需要5架不同的飞机或船舶;低于5的计数为null。措辞保持中性:“估计发射源区域”“干扰源区域”。区域不是任何人的位置,不是对原因的认定,也不说明某个来源背后是谁或是什么;扩展的措辞测试会检查输出中是否有涉及行为者、意图和冲突的词语。
局限。可信度以模型为条件。在合成数据上(生成器与模型共用同一模型,每架飞机的可达边缘不确定度为15%,另有持续性设备故障、未知功率和未知天线高度),95%区域包含真实来源的时段比例,飞机几何下为0.99,船舶几何下为1.00,50%区域约为0.77:这些水平是保守的,原因在于0.3的似然指数,一个时段的质心误差中位数为23 km(飞机)和12 km(船舶)。校准仅在合成数据上核对;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 ft |
| 低精度报告 | NACp至多为6(所报告的EPU界为556 m、0.3 NM或更大,或未知),或NIC至多为5(所报告的包含半径界为1852 m、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 m处 |
circle |
一个由20条连续报告构成的窗口(步幅10,间隔至多30 min),其到质心的平均半径为500 m至20 km,半径变异系数至多0.15,单一行进方向,总转角至少270度(每步至多90度,净转角至少为总转角的80%),且平均速度至少2 kn |
implausible_jump |
在该单元格中至少有2次跳变到达:连续报告相距至少1000 m、时间间隔至多1 h,隐含速度超过70 kn(隐含速度的计算在报告之间至少取1 s,因此时间戳相同、位置相距1 km或更远的两条报告也算一次跳变) |
implausible_speed |
该单元格中至少有3个报告的速度高于70 kn |
same_position |
至少5艘不同的船舶在同一个10分钟窗口内报告相同的位置(舍入到1e-4度,约11 m) |
不同船舶少于5艘的单元格不予发布;低于5的每个被标记计数,包括零,都以null报告。
第2版。第1版在被标记的船舶达到3艘时就认定检测器达标,并发布过3和4的计数。第2版把被标记船舶的下限提高到5,即发布下限,因此anomalous的判定绝不依据少于计数被隐去所对应下限的船舶数量。这使方法更严格,且第1版的文件从未发布。
已知的误报原因,占比规则和阈值可减少但不能消除它们:位于河流、运河和港口中、靠近粗略海岸线的船舶(2 km的内陆缓冲带正是为此而设);两艘船舶共用一个标识符,看起来像跳变;定位不良的应答机或测试设备;以及引航船和搜救船的徘徊,它们可能类似圆圈。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中的),或由lat,lon行组成的CSV。--map接受地图文件或其目录,且可重复使用。航点之间以纬度和经度中的直线相连,因此长航段需要加密;航段在经度上走较短的一侧,所以跨越反子午线的航段(从179.5 E到179.5 W)确实会跨越它。航线被分成约250 m的片段。每个片段在其自己的两个端点之间,按半径为6371.0088 km的球体测量(与决定其所在单元格的路径相同),并归入包含其中点的单元格。
对每一天和每个来源,报告给出航线长度和四个总和为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) |
ADS-B CSV文本 | 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>) |
AIS CSV文本,可选的陆地GeoJSON文本 | Vec<DayMap> |
route_exposure(route_text, &[&str], from, to) |
航线文本、地图GeoJSON文本,可选的YYYY-MM-DD范围 |
kshana-route-exposure/v1 JSON |
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代码的含义对照pyModeS对合成ADS-B帧的解码,AIS的不可用值对照pyais对合成AIVDM语句的解码,网格单元格归属对照shapely,每种单元格状态下的航线长度对照shapely和GeographicLib,内陆屏蔽对照shapely和pyproj。测试夹具由scripts/gen_interference_map_ref.py生成;CI不需要Python。这验证的是代码和字段值的读取方式以及几何计算。它并不验证被标记的单元格就是干扰。
测试#
所有测试都使用合成生成的数据,不使用网络:src/interference_map/中有单元测试,tests/interference_map_cli.rs则端到端测试各命令,包括没有任何标识符进入输出,以及拒绝执行时不留下任何文件。