周跳的探测及修复


前言:

本章节代码均在Gitee中开源:

导航工程: 导航工程及其有关的所有项目 - Gitee.comicon-default.png?t=N7T8https://gitee.com/Ehundred/navigation-engineering/tree/master/%E5%8D%AB%E6%98%9F%E5%AF%BC%E8%88%AA%E5%8E%9F%E7%90%86/%E5%91%A8%E8%B7%B3%E6%8E%A2%E6%B5%8B其中涉及到载波定位的知识,再另一章有详细讲解,感兴趣可以移步到:

我们如何收到卫星信号?(导航电文,载波与测距码)_导航测距码-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_74260823/article/details/139411651因为这章是学校作业,所以稍微正经点. 


什么是周跳

载波测量观测值

载波相位测量,因为接收机只可以观测到不足一周的相位,但是卫星发射信号到接收机上,中间一定会有很多整周期波:

所以,实际上的总相位为:

而这里的整周数,因为是无法直接测量出来的,所以我们就称他为——整周模糊度

为了计算出整周模糊度,我们还需要对卫星连续观测。在第一次观测的基础上,我们会不断受到电磁波,不足一周的相位会不断增加。当增加到一周时,就将这一周单独提出来。于是,不足一周的相位部分永远小于一周,而满一周时,便将多出的一周加到整周计数上,于是新的表达式为:

换一种方式解释: 

电子钟

我们现在有个电子钟,但是这个电子钟不太智能,只能看到秒数的部分: 

但是他的实际时间,肯定不是30秒,因为我们要考虑到看不见的分钟部分。他的实际时间是:

我们一直看这个钟。现在是30秒,慢慢他会变成31,32……,一直到,他变成了61秒 

但是,61秒就超过了一分钟,会发生进位。虽然我们看不到分钟,但是当61秒变成1秒的时候,我们还是知道,分钟的部分加了1。此时的时间为:

我们就这样一直看,当秒数从60跳到1时,我们就手动记录多加了一分钟。 就算我们不知道分钟数,但是我们可以知道,刚开始的分钟数xx,加上我们手动记录的分钟,就是当前的分钟数,我们把xx当成了一个未知的常量。

于是,新的表达式可以写成:

这样有什么意义?当我们想计算两个时间的差,分钟模糊度会被消掉,剩下的秒数和手动记录的分钟都是已知的,可以直接计算出来。 

周跳

还是电子钟。我们一直观测这个电子钟,但是突然,发生了意外,电子钟卡住了! 

这个时候,我们看到的秒数是不变的,一直为21秒,而因为一直没有秒数的变化,我们也不敢擅自给手动记录的分钟数进行操作。于是,手动记录的分钟一直停在了5分钟
突然,电子钟又恢复了正常。我们又看到了秒数,但是这个秒数,因为中间卡了一段时间,所以突然跳到了另一个值;而手动记录的分钟数,因为我们并不知道卡住的时候,过了多少分钟,所以这个5分钟就不可信了。

所以之前的数据就没用了吗?当然不是。虽然手动记录的分钟数差了一个值,但是我们把这个差值,归到本就不知道的分钟模糊度中,这样会产生两个结果:

  1. 从5分钟开始继续计数,往前的数据可以使用,往后的数据也可以使用
  2. 虽然分钟模糊度变成了一个新值,但是新值和旧值中间只差了一个常数,这个常数我们可以粗略估计出来,虽然是新值,但是可以和旧值联系起来。

这样做有什么意义?如果我们想计算卡住之前的时间和卡住之后的时间的差,如果废弃之前的数据,那么两段时间就完全联系不到一起。但是如果用新的表达式,分钟模糊度还是可以被消掉,手动记录的分钟数也是可以被使用的,两个时间还是可以按之前的方法正常计算,只不过多了一个卡住的时间这一常量。

所以,这个卡住的时间,就叫做周跳;而周跳产生的原因,就是电子钟卡了。

周跳的产生

周跳产生的原因有很多,但是根本原因,都来源于观测的中断。卫星和接收机中间隔了一栋楼,信号接收异常,观测就中断了一段时间;因为周围在考试有电磁干扰,卫星信号很弱,观测也中断了一段时间。
中断的时候,不满一周部分无法正确计数,而整周计数部分因为看到不满一周部分没有变化,所以整周计数也不敢擅自变化,于是观测值就一直停在了一个值了。而等信号恢复正常,接收机接收到了正常的信号,此时不满一周部分会突然变化为另一个值,而整周计数因为一直没变,所以还是从原来的值开始继续计数。但是和电子钟一样,在观测失锁的时候,并不知道过去了多少周,于是就把这个常数放在整周模糊度中,而整周计数按照原来的值继续计数,还是一个可以使用的值。

 这个失锁导致未知的常数,就叫做周跳。周跳产生的原因,就是信号中断了。

为什么要探测周跳?

废话,因为计算结果需要周跳。

计算机是很笨的。你们可能觉得,只需要看相位的变化,还有总相位的值,不就可以了吗?但是:

  1. 首先,计算机的观测并不是我们想象中的完全连续。计算机的观测是一种均匀中断的连续,啥意思?就是比如每隔1秒观测一次,一直连续观测下去,最终观测到的是一个又一个的点,而非一段连续的三角函数
  2. 就算相位发生了极大的变化,计算机也不知道。因为接收机只负责接收,而数据的分析要交给另外的机器去做,也就是我们现在应该去处理的,周跳的探测
  3. 相位等值发生较大变化,并不百分百因为产生了周跳,还可能因为观测产生误差,或者的的确确真实数据是如此。所以,探测周跳,还应考虑排除误差

周跳探测的方法

周跳探测,一般会采用以下几种方法:

  1. 屏幕扫描法,就是看一眼过去,看看相位有没有太大的变化
  2. 高次差法,多项式拟合法
  3. MW组合观测值法
  4. 电离层残差法
  5. 三差法

这里,我们介绍中间三种用的最多的方法:

高次差法

说简单点,就是差分数组。
(补充:一命通关差分-CSDN博客 )

如果在某一个时间点,发生了周跳,往后的所有整周模糊度一定会加上一个周跳常数ΔN,所以我们可以写出整周模糊度的函数表达:

此时,我们把整周模糊度写成一个数组:N[n],然后进行差分:

但是这可能是误差导致的,再把dif1差分:

就这样一直差分下去,周跳附近的点的差分值会越来越规律,一直到呈现出一个很规律的图形:

我们不断差分,其实就是为了排除误差,而最终出现这样的图形,就可以断定,发生了周跳。 


线性组合观测值法 

电离层残差(GF)和MW组合观测值法,实际上都是采用线性组合观测值的方法来探测周跳,所以就放一起讲了。

线性组合观测

首先,什么是线性组合观测?我们知道,一段卫星信号,有两个频率的载波,波段分别为L1和L2。
换句话说,对同一个卫星,一个接收站会受到这个卫星发来的两个不同频率的载波,而这个不同频率的载波,产生的观测值也是不同的。

假设L1载波的观测值为φ1,L2载波的观测值为φ2,φ为不足一周的相位。

那么组合观测值,φ=n*φ1+m*φ2,当n和m取不同的值时,产生的不同结果,就叫不同的线性组合观测,常见的有:

  1. 无电离层,消去电离层延迟
  2. 宽巷,n=1,m=-1
  3. 窄巷,n=1,m=1
  4. 电离层残差,n=λ1,m=-λ2

所以对一个线性组合观测值,我们直接采用一个基类: 

//单个载波
struct carrier
{double f;//频率,从L1和L2波段中选择double Lambda;//波长,f*l=cdouble Fai;//不足一周计数double N;//整周模糊度double count;//整周计数double distance;//伪距double TEC;//电子含量
};//双载波观测值
class Dual_frequency_carrier
{
public:Dual_frequency_carrier(carrier c1,carrier c2):_carrier1(c1),_carrier2(c2){}carrier _carrier1;carrier _carrier2;
};//线性组合观测值(基类)
class LineObservation
{
public:LineObservation(const Dual_frequency_carrier& carriers):_carriers(carriers){}double Observation(){return _observation;}
protected:Dual_frequency_carrier _carriers;double _observation;//线性组合观测值virtual double _Observation() = 0;//线性组合的方法
};

 而对所有的线性组合,只需要去继承这个基类,然后重写一下线性组合的方法,就可以了。

 电离层残差组合

//GF观测值
class GeometryFree : public LineObservation
{
public:GeometryFree(const Dual_frequency_carrier& carriers):LineObservation(carriers){_Observation();}
protected://重写虚函数,即线性组合的方法virtual double _Observation(){carrier car1 = _carriers._carrier1;carrier car2 = _carriers._carrier2;double A1 = -40.3 * car1.TEC;double Vion1 = A1 / pow(car1.f, 2);_observation = car1.Lambda * car1.N - car2.Lambda * car2.N + (1 - pow(car1.f, 2) / pow(car2.f, 2)) * Vion1;//直接用推导出的公式return _observation;//线性组合观测值LGF}
};

此时,我们再来看表达式:

 

两个载波的波长是已知且为常量的。
电离层延迟在电离层电子含量TEC变化不大的时候,也是不变的,而在短时间内TEC不会发生太大的变化,所有我们也可以认为他是一个常量。
最后只剩下整周模糊度,因为整周模糊度如果不发生周跳,那么整周模糊度也是一个常量,

所以,只要不发生周跳,那么LGF观测值一定不会发生太大的变化
换句话说,如果LGF发生了太大的变化,那么一定发生了周跳

我们把这个变化的最大上限,定为0.05cm,还是采用差分的方法,如果超过了最大值,就表示发生了周跳:

//GF周跳探测
class GF_detect
{
public:GF_detect(const vector<Dual_frequency_carrier>& data){//把所有的载波变为GF观测值for (const auto& e : data){_data.push_back(GeometryFree(e));}_Detect();}bool Detect(){return _detect;}vector<int> Slip(){return _slip;}private:vector<GeometryFree> _data;bool _detect;//是否发生周跳,发生周跳为truevector<int> _slip;//发生周跳的具体时间点double MAX_GAP = 5E-4;//最大阈值bool _Detect(){//遍历每一个历元差,如果差值超过了阈值,那么一定发生了周跳for (int i = 1; i < _data.size(); i++){if (_data[i].Observation() - _data[i - 1].Observation() > MAX_GAP){_detect = true;//发生了周跳_slip.push_back(i);//发生周跳的时间点}}return _detect;}
};

MW组合观测

为啥叫MW组合?因为是一个姓M和一个姓W的人提出来的。但是推导方法非常非常简单,如果把我放在那个年代,说不定能叫YB观测值。

因为符号太多,所以只能采取手写的方法推导了,见谅

最后得出的结果为:

 在这里,我们以周为单位,来进行代码的示例

class MW :public LineObservation
{
public:MW(const Dual_frequency_carrier& carriers):LineObservation(carriers){_Observation();}
private:virtual double _Obersvation(){carrier car1 = _carriers._carrier1;carrier car2 = _carriers._carrier2;double f1 = car1.f;double f2 = car2.f;double L1 = car1.Lambda;double L2 = car2.Lambda;double P1 = car1.distance;double P2 = car2.distance;_observation = (f1 - f2) / (f1 + f2) * (P1 / L1 + P2 / L2) - (car1.Fai - car2.Fai);//周数return _observation;}
};

而还是和电离层残差组合一样,

等式左边都是变化不大的值,而等式右边是整周模糊度的差值。按理说,左边计算出来的值,应该是一个变化不会太大的常量,如果变化很大,那么就表示发生了周跳。

前面的不用管,我们只需要看后面的检测方法——用取平均值和计算方差的方法,来检测到底是周跳还是误差:

class MW_detect
{MW_detect(const vector<Dual_frequency_carrier>& data){//将载波数据计算为MW组合for (const auto& e : data){_data.push_back(MW(e));}_Detect();}bool Detect(){return _detect;}vector<int> Slip(){return _slip;}
private:vector<MW> _data;bool _detect;//是否发生周跳,发生周跳为truevector<int> _slip;//周跳具体时间点double MAX_GAP = 0.5;//最大阈值:0.5周bool _Detect(){double sum = _data[0].Observation();double average = _data[0].Observation();double sigma = 0.5;bool record = false;for (int i = 1; i < _data.size(); i++){//如果可能发生周跳if (record){//判断是周跳还是误差if (abs(_data[i].Observation() - _data[i - 1].Observation()) <= 1){_detect = true;_slip.push_back(i);}}if (abs(_data[i].Observation() - average) >= sigma * 4)record = true;//可能发生周跳,也可能是误差sum += _data[i].Observation();average = sum / (i + 1);sigma = sigma * sigma + (pow(_data[i].Observation() - average, 2) - sigma * sigma) / (i + 1);}return _detect;}
};

最后,给自己叠个甲。因为自己才是导航工程大二的本科生,有些概念理解可能不到位,而又想用最容易理解的方式表达出来,所以可能正确性会稍微有些偏差。但是对初学者来说,应该不会存在太大的错误,如果可以帮到你,真的荣幸之极。还有

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/29165.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是Vue开发技术

概述 Vue.js 是一个用于构建用户界面的渐进式框架&#xff0c;它设计得非常灵活&#xff0c;可以轻松地被集成到任何项目中。 vue是视图的发音&#xff0c;其目的是帮助开发者易于上手&#xff0c;提供强大的功能构建复杂的应用程序 示例 以下是vue基本的语法概述 声明式渲…

探索未来工作新伙伴:机器人流程自动化(RPA)揭秘

想象一下&#xff0c;如果你的日常工作中那些繁琐、重复的任务&#xff0c;比如数据录入、文件整理、邮件发送等&#xff0c;都能自动完成&#xff0c;你将拥有更多时间专注于真正需要创造力和智慧的工作&#xff0c;是不是听起来就像拥有了一个私人助理&#xff1f;这并不是遥…

VUE3版本新特性

VUE3版本新特性 VUE3和VUE2的区别路由的使用vite安装项目新特性使用 1.VUE3和VUE2的区别 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece 于 2022 年 2 月 7 日星期一成为新的默认版本! Vue3性能更高,初次渲染快55%, 更新渲染快133% 。…

推荐 3 款小巧的文件压缩、投屏和快速启动软件,请收藏,避免找不到

Maya Maya是一款由博主25H开发的体积小巧、简单易用的快速启动工具。它的操作逻辑和界面设计几乎复刻了Rolan早期版本&#xff0c;功能上与Rolan几乎别无二致。Maya支持多文件拖拽添加启动、快捷键呼出、自动多列显示等功能。此外&#xff0c;Maya还具备lnk文件解析功能。 May…

多目标粒子群算法(MOPSO):原理讲解与代码实现 Matlab代码免费获取

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 粒子群算法 多目标粒子群算法 一、外部档案…

Android11 以Window的视角来看FallbackHome的启动

在WMS中&#xff0c;使用WindowState代表着一个Window并维护着一个Window的"层级树"&#xff0c;每个Window需要按照"层级"的规则进行排列。对于FallbackHome&#xff0c;其Window是挂载在home task上&#xff0c;而home task挂载在DefaultTaskDisplayArea…

python之日志(三)--将捕获的异常写入日志中

1.日志级别 import logging #日志模块 #默认下只会从第3级开始打印 logging.debug(DEBUG级别) #1级 logging.info(INFO级别) #2级 logging.warning(WARNING级别) #3级 logging.error(ERROR级别) #4级 logging.critical(CRITICAL级别) #5级 默认第1和第2级的日志不会打印 …

解决 uniapp h5 页面在私有企微iOS平台 间歇性调用uni api不成功问题(uni.previewImage为例)。

demo <template><view class"content"><image class"logo" src"/static/logo.png"></image><button click"previewImage">预览图片</button></view> </template><script> //打…

React@16.x(32)useDebugValue

目录 1&#xff0c;介绍2&#xff0c;作用 1&#xff0c;介绍 从一个例子开始&#xff1a; export default function App() {const [n, setN] useState(0);const refH1 useRef();useEffect(() > {console.log("父组件");});return <h1 ref{refH1}>{n}&l…

使用 imu_utils 、Kalibr 工具进行IMU和相机内外参外参标定

文章目录 一、问题描述二、imu_utils 工具的使用1. 安装1.1 下载与编译1.2 编译bug解决 2. 标定 IMU2.1 imu bag包录制2.1.1 imu 数据格式2.1.2 imu 数据录制 2.2 imu 标定 三、Kalibr 工具的使用1. 安装1.1 下载与编译1.2 问题解决 一、问题描述 vins系列安装完成后&#xff…

EasyRecovery电脑数据恢复软件2024数据守护神#误删文件神器#硬盘恢复利器#数据丢失救星

&#x1f310; 你是否曾经因为误删文件、硬盘损坏等原因&#xff0c;失去了重要的数据&#xff1f;别担心&#xff0c;EasyRecovery电脑数据恢复软件是你的救星&#xff01;它能够帮你找回丢失的文件&#xff0c;让你的数据重新焕发生机。 &#x1f50d; EasyRecovery软件的核…

有关健身的俄语表达,柯桥零基础俄语培训

фитнес 健身 тренер 教练 абонемент 会员卡 аэробика 有氧运动 анаэробика 无氧运动 плавание 游泳 пробежка / бег трусцой 慢跑 беговая дорожка 跑步机 йога 瑜伽 коври…

持续学习的综述: 理论、方法与应用

摘要 为了应对现实世界的动态&#xff0c;智能系统需要在其整个生命周期中增量地获取、更新、积累和利用知识。这种能力被称为持续学习&#xff0c;为人工智能系统自适应发展提供了基础。从一般意义上讲&#xff0c;持续学习明显受到灾难性遗忘的限制&#xff0c;在这种情况下…

【Linux硬盘数据读取】WIN10访问linux分区解决方案:ext2fsd

<div id"content_views" class"htmledit_views" style"user-select: auto;"><p>尝试ext2explore、Paragon ExtFS都不好用&#xff0c;强烈安利ext2fsd&#xff0c;可读写&#xff0c;很强大</p> 转自&#xff1a;https://blog…

C++之模板(一)

1、为什么需要模板 将具有相同逻辑的一段代码提供一份模板&#xff0c;当我们需要处理不同类型的时候&#xff0c;可以通过数据类型当作参数来传递&#xff0c;从而实例化出对应类型的处理版本。 2、模板的定义 也是一种静态多态。 3、模板的分类 4、函数模板 5、函数模板的使…

-------------------------面试散文-----------------------------------

问题1&#xff1a;vue中动态引入图片&#xff0c;为什么使用require&#xff1f; 回答&#xff1a;因为动态添加的src 编译过后的文件地址和被编译过后的资源文件地址不一致&#xff0c;从而导致无法访问题 而使用require 返回的就是资源文件被编译后的文件地址&#xff0c;从…

Aeron:Aeron Agent

Aeron Agent 是一个 Java 代理&#xff0c;用于提供 Aeron、Aeron Archive 和 Aeron Cluster 中发生的运行时低级日志信息。这些日志语句包括从高级管理员事件到大容量数据帧事件。 在调试 Archive 和 Cluster 问题时&#xff0c;Aeron Agent 的日志数据尤其有用。 一、Availab…

本地无法连接linux上的MariaDB数据库

问题&#xff1a;本地用DBeaver无法连接服务器上的MariaDB数据库 &#xff1f; 测试1&#xff1a;在cmd中ping 服务器IP&#xff0c;看是否能ping通&#xff0c;能ping通&#xff0c;没有问题 测试2&#xff1a;在cmd中telnet 服务器IP 端口&#xff0c;看是否能访问&#xf…

常见的Redis使用问题及解决方案

目录 1. 缓存穿透 1.1 解决方案 2. 缓存击穿 2.1 解决方案 3. 缓存雪崩 3.1 概念图及问题描述 ​编辑3.2 解决方案 4. 分布式锁 4.1 概念 4.2 基于redis来实现分布式锁 4.3 用idea来操作一遍redis分布式锁 4.4 分布式上锁的情况下&#xff0c;锁释放了服务器b中的锁…