GNSS 基础篇

3 接收机软件基础

如果说硬件是接收机的"躯体",那软件就是它的"灵魂"——硬件决定了"能不能做",软件决定了"做得好不好"。

在正式开始前,请大家思考几个问题:

1,接收机里的软件,到底在干什么?是像手机 App 那样跑操作系统,还是像单片机那样跑裸机?为什么不能统一一种模式?

2,为什么有的接收机用 C 语言写,有的用 C++,还有的用 Python?编程语言对定位性能影响大吗?

3,接收机软件动辄几万行甚至几十万行代码,是怎么组织起来的?为什么不能一个 main 函数写到底?

4,都说"软件定义一切",那 GNSS 接收机能不能纯靠软件实现?硬件是不是越来越不重要了?

接下来我们一层层剥开接收机软件的"洋葱",看看里面的世界。

3.1 操作系统与编程语言选型

3.1.1 裸机 vs RTOS vs Linux:三级演进

GNSS 接收机的软件运行环境,大致经历了三个阶段,对应三种不同的操作系统选型:

第一阶段:裸机(Bare Metal)—— 没有操作系统
程序直接跑在处理器上,没有操作系统,没有任务调度,没有内存管理。
一个 main 函数 + 几个中断,就是全部。

第二阶段:RTOS(实时操作系统)—— 轻量级多任务
跑一个小型实时操作系统,比如 FreeRTOS、uC/OS、VxWorks 等。
有任务调度、信号量、消息队列,但内核很小,通常只有几十 KB。

第三阶段:嵌入式 Linux —— 全功能操作系统
跑完整的 Linux 系统(通常是裁剪过的嵌入式 Linux)。
有完整的进程管理、文件系统、网络协议栈、驱动框架。

为什么会有这样的演进?因为接收机的功能越来越复杂——从单纯的定位,到 RTK、INS 融合、多传感器融合、网络通信、图形界面……
功能简单的时候,裸机够用;功能复杂了,就需要 RTOS 来管理多任务;功能再复杂,RTOS 也扛不住了,就得上 Linux。

三种方案的对比:

方案 典型代表 内核大小 实时性 功能丰富度 开发难度 适用场景
裸机 无OS 0 最高 最低 简单接收机、低成本模块
RTOS FreeRTOS/VxWorks 几十KB 中端接收机、专业模块
嵌入式Linux Yocto/Buildroot 几MB~几十MB 较低 最高 高端接收机、智能终端

这里有个反直觉的点:不是越高级的系统就越好
对于一个只需要输出 NMEA 语句的简单接收机,跑 Linux 是杀鸡用牛刀——增加了成本、功耗、复杂度,却没有带来任何好处。
而对于一个要跑 RTK、要接网络、要跑 Python 脚本的智能接收机,裸机根本做不了。
选型的核心原则是:够用就好,按需选择

3.1.2 不同层级接收机的 OS 选择实践

消费级模块(手机/穿戴/IoT):

  • 通常是裸机或轻量 RTOS
  • 处理器是集成在 SoC 里的小核 MCU
  • 资源极其有限(Flash 几百 KB,RAM 几十 KB)
  • 追求极致的低功耗和低成本

专业级板卡(车载/测绘/无人机):

  • 通常是 RTOS(FreeRTOS 居多)
  • 处理器是独立的 MCU 或 ARM Cortex-M/R 系列
  • 资源中等(Flash 几 MB,RAM 几百 KB~几 MB)
  • 追求实时性和可靠性

高端接收机整机(基准站/高精度):

  • 通常是嵌入式 Linux
  • 处理器是 ARM Cortex-A 系列(类似手机处理器,但性能低一些)
  • 资源充裕(Flash 几百 MB~几 GB,RAM 几十 MB~几 GB)
  • 追求功能丰富和可扩展性

这跟硬件的分层是对应的——越高端的硬件,配越复杂的软件。
就像汽车:代步小车配手动挡就够了,跑车要配双离合,重型卡车要配 AMT。

3.1.3 编程语言选型:C 为什么是王者?

GNSS 接收机的软件,用什么语言写?
答案是:核心算法几乎全是 C,外围工具什么都有

语言 用在哪里 为什么用它 占比(估算)
C 核心算法、驱动、底层 性能高、可控性强、生态成熟 70~80%
C++ 部分高层模块、RTK算法 面向对象、抽象能力强 10~20%
Python 测试脚本、工具链、上位机 开发快、生态丰富 5~10%
MATLAB 算法验证、仿真 数学库强大、可视化好 算法验证阶段
汇编 极少数关键路径 极致性能 <1%

为什么 C 语言在 GNSS 领域是绝对王者?

原因一:性能可控
GNSS 信号处理是计算密集型任务——捕获、跟踪、相关运算,每一个都要消耗大量 CPU 周期。
C 语言可以精确控制每一条指令的执行时间,可以精确控制内存布局,可以手动优化热点代码。
在资源有限的嵌入式平台上,每一毫秒、每一字节都要精打细算——这是 Python、Java 这类语言做不到的。

原因二:确定性
GNSS信号的捕获、跟踪、PVT解算甚至RTK,实时性要求非常高,实时系统要求"必须在规定时间内完成"。
C 语言没有垃圾回收,没有 JIT 编译,没有隐藏的运行时开销——执行时间是确定的。
你可以精确地算出一段代码要跑多少个时钟周期,这对于实时系统至关重要。

原因三:生态成熟
嵌入式领域的 C 语言生态是最成熟的——所有 MCU 都有 C 的编译器和库,所有芯片厂商都提供 C 的 SDK。
从 8 位 51 单片机到 64 位 ARM,C 语言通吃。

原因四:历史惯性
GNSS 接收机的代码库,很多都是从几十年前传下来的。
老代码是 C 写的,新代码继续用 C 写——重写的成本太高,而且没必要。

那 C++ 呢?
C++ 在部分高端接收机里也有使用,主要是 RTK 算法、多传感器融合这些比较复杂的模块——因为这些模块逻辑复杂,用面向对象的方式组织代码更清晰。
但 C++ 在嵌入式领域有个问题:它的"特性"太多了,一不小心就会引入额外开销(虚函数、异常、RTTI 等)。
所以通常是"C with classes"——只用 C++ 的一小部分特性,把它当"更好的 C"来用。

Python 和 MATLAB 呢?
它们几乎不会跑在接收机本体上,而是用在 PC 端的测试、验证、分析工具上。
Python 写脚本快,MATLAB 做仿真方便——但它们跑在接收机上,性能不够。

3.1.4 软件定义接收机(SDR):纯软件可行吗?

说到软件,不得不提一个概念:软件定义接收机(Software Defined Radio,SDR)。
传统接收机是"射频 + 基带硬件 + 软件",很多信号处理工作由硬件完成;
SDR 的思路是:ADC 尽量靠近天线,把信号尽早数字化,然后所有的处理(下变频、滤波、解调、相关运算)都用软件来做。

SDR 的好处:

  • 极度灵活:支持什么频段、什么信号,全靠软件改
  • 升级方便:新的信号体制,不用换硬件,升个级就行
  • 研发快:算法迭代速度快,不用等芯片流片

SDR 的问题:

  • 功耗大:通用 CPU 做信号处理,功耗是专用硬件的几十上百倍
  • 成本高:高性能 ADC + 高性能处理器,价格不菲
  • 体积大:没法做到消费级那么小

所以目前 SDR 主要用在:

  • 科研院所、或者企业内研发和验证阶段
  • 军用、航天等不计较功耗成本的领域
  • 业余爱好者的玩票

消费级和专业级接收机,主流还是"硬件加速 + 软件控制"的方案——关键的计算密集型任务(相关运算、FFT 等)用硬件做,灵活的控制和算法用软件做。
这是性能、功耗、成本、灵活性的最佳平衡点。

纯软件定义的 GNSS 接收机,未来会不会普及?
大概率不会完全替代传统方案——因为物理规律在那里,专用硬件的效率优势是通用处理器永远追不上的。
但 SDR 会在特定领域继续发展,作为传统方案的补充。

3.2 软件架构设计蓝图

几万行甚至几十万行的接收机代码,不是随便堆起来的——它有清晰的层次和结构。
好的软件架构,就像一栋设计精良的大楼:地基稳固、分层清晰、功能分区合理、人在里面走动顺畅。

3.2.1 分层架构:从底层到上层

典型的 GNSS 接收机软件,采用分层架构。从下往上,通常分为 7 层:

第 1 层:硬件驱动层(Driver Layer)
最底层,直接操作硬件寄存器。
包括:UART 驱动、SPI 驱动、I2C 驱动、GPIO 驱动、定时器驱动、中断管理等。
这一层的代码,跟具体的芯片型号强相关——换个 MCU,这层几乎要重写。

第 2 层:板级支持包(BSP Layer)
在驱动之上,封装了板级的硬件差异。
包括:板级初始化、时钟配置、电源管理、外设映射等。
比如,同样是 SPI 接口,在这块板子上接的是基带芯片,在那块板子上接的是 Flash——BSP 层把这些差异封装起来,上层不用关心。

第 3 层:基带接口层(Baseband Interface Layer)
跟基带芯片/IP 打交道的一层。
包括:基带配置、通道控制、相关器操作、中断处理、数据读取等。
这一层是"软件和硬件的桥梁"——上层软件通过这一层来控制基带硬件,读取基带输出的数据。
如果换了基带芯片,这一层要重写,但上面各层基本不用动。

第 4 层:信号处理层(Signal Processing Layer)
核心信号处理算法所在。
包括:捕获算法、跟踪算法、位同步、帧同步、电文解调等。
这一层的输入是基带输出的原始数据(相关结果、IQ 采样等),输出是伪距、载波相位、星历等"测量值"。
这一层是接收机技术含量最高的部分之一——算法的好坏,直接决定了灵敏度、精度、抗干扰能力。

第 5 层:PVT 解算层(PVT Layer)
定位解算算法所在。
包括:伪距定位、最小二乘、卡尔曼滤波、误差模型(电离层、对流层、相对论等)、卫星选择、完好性监测等。
这一层的输入是各颗卫星的伪距、载波相位、星历,输出是位置、速度、时间(PVT)。

第 6 层:应用服务层(Application Service Layer)
各种应用功能和协议。
包括:NMEA 输出、RTCM 编码/解码、配置管理、日志记录、网络通信、文件系统、AGNSS 辅助等。
这一层是"面向用户的功能"——用户能看到、能用到的功能,大多在这一层。

第 7 层:用户接口层(User Interface Layer)
最上层,跟用户交互的部分。
包括:命令行接口(CLI)、配置文件解析、Web 界面(如果有)、显示屏驱动(如果有)等。
用户通过这一层来配置接收机、查看状态、获取数据。

为什么要分这么多层?
核心原则是:高内聚,低耦合

  • 每一层只关心自己的事情,不关心其他层的内部实现
  • 层与层之间通过定义好的接口交互
  • 换某一层的实现,不影响其他层

比如:

  • 换 MCU → 只改驱动层和 BSP 层,上面全不动
  • 换基带芯片 → 只改基带接口层,上面全不动
  • 升级跟踪算法 → 只改信号处理层,下面全不动
  • 加新功能(比如 RTK)→ 在应用服务层加,下面全不动

这就是分层架构的价值——它让复杂的软件变得可管理、可维护、可扩展。

3.2.2 模块化设计:一个模块只做一件事

分层是"纵向"的划分,模块化是"横向"的划分。
每一层里面,又分成多个模块——每个模块负责一个独立的功能。

信号处理层的典型模块:

模块 职责 输入 输出
捕获模块 搜索卫星信号,找到码相位和载波频率 原始IQ数据/相关结果 捕获结果(码相位、载波频率、卫星号)
跟踪模块 持续跟踪卫星信号,保持同步 捕获结果 伪距、载波相位、载噪比
位同步模块 找到数据比特的边界 跟踪输出的相关结果 比特同步标志
帧同步模块 找到电文帧的起始位置 比特流 帧同步标志
电文解码模块 解调出星历、历书等数据 帧数据 星历、历书、时钟参数
测量值预处理 对伪距、载波相位进行预处理 原始测量值 平滑后的测量值

PVT 层的典型模块:

模块 职责
星历计算模块 根据星历参数计算卫星位置、速度、钟差
误差修正模块 计算电离层、对流层、相对论等误差修正量
卫星选择模块 选择参与定位的卫星,剔除异常卫星
定位解算模块 最小二乘 / 卡尔曼滤波解算 PVT
完好性监测模块 RAIM、FDE 等完好性算法

模块化设计的好处:

  • 可理解:每个模块功能单一,容易理解和维护
  • 可测试:每个模块可以独立测试,容易定位问题
  • 可复用:好的模块可以在不同项目中复用
  • 可并行:不同模块可以由不同的人并行开发

模块化的反面是"大泥球"(Big Ball of Mud)——所有代码搅在一起,你中有我、我中有你,改一处动全身。
这种代码,短期看写得快,长期看是灾难——每加一个新功能,都要冒着把旧功能搞坏的风险。

3.2.3 数据流与控制流

理解了分层和模块,再来看"数据是怎么流动的"。

GNSS 接收机的数据流,大致是这样的:

天线 → 射频 → ADC → 捕获模块 → 跟踪通道 → 测量值提取(伪距、载波相位等) → PVT解算 → 输出接口

这是一条"主数据通路"——数据从左往右流,每经过一个环节就被加工一次,最终变成定位结果。

除了数据流,还有控制流:

  • 配置命令 → 命令解析 → 模块配置 → 生效
  • 状态查询 → 状态收集 → 响应输出
  • 错误处理 → 错误检测 → 错误恢复

控制流是"从上往下"的——用户的命令、系统的控制,从上层往下传,控制底层的行为。

数据流和控制流,是软件架构的两条主线。
好的架构,两条线都清晰、顺畅、没有交叉混乱。

3.2.4 状态机设计:接收机的" moods "

接收机不是一直处于"定位中"的状态——它有很多种状态,不同状态下做不同的事情。

典型的接收机状态机:

状态 描述 典型行为
初始化 上电启动 硬件初始化、软件初始化、配置加载
冷启动 无任何先验信息 全频域全码域搜索捕获
温启动 有历书和大概时间 基于历书预测卫星,缩小搜索范围
热启动 有星历和精确时间 直接捕获已知卫星,快速定位
捕获中 正在搜索卫星 捕获算法运行
跟踪中 已锁定卫星信号 跟踪环运行,持续输出观测量
定位中 已有足够卫星,正在解算 PVT 解算,输出位置
导航中 稳定定位,持续输出 持续跟踪、解算、输出
失锁 信号丢失,跟踪中断 重新捕获
故障 硬件或软件异常 错误处理、尝试恢复

状态机设计的好坏,直接影响接收机的用户体验。
比如:

  • 冷启动时间长不长?
  • 信号短暂遮挡后,恢复快不快?
  • 遇到干扰,会不会直接崩掉?

这些用户能感知到的"好不好用",很大程度上取决于状态机设计得好不好。

好的状态机,应该是:

  • 状态定义清晰,没有重叠和遗漏
  • 状态转移条件明确,没有模糊地带
  • 异常状态有处理,不会卡死
  • 能从错误中恢复,而不是一错到底

3.3 软件开发全程指南

讲完了架构,我们再来看"怎么开发"。
接收机软件开发,跟普通的嵌入式软件开发有什么不同?有哪些特殊的坑?

3.3.1 开发流程:从需求到发布

一个典型的接收机软件开发项目,流程大致是这样的:

第 1 步:需求分析

  • 明确功能需求:支持哪些系统?哪些频点?精度要求?功耗要求?
  • 明确性能指标:捕获时间?跟踪灵敏度?定位精度?
  • 明确接口要求:什么接口?什么协议?输出格式?

第 2 步:架构设计

  • 确定分层架构
  • 划分模块
  • 定义模块间接口
  • 设计状态机

第 3 步:算法设计与仿真

  • 在 MATLAB/Python 中设计和验证算法
  • 用仿真数据测试算法的性能
  • 优化算法参数
  • 这一步非常关键——算法在仿真中跑不通,别指望在板子上能跑通

第 4 步:编码实现

  • 按模块分工编码
  • 遵循编码规范
  • 写单元测试

第 5 步:单元测试

  • 每个模块独立测试
  • 测试正常功能和异常情况
  • 覆盖率尽量高

第 6 步:集成测试

  • 把模块拼起来,测试整体功能
  • 测试模块间的接口和交互
  • 测试状态转移

第 7 步:系统测试

  • 在真实硬件上测试
  • 用信号源、模拟器测试性能指标
  • 外场实测

第 8 步:性能优化

  • 找出性能瓶颈
  • 优化热点代码
  • 优化内存使用
  • 优化功耗

第 9 步:发布与维护

  • 版本发布
  • Bug 修复
  • 功能迭代

这个流程跟普通软件开发差不多,但有几个 GNSS 特有的难点:

难点一:算法验证难
GNSS 算法的输入是"信号",输出是"位置"——你怎么知道算法对不对?
需要信号源、模拟器、参考接收机等专业设备。
而且很多边界情况(弱信号、强干扰、多径)很难复现。

难点二:调试难
信号处理的中间过程是看不见摸不着的——你不能像调试 UI 那样"看到"信号长什么样。
需要各种专业工具:数据采集回放、频谱仪、逻辑分析仪、示波器、调试接口……
很多时候,你只能通过"输出结果对不对"来反推"中间过程对不对"。

难点三:性能优化难
接收机是实时系统——必须在规定时间内完成计算,否则数据就丢了。
而且资源通常很紧张:CPU 算力有限、RAM 有限、Flash 有限、功耗有限。
在各种约束下把性能做到最优,是个技术活。

3.3.2 调试技巧:怎么调试一个"看不见"的问题

接收机软件开发中最头疼的事情之一就是调试——很多问题,你既看不到现象,也很难复现。

常见的调试手段:

手段一:日志(Logging)
在关键位置打日志,输出中间变量和状态。
这是最基本、也是最常用的方法。
但要注意:

  • 日志不能打太多——太多会影响性能,还可能改变时序(“海森堡效应”:你一观测,现象就消失了)
  • 日志不能打太少——太少定位不了问题
  • 日志要有分级:ERROR、WARNING、INFO、DEBUG,方便过滤

手段二:数据导出(Data Export)
把原始数据(IQ 采样、相关结果、测量值)导出到文件,然后在 PC 上用 MATLAB/Python 分析。
这是信号处理调试的"神器"——你可以把问题场景的数据录下来,慢慢分析。
很多时候,问题在板子上百思不得其解,数据导出来一看图,马上就明白了。

手段三:仿真对比(Simulation Comparison)
同样的输入数据,在 MATLAB 仿真里跑一遍,在目标板上跑一遍,对比结果。
如果不一样,说明实现有 bug;如果一样但结果不对,说明算法本身有问题。
这是定位"算法 bug"和"实现 bug"的有效方法。

手段四:JTAG/SWD 调试
用调试器连接芯片,单步执行、设置断点、查看寄存器和内存。
这是底层驱动、硬件相关问题的调试利器。
但对于信号处理算法,单步调试的意义不大——因为数据量太大,单步看到天荒地老也看不完。

手段五:性能剖析(Profiling)
找出哪些函数耗时最多,哪些是性能瓶颈。
工具有:

  • 简单的:GPIO 翻转 + 示波器测量
  • 专业的:性能分析工具(如 perf、SystemView)
    性能优化的第一步,永远是"先找到瓶颈在哪里"——不要凭感觉优化,要用数据说话。

调试的"心法":

  • 先复现,再分析——不能稳定复现的问题,很难解决
  • 缩小范围——用二分法逐步缩小问题范围
  • 对比法——好的和坏的对比,找出差异
  • 假设验证——先假设原因,再设计实验验证

3.3.3 测试与验证:怎么证明你的接收机是对的?

“我的接收机定位准不准?”——这是一个看似简单、其实很难回答的问题。
因为你需要一个"真值"来对比,而真值本身就很难获得。

GNSS 接收机的测试,通常分为几个层次:

第一层:实验室测试(信号源/模拟器)
用 GNSS 信号模拟器生成标准信号,测试接收机的各项指标。

测试项目 测试内容 典型设备
捕获灵敏度 最弱能捕获多弱的信号 信号模拟器
跟踪灵敏度 最弱能维持跟踪多弱的信号 信号模拟器
定位精度 静态/动态定位误差 信号模拟器
首次定位时间(TTFF) 冷/温/热启动时间 信号模拟器
伪距精度 伪距测量噪声 信号模拟器
载波相位精度 载波相位测量噪声 信号模拟器
动态性能 高速度/高加速度下的表现 信号模拟器

信号模拟器的好处是:

  • 场景可重复,每次测试条件一样
  • 可以精确控制信号强度、卫星数量、动态参数
  • 可以模拟各种极端场景(弱信号、高动态、干扰)

第二层:基准对比测试(跟参考接收机比)
找一台高精度的参考接收机(比如 Trimble、Leica、Septentrio 的高端机型),跟你的接收机放在一起,同时观测,对比结果。

  • 静态对比:固定位置,长时间观测,看偏差和稳定性
  • 动态对比:车载、机载,看动态跟踪性能
  • 环境对比:城市峡谷、树荫、室内边缘等不同环境

第三层:外场测试(真实环境)
到真实环境中去测试,看实际表现。

  • 开阔场:最好的环境,测试基准性能
  • 城市环境:测试多径、遮挡、重捕能力
  • 室内/半室内:测试弱信号性能
  • 干扰环境:测试抗干扰能力

外场测试最接近真实使用场景,但也最难复现——每次测试的卫星分布、环境条件都不一样,很难做精确的 A/B 对比。

第四层:长期稳定性测试
长时间连续运行,看会不会出问题:

  • 会不会死机?
  • 会不会漂移?
  • 会不会有偶发的 bug?
  • 温度变化会不会影响?

长期测试通常要跑几天、几周甚至几个月——很多问题,只有长时间运行才会暴露出来。

3.3.4 性能优化:从代码层面怎么提升性能

接收机软件的性能优化,主要围绕三个目标:

  • 更快(计算速度)
  • 更小(内存占用)
  • 更省(功耗)

a. 计算速度优化

常见的优化手段:

算法级优化(最有效):

  • 换更好的算法——O(n²) 换成 O(n log n),效果立竿见影
  • 减少计算量——能提前算的提前算,能复用的复用
  • 近似计算——精度允许的情况下,用近似代替精确计算

代码级优化:

  • 循环展开(Loop Unrolling)——减少循环开销
  • 函数内联(Inline)——减少函数调用开销
  • 查表代替计算——用空间换时间
  • 定点化——把浮点运算换成定点运算(在没有 FPU 的 MCU 上效果显著)

编译器优化:

  • 开 O2/O3 优化
  • LTO(链接时优化)
  • PGO(剖面引导优化)

硬件加速:

  • DSP 指令(ARM NEON 等)
  • FPGA 加速
  • 专用硬件模块

优化的原则:

  • 先测量,再优化——不要凭感觉优化,先用 profiling 工具找到瓶颈
  • 20/80 法则——20% 的代码消耗了 80% 的时间,优化那 20% 就行
  • 不要过度优化——为了 1% 的性能提升,把代码搞得面目全非,不值得

b. 内存优化

嵌入式系统的 RAM 通常很紧张——几 MB 甚至几百 KB 的 RAM,要装下所有数据。

内存优化手段:

  • 减少全局变量——能用局部变量就不用全局
  • 动态分配按需使用——用完就释放
  • 数据结构优化——用更小的类型,合理对齐
  • 缓冲区复用——不同时使用的缓冲区共用一块内存
  • Flash 存常量——常量数据放在 Flash 里,不占 RAM
  • 压缩——数据量大的话,可以压缩存储

c. 功耗优化

对于电池供电的设备(手表、追踪器等),功耗是关键指标。

功耗优化手段:

  • 降低主频——能跑慢点就跑慢点
  • 低功耗模式——空闲时进入睡眠/深度睡眠
  • 按需唤醒——有数据才唤醒处理,处理完继续睡
  • 关闭不用的外设——不用的模块关掉时钟和电源
  • 算法优化——减少计算量 = 减少 CPU 工作时间 = 省电

功耗优化是个系统工程——从硬件到软件,从底层到上层,每一层都要抠。

3.3.5 常见坑点与避坑指南

最后,分享一些接收机软件开发中常见的"坑"——都是前人踩过的血泪教训。

坑一:时序问题
现象:偶尔出问题,大部分时候正常,很难复现。
原因:多任务/中断之间的时序竞争、数据竞争。
避坑:

  • 共享数据加保护(信号量、互斥锁、关中断)
  • 不要在中断里做太多事情
  • 任务之间用消息队列通信,不要直接共享变量

坑二:溢出问题
现象:偶尔结果不对,或者突然跳变。
原因:整数溢出、缓冲区溢出、数组越界。
避坑:

  • 仔细考虑数值范围,选择合适的数据类型
  • 所有数组访问都检查边界
  • 缓冲区用环形缓冲区(Ring Buffer),管理读写指针

坑三:精度问题
现象:计算结果跟仿真差一点,积累起来误差很大。
原因:浮点精度不够、定点化误差、近似计算的误差。
避坑:

  • 关键计算用 double,不要全用 float
  • 定点化 carefully,注意量化误差
  • 注意误差累积——迭代算法尤其要注意

坑四:初始化问题
现象:第一次启动正常,重启后不正常;或者冷启动正常,热启动不正常。
原因:全局变量没正确初始化、状态没正确复位。
避坑:

  • 所有全局变量显式初始化
  • 状态切换时,确保所有相关变量都复位
  • 不要假设"上电时 RAM 是 0"——不一定

坑五:边界条件
现象:大部分情况正常,极端情况出问题。
原因:没考虑边界情况(0 颗卫星、全部失锁、数据全 0 等)。
避坑:

  • 每个函数都考虑输入的边界情况
  • 单元测试要覆盖边界条件
  • 防御式编程——对所有输入做校验

坑六:硬件相关的"玄学"问题
现象:这块板子正常,那块板子不正常;或者温度一变就不正常。
原因:硬件差异、时序裕量不够、电源噪声、温度漂移。
避坑:

  • 不要假设硬件是"理想"的
  • 留出足够的设计裕量
  • 遇到"玄学"问题,先怀疑硬件,再怀疑软件

小结一下:

接收机软件,是 GNSS 技术的"灵魂"——硬件搭好了舞台,软件才是台上唱戏的主角。
从操作系统选型到编程语言选择,从分层架构到模块化设计,从开发流程到性能优化,每一个环节都有它的门道。

“硬件决定上限,软件决定下限”——这句话的另一面是:
好的软件,能把硬件的潜力充分发挥出来;差的软件,再好的硬件也白搭。
一个优秀的接收机,一定是硬件和软件协同优化的结果。

基础篇到这里就告一段落了——我们从天上的卫星系统,讲到了地上的接收机硬件,又讲到了接收机里的软件。
接下来的高级篇,我们将深入 GNSS 的核心技术:信号体制、捕获算法、跟踪算法、PVT 解算、RTK……
那些听起来很"黑科技"的东西,我们一层层拆开来看。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐