编写C#桌面窗体,通过任意日期的GPS广播星历文件,计算任意时刻、任意卫星的位置。
运行环境:Visual Studio 2022,.NET6.0,我的Visual Studio 2019无法正常运行该程序。
一、程序演示
GPS广播星历文件计算卫星位置程序演示视频
二、数据获取
1.下载GPS广播星历文件
我下载的是2024-04-23的GPS广播星历文件
2.解压文件,更改文件后缀为txt
3.广播星历文件的格式说明
下图中许多参数在之后的公式中会用到
注意文件有固定格式,记住每八行为一组数据,这很重要。
三、数学公式(编写多个函数)
需要注意:
参考时刻是卫星广播星历文件中的时刻
观测时刻可以是任意时刻,即所计算的时刻
该程序存在不足:默认。
可以根据需要自行编写和的转换代码。
1.计算卫星运动的平均角速度n
参考时刻的卫星平均角速度
观测时刻卫星的平均角速度
(rad/s)——卫星平均角速度的改正值(摄动参数)
为万有引力常数G与地球总质量M之积
——时的轨道长半径平方根
2.计算观测时刻卫星的平近点角
—— 时的平近点角
3.计算偏近点角,
用弧度表示的开普勒方程:
e——时的轨道偏心率
迭代法求解,令=代入公式,求出后再代入。由于轨道偏心率e很小,迭代收敛很快
迭代法代码:
do
{
double newE = Ms + e * Math.Sin(Es);
if (Math.Abs(newE - Es) < precision) // 检查收敛性
{
break; // 如果收敛,跳出循环
}
Es = newE; // 更新E的值
} while (true); // 无限循环,直到满足条件跳出
4.计算真近点角
5.计算升交角距
——时的近地点角距
6.对升交角距u、卫星矢径r、卫星轨道倾角i进行摄动改正
7.计算观测时刻升交点的经度L
8.计算卫星在轨道平面坐标系中的位置
9.计算卫星在(瞬时地球)WGS-84坐标系中的位置
四、算法实现
如果根据指定日期指定时刻指定卫星的广播星历计算卫星位置,理清思路后,并不难,就是有些麻烦,而且一不留意就容易出错,需要一直很仔细的编写代码。
如果要根据任意日期的GPS广播星历文件,计算任意时刻、任意编号卫星的位置的话,难度有点大。
1.算法思路
这是我编写时的思路。
(1)搭建窗体
(2)在广播星历文件中先截取6号卫星,固定时刻16时的8行数据放入新的txt文本文件,
(16时的6号卫星只是图个吉利。可以自行选取。),计算时刻为15:10:26。
个人感觉观测时刻离参考时刻越近,计算位置结果越精确。
(3)研究提取出来的八行数据的结构,新建SatellitePosition.cs类文件,在里面编写函数读取文件,遍历文件储存于数组,提取数据等多个函数,并用在Form.cs,RichTextBox控件呈现文件的内容。
在读取文件编写数据时,我先实现把地址写入代码读取文件,成功后,才尝试通过窗体Button打开文件的方式读取文件。
读取文件时,应当注意参数后面D-09等科学计数法的处理。需要用到正则表达式。
(4)编写多个函数用于计算卫星位置,上面数学公式,每一步编写一个函数,共计9个函数进行计算。计算时需要调用上一步编写的函数提取广播星历文件中参数。
因为计算函数需要用到之前的计算结果。
在实际运算中,为方便写出代码(由简单到困难),某些ComBox读取的时间等参数先写死在代码里,在运行成功后,为方便调试,又改为写死状态。
每写完一个函数,最好运行检查一下,看是否出错。可以将运算结果通过TextBox或MessageBox呈现出来。
(5)在Form.cs中调用SatellitePosition.cs中所有的与计算有关的函数。计算出15:10:26时6号卫星在WGS-84坐标系中的位置。把所有写死的数据改活。
上面已经实现算法最基本的计算功能。
读取八行数据的文本文件后,计算指定卫星,指定时刻,任意观测时间的卫星坐标。
观测时刻离参考时刻越近,计算位置结果越精确。因此如果输入的观测时间与指定时刻差距过大,结果会不精确。
接下来需要实现实现筛选功能。
读取任意广播星历文件,选择任意卫星,输入任意时刻,计算卫星位置。
我是单独实现筛选功能后,再与上面的计算功能连接在一起实现程序的编写。
(6)实现根据下拉框中选择的时间,在完整的广播星历文件中检索离所选取时间最近的时刻,并且显示到RichTextBox控件中。
原理是如果ComBox所选的时间为单数A,则检索文件中A+1时刻的数据,以八行作为一个模块,把所有数据写入列表。最后返回结果为数组。
(7)实现根据下拉框里选择的卫星编号,检索上一步返回的数组中的数据,得到卫星编号与下拉框中的编号一致的特定行,并将该行所在的八行数据写入列表。结果返回数组。最终RichTextBox控件只会显示八行数据。
(8)更改之前(3)中的代码,从文本中读取改为从(7)的返回数组中读取。
(9)编写函数,根据广播星历文件的日期计算该日是所在周的第几天。
public int GetDayOfWeekInWeek(string[] timeParts)
{
// 解析年份
int year = 2000 + int.Parse(timeParts[1]);
int month = int.Parse(timeParts[2]);
int day = int.Parse(timeParts[3]);
// 构造DateTime对象
DateTime date = new DateTime(year, month, day);
// 获取给定日期所在周的第一天
DayOfWeek firstDayOfWeek = DayOfWeek.Sunday;
// 获取给定日期是该周的第几天
int dayOfWeek = (int)date.DayOfWeek - (int)firstDayOfWeek;
if (dayOfWeek < 0)
{
// 将周天(默认为0)移到末尾
dayOfWeek += 7;
}
// 加1是为了将周天作为一周的第一天
dayOfWeek += 1;
days = dayOfWeek;
return days;
}
2.具体代码
代码仍存在不足之处,会在整理完善后上传
3.不足之处
(1)默认toc=toe;
(2)只能根据公元2000年后的GPS广播星历文件计算卫星位置。
在计算周内秒时(周日为一周的第一天),根据计算日期所在周第几天的函数代码为2000+广播星历文件提取出的日期(int year = 2000 + int.Parse(timeParts[1]);),因此只能利用公元2000-2999年的广播星历文件计算卫星位置。
(3)由于广播星历文件的卫星时刻会是1时59等非整数时刻,在筛选最近时刻时存在误差。
可以通过在检索时间的同时,添加条件:检索到单数与文件中时刻一样时,以单数时刻为准,而不是单数加一后的双数时刻。