周跳的探测及修复

news2024/10/6 4:06:13


前言:

本章节代码均在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;
	}
};

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

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

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

相关文章

2024/6/16周报

文章目录 摘要Abstract文献阅读题目问题本文贡献方法aGNN输入和输出模块嵌入模块编码器和解码器模块&#xff1a;支持多头注意的GCN多头自注意力机制GCN模型解释&#xff1a;SHAP 案例研究地下水流动与污染物运移模型研究场景设计 数据集实验结果 代码复现结论 摘要 本周阅读了…

BatchNormalization和Layer Normalization解析

Batch Normalization 是google团队2015年提出的&#xff0c;能够加速网络的收敛并提升准确率 1.Batch Normalization原理 图像预处理过程中通常会对图像进行标准化处理&#xff0c;能够加速网络的收敛&#xff0c;如下图所示&#xff0c;对于Conv1来说输入的就是满足某一分布…

Python 数据持久化:使用 SQLite3 进行简单而强大的数据存储

&#x1f340; 前言 博客地址&#xff1a; CSDN&#xff1a;https://blog.csdn.net/powerbiubiu &#x1f44b; 简介 SQLite3是一种轻量级嵌入式数据库引擎&#xff0c;它在Python中被广泛使用。SQLite3通常已经包含在Python标准库中&#xff0c;无需额外安装。你只需导入 s…

antd的表格组件错乱问题

环境 react&#xff1a;17.0.2 antd&#xff1a;3.26.20 问题 表格头列宽度和表格体列宽度不一致&#xff0c;表格错乱 解决 针对这个问题官方github仓库里面有专门的issues https://github.com/ant-design/ant-design/issues/13825 里面给出了几种解决方案&#xff1a…

31、matlab卷积运算:卷积运算、二维卷积、N维卷积

1、conv 卷积和多项式乘法 语法 语法1&#xff1a;w conv(u,v) 返回向量 u 和 v 的卷积。 语法2&#xff1a;w conv(u,v,shape) 返回如 shape 指定的卷积的分段。 参数 u,v — 输入向量 shape — 卷积的分段 full (默认) | same | valid full&#xff1a;全卷积 ‘same…

Compose 可组合项 - DatePicker、DatePickerDialog

一、概念 一般是以对话框的形式呼出&#xff0c;DatePickerDialog 就是对 DatePicker 的一个简单对话框封装。 Composable fun DatePicker( state: DatePickerState, modifier: Modifier Modifier, dateFormatter: DatePickerFormatter remember { DatePickerFor…

15.编写自动化测试(下)

标题 三、控制测试流程3.1 添加测试参数3.2 并行或连续运行测试3.3 显示函数输出3.4 指定/过滤测试用例名称3.5 忽略某些测试用例3.6 只运行被忽略的测试 四、测试的组织结构4.1 概念引入4.2 测试私有函数4.2 单元测试4.3 集成测试4.4 集成测试中的子模块4.5 二进制crate的集成…

【漏洞复现】畅捷通T+ keyEdit SQL注入漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

Confluence安装

Confluence安装 1.安装 #下载confluence版本&#xff08;8.5.11&#xff09; https://www.atlassian.com/software/confluence/download-archives #修改权限 chmod x atlassian-confluence-8.5.11-x64.bin #执行安装 ./atlassian-confluence-8.5.11-x64.bin按照以下提示输入&…

SD-WAN在教育行业的应用及优势解析

随着教育领域的数字化转型&#xff0c;网络技术的需求变得愈发迫切。作为一种前沿的网络解决方案&#xff0c;SD-WAN正在为教育行业提供强有力的支持。本文将详细探讨SD-WAN在教育行业的应用&#xff0c;并分析其为教育行业带来的众多优势。 实现多校区高效互联 教育机构通常拥…

稳了?L3规模化落地在即,激光雷达公司成首批赢家

作者 | 芦苇 编辑 | 德新 在中国&#xff0c;距L3级自动驾驶的规模化落地&#xff0c;又近了一步。 随着国内试点政策刷新&#xff0c;越来越多的车企在部分市域获得了自动驾驶测试牌照&#xff0c;能上路测试的L3级自动驾驶车辆正在快速增加。 其中一个重要节点是&#xf…

Python基础用法 之 转义字符

将两个字符进⾏转义 表示⼀个特殊的字符 \n ---> 换⾏&#xff0c;回⻋ \t ---> 制表符, tab键 注意&#xff1a; print( end\n)&#xff1a; print() 函数中默认有⼀个 end\n, 所以,每个 print 结束之后, 都会输出⼀ 个换行。 未完待续。

Java数据类型及运算符及数组(与C语言对比)

Java和C语言在数据类型大部分相同&#xff0c;但是也有不同 1.新增了byte类型&#xff08;相当于C语言中把char用作整数一样&#xff09; 2.然后就是char类型的大小改为了2字节。 3.布尔型改名为boolean而不是bool,且大小没有明确规定&#xff0c;方便进行不同平台之间的移…

使用dev_dbg调试

首先内核要使能两个配置才可以使用。一般内核都是打开的。 CONFIG_DEBUG_FSy CONFIG_DYNAMIC_DEBUGy 当编译选项CONFIG_DYNAMIC_DEBUG打开的时候&#xff0c;在编译阶段&#xff0c;kernel会把所有使用dev_dbg()的信息记录在一个table中&#xff0c;这些信息我们可以从/sys/k…

在线预览多类型文件_全栈

目录 一、下载运行项目 二、项目功能 三、前端项目引用 四、文件预览样式更改 在做项目时经常用到在线预览文件&#xff0c;给大家介绍一个好用的在线预览文件项目。使用技术是后端Java&#xff0c;前端Freemarker模板。 FreeMarker 特别适应与 MVC 模式的 Web 应用&#x…

从“产品的RFM分析”看如何探索“职业方向”

我们在做产品分析时&#xff0c;经常会用到一种方法“产品的RFM分析”&#xff0c;它是一种客户细分和价值评估的常用方法&#xff0c;广泛应用于电子商务、零售和其他众多行业&#xff0c;它可以帮助企业和产品团队更好地理解用户行为&#xff0c;优化营销策略&#xff0c;提升…

解禁日大涨,爱玛科技的投资前景值得信任吗?

6月17日&#xff0c;爱玛迎来6.28亿股、金额超190亿元的解禁&#xff0c;占总股本72.91%。不过&#xff0c;爱玛股价在巨量解禁中反而迎来涨势&#xff0c;因为这部分股票中&#xff0c;创始人张剑持有的限售股数量几乎就占了爱玛总股本的七成。某种意义上&#xff0c;市场认为…

【产品经理】订单处理4-拆单策略

上次讲解了订单的促销策略&#xff0c;本次讲解下订单处理过程中的拆单策略。 订单拆单策略分为自动拆单、手动拆单&#xff0c;拆单时机也分为订单未被审核前拆单、订单审核后因仓库/快递情况的拆单&#xff0c;本次主要讲解订单未被审核前拆单、订单审核后快递超重的拆单&am…

ollama模型CPU轻量化部署

一、定义 ollama 定义环境部署demo加载本地模型方法基本指令关闭开启ollamaollama 如何同时 运行多个模型, 多进程ollama 如何分配gpu修改模型的存储路径 二、实现 ollama 定义 ollama 是llama-cpp 的进一步封装&#xff0c;更加简单易用&#xff0c;类似于docker. 模型网址…

SFNC —— 标准特征命名约定(一)

系列文章目录 SFNC —— 标准特征命名约定&#xff08;一&#xff09; 文章目录 系列文章目录1、介绍1.1 约定&#xff08;Conventions&#xff09;功能名称和接口&#xff08;Feature Name and Interface&#xff09;功能类别&#xff08;Feature Category&#xff09;功能级别…