简介低地球轨道卫星网络仿真框架Hypatia是一套面向卫星通信领域的研究与工程仿真平台主要服务对象为无线网络研究人员、系统工程师以及相关专业高年级学生。该框架覆盖星座设计、轨道动力学、无线传播与信道编码、链路预算、网络协议仿真等关键环节支持用户灵活配置卫星数量、轨道高度、用户分布与流量模型能够系统评估覆盖范围、网络容量、时延和服务质量等核心指标。资源共312个文件压缩包总大小33.63MB其中以Python脚本92个为主并包含C/C核心模块、Markdown与文本文档、Shell工具脚本以及少量数据文件结构清晰易于定位和二次开发。目前已有449人学习或下载。通过研读源码与实际运行场景学习者可深入理解LEO卫星网络的仿真方法掌握Hypatia的扩展思路为太空互联网等前沿课题提供有效的实验支撑。1. 低地球轨道(LEO)卫星网络仿真框架一份能直接改参数的ns-3源码包做LEO星座仿真的人第一课往往是「别拿地面网络的思路套卫星」。低地球轨道(LEO)卫星网络仿真框架之所以是块硬骨头在于节点永远在动、链路永远在断、路由永远在变——你需要的不是一套拓扑脚本而是一整套把轨道动力学、星间激光链路、地面接入链路和转发决策粘在一起的机制。这份源码压缩包正是干这个用的它基于ns-3搭建以Hypatia仿真框架的组件为核心用一组NetDevice和Helper把从卫星节点、星历计算到激光星间链路和GSL地面链路的完整链路打通。适合正在做星座设计、链路预算或协议评估的研究生和工程师能让你跳过从零抠底层的阶段直接拿到一套能跑、能改、能出数的工程骨架。2. 源码包结构拆解核心器件、链路器件与辅助器的分层关系2.1 先看清文件清单哪些是主体哪些是螺丝拿到压缩包第一件事不是急着编译而是把这串文件名归类。我习惯按「节点层—链路层—辅助层」三堆来拆# 节点与时间基准 satellite.cc # 卫星节点实体封装轨道位置、节点ID julian-date.cc # 儒略日转换轨道计算的时间基准 constants-gen.cc # 星座常量生成轨道高度、倾角、面数等 # 链路与信道 point-to-point-laser-net-device.cc # 星间激光链路设备 point-to-point-laser-helper.cc # 激光链路安装器 gsl-net-device.cc # 地面站-卫星链路设备 gsl-channel.cc # 地面链路信道模型 # 拓扑与路由 topology-satellite-network.cc # 星座拓扑主入口 arbiter-single-forward-helper.cc # 单径转发仲裁器这套文件的分层逻辑很典型satellite.cc和julian-date.cc是地基topology-satellite-network.cc是施工图纸剩下的*-net-device.cc和*-helper.cc是水电管线。先编译地基再装链路最后跑拓扑顺序错了你就会面对一堆undefined reference。2.2 轨道时间基准julian-date和constants-gen的分工LEO仿真里最容易翻车的是时间系统。ns-3默认用Simulator::Now()给你相对时间但卫星在轨位置计算要的是绝对时间戳——儒略日。这两套时间如果不做映射卫星位置全乱。julian-date.cc干的事就是把Unix时间戳转成儒略日再配合constants-gen.cc生成轨道六根数。注意这两个文件是配套的// 从Unix时间戳转儒略日 double unixToJulian(double unixSeconds) { return unixSeconds / 86400.0 2440587.5; } // 用儒略日计算某时刻卫星的平近点角 double computeMeanAnomaly(double julianDate, double epoch, double meanMotion) { return std::fmod(meanMotion * (julianDate - epoch), 2 * M_PI); }unixToJulian里的2440587.5是Unix纪元对应的儒略日偏移量这是天文计算的标准常数。computeMeanAnomaly喂进去的是儒略日差乘上平均角速度meanMotion再取模得到当前时刻卫星在轨道上的角度位置。如果你要改场景时间起点动的是epoch参数而不是unixToJulian后者是通用换算。2.3 拓扑入口topology-satellite-network.cc怎么把节点串起来这个文件是整套框架的「总装车间」。我在TopologySatelliteNetwork类里看到的典型流程分成三步生成卫星节点-安装轨道模型-挂链路。贴一下核心结构class TopologySatelliteNetwork { public: TopologySatelliteNetwork(int numOrbits, int numSatellitesPerOrbit, double orbitHeightKm, double inclinationDeg); void BuildLeoSatelliteNetwork(NodeContainer satellites); void InstallInterSatelliteLinks(double linkRangeKm); private: int m_numOrbits; // 轨道面数量 int m_numSatellitesPerOrbit; // 每面卫星数 double m_orbitHeightKm; // 轨道高度 double m_inclinationDeg; // 轨道倾角 };构造函数里四个参数直接决定星座长什么样numOrbits * numSatellitesPerOrbit就是星座总节点数orbitHeightKm影响链路预算inclinationDeg影响覆盖纬度。InstallInterSatelliteLinks里会遍历所有卫星对判断距离是否小于linkRangeKm小于才建激光链路——这正是激光星间链路的经典建链判据。3. 星间激光链路与GSL接口两条链路的模型差异和安装方法3.1 PointToPointLaser与GSL的根本区别这套框架里同时存在两套通信链路你要是不分清它们各自服务什么后面统计延迟的时候会懵。星间激光链路point-to-point-laser-net-device解决的是「天上和天上」的通信卫星与卫星之间距离几十到几千公里用激光波长衰减模型是自由空间传播损耗叠加指向误差。地面链路gsl-net-device解决的是「天上和地下」的通信卫星与地面站之间的接入链路要考虑大气吸收、雨衰、仰角变化。这两套链路在ns-3里走完全不同的信道类型。激光链路用的是光子级别的接收判决模型它不该插到普通PointToPointChannel上否则你只是在跑光纤仿真而不是太空激光。GSL则要绑定地面站的经纬度坐标卫星过顶时链路才导通不是永久在线。3.2 安装激光链路的两种手段helper和手工配置框架给point-to-point-laser-helper.cc提供了安装器典型用法如下PointToPointLaserHelper laserHelper; laserHelper.SetAttribute(DataRate, StringValue(10Gbps)); laserHelper.SetAttribute(PhotonWavelengthNm, DoubleValue(1550)); laserHelper.SetAttribute(Enable, BooleanValue(true)); NetDeviceContainer islDevices; for (uint32_t i 0; i satelliteNodes.GetN(); i) { for (uint32_t j i 1; j satelliteNodes.GetN(); j) { double dist GetGreatCircleDistance(satellitePositions[i], satellitePositions[j]); if (dist 3000e3) { // 3000公里内才考虑建链 islDevices.Add(laserHelper.Install(satelliteNodes.Get(i), satelliteNodes.Get(j))); } } }这段代码里Enable必须显式设为true这个参数我在多个版本里见到它默认值不同最稳的做法是每回都写清楚。PhotonWavelengthNm设1550是标准C波段光通信波长链路预算按这个波长算衰减才有意义。Install返回的NetDeviceContainer会同时包含两端设备的指针。距离阈值3000e3不是拍脑袋——它取决于激光发射功率、望远镜口径和接收灵敏度你可以从链路方程反推但刚开始用这个值足够跑通仿真。3.3 GSL接口安装与动态连接问题GSL链路跟激光链路不一样它是「一片一片」出现的卫星在某一时刻只对当前可见的地面站建立连接。gsl-channel.cc里我看到的逻辑是每经过一个仿真步长就更新一次连接状态地面站能看到卫星仰角大于最小仰角阈值时才置为连通。void GslChannel::UpdateConnectivity(PtrSatellite sat, PtrGroundStation gs, double simulationTime) { double elevation CalculateElevation(sat-GetPositionAt(simulationTime), gs-GetPosition()); bool shouldConnect (elevation m_minElevationDeg); if (shouldConnect !m_connected) { m_connected true; m_connectedAt simulationTime; } else if (!shouldConnect m_connected) { m_connected false; m_disconnectedAt simulationTime; } }这段逻辑的价值在于告诉你一件事GSL链路是有生命周期的。如果你在统计时把所有流量都当成「随时可发」延迟会严重失真。更贴近实际的口径是只有m_connected true期间产生的流量才计为有效数据。m_minElevationDeg一般设10度设太低地面站天线会收到大量大气噪声。4. 单径仲裁转发arbiter-single-forward-helper的路由逻辑与配置4.1 为什么需要arbiterLEO拓扑里没有「永久的下一跳」传统地面路由协议默认邻居关系是稳定的但LEO星座的邻居表是分钟级变动的。Hypatia采用的方式是不做全网路由表的频繁重算而是让每个卫星节点维护一个「仲裁器」只负责决定「当前收到的包从哪个接口出去」。arbiter-single-forward-helper是最简的一种单径转发选一条最优出接口不走多径。换来的是低开销和确定性的转发顺序这在学术仿真里更容易出干净的数据。4.2 配置参数与调用方式先看它的安装接口ArbiterSingleForwardHelper arbiterHelper; arbiterHelper.SetAttribute(RoutingTableRefreshInterval, TimeValue(Seconds(10))); arbiterHelper.SetAttribute(LookAheadTime, TimeValue(MilliSeconds(500))); arbiterHelper.Install(satelliteNodes);RoutingTableRefreshInterval是路由表刷新周期。10秒只是参考值实际取决于星座规模——卫星间相对位置变化快的轨道高度比如500公里左右的极轨星座刷新间隔建议缩到5秒不然路由滞后会让转发走向死胡同。LookAheadTime是预判窗口仲裁器会在「当前时刻500毫秒」的位置上计算邻居关系因为激光链路从转动到锁定的机械延迟就约等于这个量级。4.3 转发决策到底怎么实现的arbiter-single-forward-helper内部的核心逻辑我拆给你看uint32_t ArbiterSingleForwardHelper::FindBestInterface(PtrNode node, const std::vectordouble costVector) { uint32_t bestInterface 0; double bestCost std::numeric_limitsdouble::max(); for (uint32_t i 0; i costVector.size(); i) { if (costVector[i] 0 costVector[i] bestCost) { bestInterface i; bestCost costVector[i]; } } return bestInterface; }注意这里有个隐蔽前提costVector[i] 0代表这个接口当前可用0或负值会被跳过。所以你在构造代价矩阵的时候一定要先把断链的接口代价标成-1而不是留空。我见过有人直接把距离当代价填进去结果把几万公里以外的不可达邻居算成了最优下一跳——因为距离大但为正仲裁器以为它可达。4.4 单径转发在LEO场景的表现上限单径的好处是简单、可复现但你要清楚它的代价上限当一颗卫星的邻居全部断链时它只能丢包。这在极区是常见事件——极轨星座到高纬度地区星间链路的几何关系会发生翻转传统「左右邻居跨轨邻居」的固定邻居表会暂时失效。Https如果是做吞吐量边界研究arbiter-single-forward-helper是你的基线如果是研究抗毁路由你得换多径版本或者加链路预测模块。5. 避坑记录编译、时间同步与链路真实的四个检查点5.1 加了激光链路代码流量却始终为零现象在拓扑文件里添加了PointToPointLaserHelper的调用编译通过仿真跑起来但FlowMonitor统计不到任何跨星流量。原因PointToPointLaserNetDevice的Enable属性在某些版本默认是false。物理上激光链路需要发射端主动开启激光器框架用这个默认值模拟「链路存在但不发光」的状态。你装了设备但不启用跟现实里望远镜对准了但没开激光一模一样。解决在Install之前显式加一行laserHelper.SetAttribute(Enable, BooleanValue(true));再跑一次。如果还不通下一步在仿真第1秒打印设备状态检查m_enabled字段是否为true。5.2 卫星位置不对所有链路距离计算全错现象跑起来后通过MobilityModel打印卫星坐标发现卫星位置在仿真刚开始几十秒后就漂移了链路时断时续。原因ns-3的调度时间是相对的Simulator::Now()从0开始计数而轨道传播器内部用的是儒略日绝对时间。我在2.2节强调过这个映射。典型错误是有人直接把Simulator::Now().GetSeconds()当儒略日差传入computeMeanAnomaly结果平近点角计算错了好几个弧度。解决在时间基准层做一次封装所有轨道计算函数只接收「自纪元起累计秒数」内部统一先转儒略日再算。我一般会在TopologySatelliteNetwork构造函数里记一个m_epochUnix之后任何位置计算都过一遍julianToUnix的逆变换杜绝混用。5.3 GSL链路的时延抖动大得离谱现象地面站到卫星的延迟曲线是锯齿状波动几百毫秒但理论上LEO到地面最多几十毫秒。原因GSL信道模型里既有传播延迟又叠加了信号跟踪和重捕获的随机延迟。gsl-channel.cc在低仰角时会给信道增加一个「重新指向延迟」这是为了模拟天线伺服系统的跟踪滞后。如果你的延迟分布统计包含这个误差项结果自然难看。解决先把CalculateElevation打印出来确认延迟尖峰是否都发生在仰角低于最小仰角阈值的时刻。如果是说明你统计里混入了未连通时段的数据过滤掉m_connected false的采样点再看曲线。5.4 仲裁器选了显然不可能到达的下一跳现象两个相距上万公里、中间没有其他卫星的节点仲裁器给出的下一跳是直连邻居但这两个节点之间不存在激光链路。原因FindBestInterface只检查代价为正没检查接口对端是否真正连通。代价矩阵在更新之前上一轮仿真步内残留了一个「曾经连通」的邻居信息这一轮链路已经断了但代价没来得及置负。解决在每次仲裁决策前强制跑一遍所有接口的IsLinkUp()把断链接口的代价置为-1再交给选择器。代价矩阵的更新顺序必须紧跟UpdateConnectivity两者放在同一个仿真时间点执行。6. 验证技巧把topology-satellite-network.cc改成你自己的星座场景6.1 参数化改造从「跑通」到「跑出你的星座」源码里的constants-gen.cc默认给出一套类Starlink参数——大约1100公里高度、53度倾角、72个轨道面。你直接跑通不会证明任何科学结论但把它改造成你自己的场景参数就是研究。我习惯把这四个值抽出来做成配置头文件// constellation-config.h #define CONSTELLATION_ORBITS 48 #define CONSTELLATION_SATS_PER_ORBIT 22 #define CONSTELLATION_HEIGHT_KM 1150.0 #define CONSTELLATION_INCLINATION_DEG 53.0然后把topology-satellite-network.cc构造调用改成引用这组宏TopologySatelliteNetwork network(CONSTELLATION_ORBITS, CONSTELLATION_SATS_PER_ORBIT, CONSTELLATION_HEIGHT_KM, CONSTELLATION_INCLINATION_DEG);改完之后不要立刻全跑先跑一分钟仿真验证总节点数——48 * 22应该是1056颗卫星。节点数对了再逐步加链路。6.2 验证链路预算的三个检查点链路装完别急着看吞吐量先做这三步验证能过滤掉八成配置问题检查ISL链路总数打印NetDeviceContainer::GetN()。对于每个轨道面内相邻卫星建链的场景链路数应该接近「每面卫星数 * 轨道面数 * 2」量级——具体取决于你的建链阈值。检查单跳延迟数量级用FlowMonitor统计两个相邻卫星间的传播延迟应该在1到10毫秒量级。激光链路近光速传播3000公里距离的延迟约10毫秒偏差超过一个数量级就要查信道类型。检查GSL链路切换频率打印一颗卫星在24小时内的m_connected翻转次数。近地轨道周期约100分钟一颗卫星对固定地面站的可见窗口约10到15分钟翻转次数应该跟这个量级对得上。6.3 端到端验证脚本的思路我在跑大场景前会先构造一个最小两跳场景做端到端验证一个地面站发送、一颗中继卫星转发、另一颗卫星落地。用FlowMonitor统计能否收包这一步过了再放大到整个星座。验证脚本的判据很简单——接收端的包计数必须大于零且每跳延迟在预期范围内。这套流程走完后你手里已经有一份能出数的LEO星座仿真框架了。从那以后我处理任何卫星仿真项目都强制走一遍「节点数校验→链路数校验→单跳延迟校验→再跑量」的流程翻车率降到很低。希望帮到你。本文还有配套的精品资源点击获取