周跳的探测及修复


前言:

本章节代码均在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=c
	double 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;//是否发生周跳,发生周跳为true
	vector<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;//是否发生周跳,发生周跳为true
	vector<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;
	}
};

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Excel批量一列转多列多行

你在公司或学校是否遇到过对人员进行分组&#xff0c;你是否曾一个一个复制粘贴&#xff0c;如只有100人&#xff0c;尚有时间一一分组&#xff0c;如1000人&#xff0c;甚至更多&#xff0c;不知分到“地老天荒”是否可以完成&#xff01; 今天刘小生分享一个方法“用替换等号…

OZON爆款新品推荐丨OZON珠宝首饰好款推荐

在OZON平台上&#xff0c;珠宝首饰一直以其独特的魅力和精致的工艺吸引着广大消费者。以下是几款推荐的OZON珠宝首饰好款&#xff1a; Top1 隐形树藤项链 MARIOLA Ювелирное колье серебро 925 леска невидимка женское сере…

一个示例学习C语言到汇编层面

给出以下代码 #include<stdio.h> int main() {int x 0, y 0, z 0;while (1) {x 0;y 1;do {printf("%d\n", x);z x y;x y;y z;} while (x < 255);}return 0; }我们把这个程序编写成32位程序&#xff0c;然后我们放入IDA中进行分析 .text:0080187…

DAY04 HTMLCSS

文章目录 一 表单(1) 数字控件(2) 颜色控件(3) 日期控件(4) 月份控件(5) 星期控件(6) 搜索控件(7) 范围控件 二 浮动框架三 结构化标签四 CSS1 CSS概述2 CSS的编写位置1. inline style 行内样式2. inner style 内部样式3. outer style 外部样式4. 小结 3 CSS选择器1. 通用选择器…

OSPF 动态路由协议(思科、华为)

#交换设备 OSPF 动态路由协议 一、基本概念 1.中文翻译&#xff1a;开放式最短路径优先路由协议&#xff08;open shortest path first&#xff09;&#xff0c;是一个内部网关路由协议&#xff08;一个自治系统内&#xff09;2.也称为&#xff1a;链路状态路由协议&#xf…

【操作系统原理】三级项目-基于并行计算的的单词统计

前言 这是当时选修《操作系统原理》时需要做的一个实验项目&#xff0c;也是三级项目&#xff0c;选题选的是第二章有关并行计算的实验&#xff0c;这个项目有匿名版的PPT和视频&#xff0c;可以私我&#xff0c;发送“基于并行计算的的单词统计”即可获取整个项目的压缩包。 …

java:【@ComponentScan】和【@SpringBootApplication】扫包范围的冲突

# 代码结构如下&#xff1a; 注意【com.chz.myBean.branch】和【com.chz.myBean.main】这两个包是没有生重叠的。 主程序【MyBeanTest1、MyBeanTest2、MyBeanTest3】这两个类是在包【com.chz.myBean.main】下 # 示例代码 【pom.xml】 <dependency><groupId>org.…

基本表的定义:创建表、修改表、删除表

一、创建数据库与打开数据库 学生选课数据库 学生&#xff08;学号&#xff0c;姓名&#xff0c;性别&#xff0c;出生时间&#xff0c;所在系&#xff09; 课程&#xff08;课程编号&#xff0c;课程名&#xff0c;先修课程号&#xff09; 选课&#xff08;学号&#xff0…

【机器学习300问】120、该怎么用RNN来构建语言模型?

一、基本概念补充 在构建语言模型之前补充几个自然语言处理&#xff08;NLP&#xff09;基本概念。 &#xff08;1&#xff09;语料库&#xff08;Corpus&#xff09; ① 语料库的定义 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;语料库是一个经过组织和加工…

二叉树-根据先序遍历和中序遍历序列重建二叉树

目录 一、问题描述 二、解题思路 1.首先明确先序遍历和中序遍历的性质&#xff1a; 2.确定根节点及左右子树 3.对子树进行递归操作 4.递归返回条件 三、代码实现 四、刷题链接 一、问题描述 二、解题思路 1.首先明确先序遍历和中序遍历的性质&#xff1a; 先序遍历&am…

探索比特币多面体

目录 前言 一、比特币挖矿 1.挖矿设备的演化 2.矿池 二、比特币脚本 1.交易结构 2.交易的输入 3.交易的输出 4.P2PK 输入输出脚本的形式 实际执行情况 5.P2PKH 输入输出脚本的形式 实际执行情况 6.P2SH 输入输出脚本的形式 7.进一步说明 8.多重签名 9.脚本执…

Graphviz——安装、绘制可视化协议状态机(Python)

1、简介 Graphviz 是一个开源的图形可视化软件包&#xff0c;特别擅长绘制有向图和无向图等结构化图形。它非常适合用于生成各种图表&#xff0c;例如流程图、网络图、状态机图、层次结构图等。Graphviz 的主要组件 dot: 这是Graphviz最常用的布局程序&#xff0c;用于创建有向…

(杭州中科微)全星座定位导航模块GM32的应用推荐及性能指标解析

1、 关于GNSS的原理&#xff1a; 它是通过接收来自地球轨道上的W星信号&#xff0c;并利用信号传播延迟的原理&#xff0c;计算与接收器之间的距离&#xff0c;从而实现对接收器位置的精确测量。 而GNSS的定位原理&#xff1a;W星导航系统GNSS接收机主要是通过三边测量法&…

Postgresql配置SSL连接

1、系统需要有openssl、openssl-devel包 yum -y install openssl openssl-devel 2、查看当前数据库是否使用openssl编译 pg_config|grep CONFIGURE 如果没有重新编译 make clean make && make install 3、服务器端证书配置 服务器端需生成三个文件: root.crt(根证…

如何用stable diffusion画出这种风景幻视画?

最近出现了一种奇怪的表情包。 看到小图的时候有几个字&#xff0c;点看一看却是一张正常的图片。 比如&#xff0c;看一个有意思的图&#xff0c;这两张图的预览模式下有明显的“银河”两字。 点开放大呢&#xff1f; 竟然是服饰的形状和颜色。 再看一张类似效果的&#xf…

Java_JDK下载与环境变量配置

目录 一、JDK下载安装 二、安装后配置环境变量 三、在编辑器里使用JDK 一、JDK下载安装 JDK 是Java开发工具包&#xff0c;它提供了用于开发和运行Java程序所需的工具和库。JDK包括Java编译器、Java虚拟机、Java标准库等。在IDEA中使用Java语言编写代码时&#xff0c;需要安…

20240617通过串口配置索尼SONY的HDMI OUT输出的8530机芯

20240617通过串口配置索尼SONY的HDMI OUT输出的8530机芯 2024/6/17 15:54 缘起&#xff1a;需要在RK3588开发板OK3588-C上使用SONY的8530机芯。特意熟悉8530的串口命令&#xff01; 目的&#xff1a;需要配置SONY的8530机芯为RGB输出&#xff0c;4K分辨率。 串口波特率&#x…

Redis 管道

Redis的消息交互 当我们使用客户端对Redis进行一次操作时&#xff0c;如下图所示&#xff0c;客户端将请求传送给服务器&#xff0c;服务器处理完毕后&#xff0c;再将响应回复给客户端&#xff0c;这要花费一个网络数据包来回的时间。 如果连续执行多条指令&#xff0c;那就会…

Elixir学习笔记——编写文档

Elixir 将文档视为一等级别类。文档必须易于编写且易于阅读。在本指南中&#xff0c;您将学习如何在 Elixir 中编写文档&#xff0c;涵盖模块属性、样式实践和文档测试等结构。 Markdown Elixir 文档是使用 Markdown 编写的。网上有很多关于 Markdown 的指南&#xff0c;我们…

根据配置的参数规格生成商品SKU

参数规格如下&#xff1a; let specParam [[红色,绿色,白色,黄色], [大,小]]js部分&#xff1a; let getSpecParamCom (specData, index) > {for (let i 0; i < specData[index].length; i) {tempResult[index] specData[index][i];if (index ! specData.length - …