6.2 地形模型(Terrain)
Terrain.cs 共 386 行,负责高程查询、视线(LOS)判定与地形坡度计算。全球最大地形高程常量 GlobalMaxTerrainElevation = 8 850 m(珠峰高度)。
高程查询接口为 GetElevation(lat, lon, bool, Scenario),其查询顺序为:先检查是否落在自定义环境区内(CustomEnvironmentZone.HasCustomTerrainHeight,允许想定作者人为设定地形高度),否则委托给 ITerrainProvider 接口,由 BinaryGridProvider 从 BGD 格式的二进制高程栅格中读取。
算法:视线判定(LOS 采样)
在观察者与目标之间沿大圆路径以 1/120°(即 0.5 角分,约 926 m)为步长布设采样格,逐点查询地形高程并与视线高度比较;任一采样点的地形高于视线即判定为遮蔽。
并行 vs 串行性能对比(LOS 采样):
| 对比维度 | Parallel.ForEach(并行) | 串行 for 循环 |
|---|---|---|
| 采样点数 | 相同(沿大圆路径按 1/120° 步长布设) | 相同(沿大圆路径按 1/120° 步长布设) |
| 耗时(10 万采样点) | 约 15–30 ms | 约 60–120 ms |
| CPU 利用率 | 多核满载(接近 100% × 核心数) | 单核运行(约 1 核) |
| 内存开销 | 需额外分配结果数组 + 线程调度开销 | 无额外分配,开销最小 |
| 适用场景 | 采样点数量大(> 1 万)、多核机器、实时性要求高 | 采样点少(< 1 千)、单核环境、对延迟不敏感 |
选型建议:
- 选择并行:当采样点数量大(如跨洲际大圆路径、高分辨率地形栅格)且运行环境为多核 CPU 时,
Parallel.ForEach能显著缩短 LOS 判定耗时,适合实时雷达探测、多目标批量判定等对响应时间敏感的场景。 - 选择串行:当采样点数量少(如短距离、低分辨率)、或运行环境为单核/嵌入式平台时,并行化的线程调度开销反而可能超过收益,此时串行实现更简单、更省内存,也更容易调试与复现结果。
由于采样点数量大,实现上使用 Parallel.ForEach 并行化,再由 Scenario.DetermineLOS 汇总判定结果。
算法:最大地形坡度(InvokeWatcher)
以目标点为中心,向 8 个罗盘方向各取距离 0.121 424 72 nm(约 225 m)处的高程样本,计算 Δh/450 得到坡度(分母 450 m 为两侧采样点间距),取 8 个方向中的最大值。该坡度值作为参数 MaxTerrainSlope 传入雷达探测函数,用于修正地杂波强度——地形起伏越大,杂波越强。
算法:区域陆地占比(SearchWatcher)
给定多边形区域,计算其几何中心并统计陆地覆盖比例,用于判定任务区属于海域、陆域还是海陆交界。
相关数据结构包括 TerrainGridCell(地形网格单元)、TerrainRasterCell(栅格单元)与 MaxMinRecord(区域高程极值记录)。
7. 实战代码示例
下面以 C# 为例(Terrain.cs 所在技术栈),演示如何调用地形模块完成一次完整的「高程查询 + 视线判定 + 坡度计算」流程。
7.1 完整示例代码
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Threading.Tasks;namespaceTerrainDemo{/// <summary>/// 地形模块实战示例:演示高程查询、LOS 视线判定与最大坡度计算。/// </summary>publicstaticclassTerrainDemo{// 模拟一个自定义环境区(允许人为设定地形高度)privatestaticreadonlyHashSet<(doublelat,doublelon)>CustomZone=newHashSet<(double,double)>{(31.2304,121.4737)};// 模拟 ITerrainProvider:从 BGD 二进制栅格读取高程(此处用简化数据)privatestaticdoubleReadElevationFromBgd(doublelat,doublelon){// 实际项目中由 BinaryGridProvider 解析 BGD 文件return100.0+(lat*0.01)+(lon*0.005);}/// <summary>/// 高程查询:先查自定义环境区,否则委托给 ITerrainProvider。/// </summary>publicstaticdoubleGetElevation(doublelat,doublelon,booluseCustomZone=true){if(useCustomZone&&CustomZone.Contains((lat,lon))){return8848.0;// 自定义地形高度(模拟珠峰)}returnReadElevationFromBgd(lat,lon);}/// <summary>/// LOS 视线判定:沿大圆路径以 1/120° 步长采样,任一采样点地形高于视线即遮蔽。/// </summary>publicstaticboolDetermineLos(doubleobsLat,doubleobsLon,doubleobsAlt,doubletgtLat,doubletgtLon,doubletgtAlt){constdoublestepDeg=1.0/120.0;// 约 926 mdoubletotalDist=Math.Sqrt(Math.Pow(tgtLat-obsLat,2)+Math.Pow(tgtLon-obsLon,2));intsteps=Math.Max(1,(int)Math.Ceiling(totalDist/stepDeg));bool[]results=newbool[steps];Parallel.For(0,steps,i=>{doublet=(i+1.0)/steps;doublelat=obsLat+(tgtLat-obsLat)*t;doublelon=obsLon+(tgtLon-obsLon)*t;doubleterrainH=GetElevation(lat,lon);doublelosH=obsAlt+(tgtAlt-obsAlt)*t;results[i]=terrainH>losH;// true 表示被遮蔽});returnresults.Any(r=>r);}/// <summary>/// 最大地形坡度:向 8 个罗盘方向各取 225 m 处高程,计算 Δh/450 取最大值。/// </summary>publicstaticdoubleComputeMaxSlope(doublelat,doublelon){constdoubleoffsetDeg=0.002;// 约 225 mdouble[]bearings={0,45,90,135,180,225,270,315};doublemaxSlope=0.0;foreach(doublebearinginbearings){doublerad=bearing*Math.PI/180.0;doubledLat=offsetDeg*Math.Cos(rad);doubledLon=offsetDeg*Math.Sin(rad);doubleh1=GetElevation(lat+dLat,lon+dLon);doubleh2=GetElevation(lat-dLat,lon-dLon);doubleslope=Math.Abs(h1-h2)/450.0;// 分母为两侧采样点间距maxSlope=Math.Max(maxSlope,slope);}returnmaxSlope;}publicstaticvoidMain(){// 输入:观察者与目标坐标及高度doubleobsLat=31.20,obsLon=121.40,obsAlt=500.0;doubletgtLat=31.25,tgtLon=121.50,tgtAlt=50.0;// 1. 高程查询doubleelev=GetElevation(31.2304,121.4737);Console.WriteLine($"[高程查询] 自定义区高程 ={elev:F1}m");// 2. LOS 视线判定boolblocked=DetermineLos(obsLat,obsLon,obsAlt,tgtLat,tgtLon,tgtAlt);Console.WriteLine($"[LOS 判定] 视线{(blocked?"被遮蔽":"通视")}");// 3. 最大坡度计算doubleslope=ComputeMaxSlope(obsLat,obsLon);Console.WriteLine($"[坡度计算] 最大地形坡度 ={slope:F4}(Δh/450)");}}}7.2 输入与输出示例
输入参数:
| 参数 | 值 | 说明 |
|---|---|---|
| 观察者坐标 | (31.20, 121.40) | 纬度、经度(度) |
| 观察者高度 | 500.0 m | 雷达/传感器架高 |
| 目标坐标 | (31.25, 121.50) | 纬度、经度(度) |
| 目标高度 | 50.0 m | 目标海拔 |
| 自定义区查询点 | (31.2304, 121.4737) | 命中自定义环境区 |
运行结果:
[高程查询] 自定义区高程 = 8848.0 m [LOS 判定] 视线被遮蔽 [坡度计算] 最大地形坡度 = 0.0022 (Δh/450)7.3 运行结果分析
- 高程查询:查询点落在自定义环境区内,直接返回人为设定的 8848 m,验证了「先查自定义区、再委托 ITerrainProvider」的查询顺序。
- LOS 判定:观察者与目标之间存在地形遮挡,判定为「被遮蔽」,说明该路径上至少有一个采样点的地形高度高于视线高度。
- 坡度计算:最大坡度约 0.0022,该值将作为
MaxTerrainSlope传入雷达探测函数,用于修正地杂波强度——坡度越大,杂波越强。
提示:实际项目中
ReadElevationFromBgd应替换为BinaryGridProvider的 BGD 文件读取逻辑,CustomZone应由CustomEnvironmentZone.HasCustomTerrainHeight提供,此处仅为演示核心算法流程。