卫星系统算法课程设计 - 第一部分:城市时间窗口、间隙等

发布于:2023-01-04 ⋅ 阅读:(599) ⋅ 点赞:(0)

  先看我上传的视频,看看是否是你们想要的效果:

【算法课设】 卫星系统 运行情况存档_哔哩哔哩_bilibili

【算法课设】存档2_哔哩哔哩_bilibili

注意:视频中没有画出地球那种越靠极点纬度线越短的情况,但面积计算是没有问题的,因为画图和计算分离,图只是可视化,但面积等数据用的是微积分。

1、问题背景

  当前,卫星移动通信、气象预报、遥感探测、军事侦察、资源勘探、灾害监
测、导航定位等多个领域发挥着越来越重要的作用。而且,在一般的航天任务应
用中,我们往往需要使用一群卫星共同来完成任务。将一群卫星的集合,称作卫
星星座。
  在每个时刻,卫星的服务范围可以近似成一个圆锥形的区域。该锥形区域在
地面上的投影为球面圆形的(注:球面圆,不是平面圆)。如果该时刻,地面目标
在该圆形区域内,则卫星可以对该地面目标提供服务,称作“地面目标在该时刻
被卫星覆盖”。

2、第一个问题----计算卫星与目标的时间窗口

  卫星对一个地面目标的时间窗口,是指在仿真时间内,卫星对目标可以提供
服务的时间段的集合。时间窗口可以表示为一个时间区间,在该区间内卫星能够
对目标提供服务,而不在该时间区间内,卫星不能对目标提供服务。
卫星星座对一个地面目标的时间窗口,为星座中每颗卫星对目标时间窗口的
并集。
完成如下计算:
(1) 对以下每个目标,计算星座对其时间窗口。同时,统计时间窗口的最
大值、最小值与累积值。对每个目标,计算其时间间隙,并统计时间间隙的最大
值与累积值。
目标的数据如下:

 

(2) 在经度 75°E-135°E,纬度范围 0°N-55°N 的区域范围内,计算每
个坐标的时间间隙的最大值、时间间隙的和,以及时间间隙平方和三个指标,并 将结果分别以可视化形式展示出来,经纬度的离散化精度可调节,最小需要考虑
到 0.1°。
2.1、问题一分析
  据我所知,每年的数据是不一样的,如果有人在做此课设,写代码时需要注意自己写的城市个数与每个城市的名称地点位置。
2.1.1、数据预处理
  老师给我们的数据包含了9个卫星在一天之内每一秒的位置信息,这些点也就组成了该卫星服务的“圆”(看作是平面圆,之后说到的矩形也是如此,但计算面积是要用微积分)区域。
  那么我们可以想到,有城市位置,有9个圆的位置,那是不是把这个城市与某秒9个圆的位置进行计算,看看是否在圆内,如果9个圆都不覆盖此城市,那说明该秒时这个城市不被服务。
  但这里遇到一个问题:每一秒每个卫星都有21个点(没记错的话),那数据量是不是有点太大了。所以我们拿到题目不要上来就刷刷写代码,没什么luan用,先分析问题。
  那么我们可以想到,能不能找两个点,这两个点正好是该秒时21个点的最高点与最低点,这样就可以得到圆直径,然后两个点的中点就是圆心。
  但我发现以前的学长用的就是这个方法,你们应该可以找得到。那么我们要有所创新,不要和别人一样,而且,只要两个点其实误差会稍大(虽然估计大不了千分之一)。那么我将给出新的方法
* 数据预处理---取圆上3个点通过克莱姆法则求解半径与圆心 
   我们在选取最高点和最低点的情况下,再选取一点,你们看那个学长的文章应该能知道最高和最低点,然后我们在要最左边点或最右边点,自己去看一下是第几个点。 然后看下图
  

 (本图取自本人的汇报ppt,希望对你们有帮助)

接下里将放出数据预处理代码,这些代码是使用c++写的,请自己用c++开一个空项目,将下列代码放入cpp中,把老师给的卫星数据放入项目文件,运行就能得到结果。目的是将卫星信息简化,将每个卫星每一秒的中心位置或者说圆心位置写入文件中。那么文件应该包含86400个经度纬度信息。

#include<iostream>
#include<cmath>
#include<fstream>
#include <stdio.h>
#include<stdlib.h>
#include<string>
#include<vector>
#include <typeinfo>//typeid
using namespace std;

double Radius[86400];//用于临时保存每一秒对应的半径
double test1[10];
double aveRadius[9];



double pingJun(double* radius)
{
	double cul = 0;
	for (int i = 0; i < 86400; i++)
	{
		cul += radius[i];
	}
	//f
	return (cul / (double)86400);
}

double HangLieShi(double a11, double a12, double a21, double a22)
{
	return (a11 * a22 - a12 * a21);
}
class myPoint
{
public:
	myPoint()
	{
		x = 0, y = 0;
	}
	myPoint(double x0, double y0) { x = x0; y = y0; }
	myPoint(myPoint& P) { x = P.x; y = P.y; }
	myPoint& operator=(const myPoint& P)
	{
		x = P.x;
		y = P.y;
		return *this;
	}
	~myPoint() {}
	void set(double x0, double y0)
	{
		x = x0;
		y = y0;
	}
	double x, y;
};
myPoint Position[9][86400];//第i个卫星的第j秒的圆心
class Line
{
public:
	Line(myPoint a0, myPoint b0) :a(a0), b(b0)
	{
		if (a.x == b.x) 
		{ 
			A = 0; 
			B = 1; 
			C = (a.y + b.y) / 2; 
		}
		else if (a.y == b.y) 
		{ 
			A = 1; 
		    B = 0; 
	     	C = (a.x + b.x) / 2; 
		}
		else
		{
			A = 1;
			B = (a.x - b.x) / (a.y - b.y);
			C = (a.x + b.x) / 2 + B * (a.y + b.y) / 2;
		}
	}
	~Line() {}
	myPoint a, b;
	double A, B, C;
};


class Circle
{
public:
	Circle()
	{
	}
	Circle(myPoint a0, myPoint b0, myPoint c0) :a1(a0), b1(b0), c1(c0) {}
	~Circle() {}
	double calculation(int number, int time);//计算圆心,半径
	void reSet(myPoint&a0, myPoint&b0, myPoint&c0)
	{
		a1 = a0; b1 = b0; c1 = c0;
	}
protected:
	myPoint a1, b1, c1;
	double x0, y0, R;
};
//number对应卫星编号,time即时刻,要将每个卫星每时刻对应的圆心坐标进行保存
double Circle::calculation(int number, int time)
{
	Line l1(a1, b1), l2(a1, c1);
	double J;
	J = HangLieShi(l1.A, l2.A, l1.B, l2.B);
	//简单来说呢,就是克莱姆法则求解方程
	//如果两个直线不同,说明可以使用克莱姆法则求解
	if (J)
	{
		//将向量的常量代替矩阵对应的值,求出圆心
		x0 = HangLieShi(l1.C, l2.C, l1.B, l2.B) / J;
		y0 = HangLieShi(l1.A, l2.A, l1.C, l2.C) / J;
		Position[number][time].set(x0, y0);
		//使用圆方程解半径
		R = sqrt((a1.x - x0) * (a1.x - x0) + (a1.y - y0) * (a1.y - y0));
	}
	else
	{
		return -1;
	}
	return R;
}


//读文件函数
void Read(int number, double &radius)
{
	ifstream  ifs;//流对象
	//读文件0-8
	if (0 <= number && 9 > number)
	{
		switch (number)
		{
		case 0:
		{
			ifs.open("SatCoverInfo_0.txt");
			break;
		}
		case 1:
		{
			ifs.open("SatCoverInfo_1.txt");
			break;
		}
		case 2:
		{
			ifs.open("SatCoverInfo_2.txt");
			break;
		}
		case 3:
		{
			ifs.open("SatCoverInfo_3.txt");
			break;
		}
		case 4:
		{
			ifs.open("SatCoverInfo_4.txt");
			break;
		}
		case 5:
		{
			ifs.open("SatCoverInfo_5.txt");
			break;
		}
		case 6:
		{
			ifs.open("SatCoverInfo_6.txt");
			break;

		}
		case 7:
		{
			ifs.open("SatCoverInfo_7.txt");
			break;
		}
		case 8:
		{
			ifs.open("SatCoverInfo_8.txt");
			break;
		}
		default:
		{
			return;
		}

		}
	}
	else
	{
		return;
	}
	//ifs.open("SatCoverInfo_0.txt");
	if (!ifs)
	{
		cout << "读取失败" << endl;
		return ;
	}
	//打开指定文件
	int getLineNum = 22;//每次读取22行
	double x = 0, y = 0;
	char name[20];
	myPoint a, b, c;
	Circle A;
	//读文件。
	for (int j = 0; j < 86400; j++)
	{
		for (int i = 0; i < getLineNum; i++)
		{
			if (i == 0)
			{
				ifs.getline(name, 20);
				continue;
			}
			ifs >> x >> y;
			if (i == 1)
			{
				a.set(x, y);
			}
			else if (i == 6)
			{
				b.set(x, y);
			}
			else if (i == 11)
			{
				c.set(x, y);
			}
			else if (i == 21)
			{
				ifs.getline(name, 20);
			}
		}
		A.reSet(a, b, c);
		Radius[j] = A.calculation(number, j);
	}
	ifs.close();
	radius = pingJun(Radius);
}

void Write(int number)
{
	ofstream  ofs;//流对象
	//读文件0-8
	if (0 <= number && 9 > number)
	{
		switch (number)
		{
		case 0:
		{
			ofs.open("Position_0.txt");
			break;
		}
		case 1:
		{
			ofs.open("Position_1.txt");
			break;
		}
		case 2:
		{
			ofs.open("Position_2.txt");
			break;
		}
		case 3:
		{
			ofs.open("Position_3.txt");
			break;
		}
		case 4:
		{
			ofs.open("Position_4.txt");
			break;
		}
		case 5:
		{
			ofs.open("Position_5.txt");
			break;
		}
		case 6:
		{
			ofs.open("Position_6.txt");
			break;

		}
		case 7:
		{
			ofs.open("Position_7.txt");
			break;
		}
		case 8:
		{
			ofs.open("Position_8.txt");
			break;
		}
		default:
		{
			return;
		}

		}
	}
	else
	{
		return;
	}
	//是否打开成功
	if (!ofs)
	{
		cout << "读取失败" << endl;
		return;
	}

	for (int j = 0; j < 86400; j++)
	{
		ofs << Position[number][j].x << "         " << Position[number][j].y<<endl;
	}
}

int main()
{
	
	for (int i = 0; i < 9; i++)
	{
		Read(i, aveRadius[i]);
		Write(i);
	}
	for (int i = 0; i < 9; i++)
	{
		cout << "第" << i<< "个卫星区域平均半径为:" << aveRadius[i] << endl;
	}
	//一下的注释代码是最开始使用来求平均半径的
	//Read(3, aveRadius[3]);
	//Write(3);
	//cout << "平均半径为:" << aveRadius[3] << endl;
	return 0;
}

  那么以上,我们就可以把第0到第8个卫星的位置信息写入文件中,第0个卫星的文件是Position_0,第1是Position_1,后面同理。

  然后这里我直接给出9个卫星的平均半径,之后在qt中直接用这个半径作为每个卫星的半径就可以了。卫星服务区的半径是:7.0068

2.2、求城市时间窗口与时间间隙

  时间窗口,比如0 - 86399秒内,第1秒-第400秒该城市被卫星服务(只要被9个卫星的其中一个服务就是被服务),那么产生一个时间窗口[1,400],那么第0秒和第401秒是不被服务的。

  我下面再讲的仔细些,包含代码,先写城市City类,成员变量有:城市名称、经度、纬度、时间窗口的QVector。然后写一个时间窗口TimeSlot类,成员变量:startTime、overTime、length。

[startTime,overTime],length = overTime - startTime + 1。

然后卫星的类Satellite,成员变量:double *x,double *y。

qt的代码如下:

city.h

#ifndef CITY_H
#define CITY_H

#include"timeslot.h"
#include<QVector>
//这是一个城市的类
class City
{

public:
    double longitude;//城市的经度
    double latitude;//城市的纬度
    QString name;//城市的名字
    QVector<TimeSlot> slot;//该城市的时间窗口,后面会计算,出现一个窗口时候就会push进去一个窗口

public:
    City();

};

//保存城市的表
class CityList
{
public:
    City city[19];//一共有19个城市
    //构造函数
    CityList();
    int location(QString n);//传入名字,定位

};



#endif // CITY_H

city.cpp

#include "city.h"

City::City()
{
    this->longitude = 0;
    this->latitude = 0;
    name = "";
}


CityList::CityList()
{
    city[0].name = "奥克兰";
    city[0].longitude = 237.87;//-122.13
    city[0].latitude = 37.47;
    //
    city[1].name = "敖德萨";
    city[1].longitude = 30.46;
    city[1].latitude = 46.3;
    //
    city[2].name = "冈山";
    city[2].longitude = 133.54;
    city[2].latitude = 34.4;
    //
    city[3].name = "俄克拉荷马城";
    city[3].longitude = 262.68;//-97.32
    city[3].latitude = 35.29;
    //
    city[4].name = "鄂木斯克";
    city[4].longitude = 55;
    city[4].latitude = 73.22;
    //
    city[5].name = "奥拉涅斯塔克";
    city[5].longitude = 290.42;//69.58
    city[5].latitude = 12.3;
    //
    city[6].name = "奥拉多";
    city[6].longitude = 278.78;//-81.22
    city[6].latitude = 28.3;
    //
    city[7].name = "大阪";
    city[7].longitude = 135.3;
    city[7].latitude = 34.4;
    //
    city[8].name = "奥斯陆";
    city[8].longitude = 10.41;
    city[5].latitude = 159.56;
    //
    city[9].name = "渥太华";
    city[9].longitude = 284.57;//-75.43
    city[9].latitude = 45.25;
    //
    city[10].name = "瓦拉杜古";
    city[10].longitude = 358.6;//-1.4
    city[10].latitude = 12.2;
    //
    city[11].name = "帕果帕果";
    city[11].longitude = 189.58;//-170.42
    city[11].latitude = -14.16;
    //
    city[12].name = "巨港";
    city[12].longitude = 104.5;
    city[12].latitude = -2.59;
    //
    city[13].name = "波赫恩";
    city[13].longitude = 158.1;
    city[13].latitude = 6.55;
    //
    city[14].name = "帕尔马";
    city[14].longitude = 2.39;
    city[14].latitude = 39.26;
    //
    city[15].name = "巴拿马";
    city[15].longitude = 280.7;//-79.3
    city[15].latitude = 8.57;
    //
    city[16].name = "帕皮提";
    city[16].longitude = 210.66;//
    city[16].latitude = -17.32;
    //
    city[17].name = "帕拉马里博";
    city[17].longitude = 304.86;//55.14
    city[17].latitude = 5.52;
    //
    city[18].name = "巴黎";
    city[18].longitude = 2.2;
    city[18].latitude = 48.51;
}


int CityList::location(QString n)
{
    for(int i = 0; i < 19; i++)
    {
        if(city[i].name == n)
        {
            return i;
        }
    }
    return -1;
}




timeslot.h

#ifndef TIMESLOT_H
#define TIMESLOT_H


//如题,这是一个时间段的类,用于保存各城市各时间窗口的值(开始时间、结束时间、时间长度)
class TimeSlot
{
public:
    int startTime;
    int overTime;
    int length;
public:
    TimeSlot();
    TimeSlot(int s, int o);
    void set(int start, int over);
};

//时间间隙
class TimeGap
{

};

#endif // TIMESLOT_H

timeslot.cpp

#include "timeslot.h"

TimeSlot::TimeSlot()
{
    startTime = 0;
    overTime = 0;
    length = 0;
}
TimeSlot::TimeSlot(int s, int o)
{
    startTime = s;
    overTime = o;
    length = overTime - startTime + 1;
}

void TimeSlot::set(int start, int over)
{
    startTime = start;
    overTime = over;
    //[1, 5]的长度是 5 - 1 + 1
    //对于区间[l, r],长度为 r - l + 1
    length = overTime - startTime + 1;

}

satellite.h

#ifndef SATELLITE_H
#define SATELLITE_H

//卫星的类,保存半径与中心坐标,但由分析情况来看,半径已经不需要了
class Satellite
{
public:
    double *x;
    double *y;
public:
    Satellite();
    ~Satellite();


};

#endif // SATELLITE_H

satellite.cpp

#include "satellite.h"

Satellite::Satellite()
{
    x = new double[86400];
    y = new double[86400];
}

Satellite::~Satellite()
{
    delete []x;
    delete []y;
}

2.2.1、时间窗口求解方法

mianwindow.cpp中的,主要看方法,代码的注释非常超级终极无敌tm详细,我认为我说的再多用处也不大,不如你们好好看看这个代码。

解释:设置bool类型的变量flag,一开始设置为false 若某一刻城市被服务,将flag置为true。 若某一刻不被服务,同时flag为true,说明产生了一个时间窗口 通过timeSlot的构造函数创建对象,将其 push_back到该城市的vector<timeSlor>容器中。同时flag置为false。

然后我此处只求了时间窗口,还有时间间隙呢,那么时间间隙我不太懂意思,有两种解释。

一:城市不被服务的时间段就是时间间隙。

二、两个时间窗口之间产生一个时间间隙。

请自己体会,我在课设中用的是第二种解释,你们可以去问老师到底是哪种。

只要弄明白下列代码,基本没什么问题了,时间间隙大不了你们自己写一个timeGap,或者通过方法用窗口求间隙,很简单的。当时我就是懒得再写一个timeGap了,现在想想,确实写一个会比较好。

  时间窗口最大最小值、累计值就不要多说了吧?你好好想想TimeSlot里我为什么要写length,tnn的遍历一遍这个城市的QVctor<TimeSlot>容器不就有最大最小和累计了吗!!!!

间隙同理哈。

//读取卫星信息
void MainWindow::ReadFile(int number)
{
    ifstream ifs;
    switch (number)
    {
    case 0:
    {
        ifs.open("D:/vc/z/myEarth/Position_0.txt");
        break;
    }
    case 1:
    {
        ifs.open("D:/vc/z/myEarth/Position_1.txt");
        break;
    }
    case 2:
    {
        ifs.open("D:/vc/z/myEarth/Position_2.txt");
        break;
    }
    case 3:
    {
        ifs.open("D:/vc/z/myEarth/Position_3.txt");
        break;
    }
    case 4:
    {
        ifs.open("D:/vc/z/myEarth/Position_4.txt");
        break;
    }
    case 5:
    {
        ifs.open("D:/vc/z/myEarth/Position_5.txt");
        break;
    }
    case 6:
    {
        ifs.open("D:/vc/z/myEarth/Position_6.txt");
        break;

    }
    case 7:
    {
        ifs.open("D:/vc/z/myEarth/Position_7.txt");
        break;
    }
    case 8:
    {
        ifs.open("D:/vc/z/myEarth/Position_8.txt");
        break;
    }
    default:
    {
        return;
    }
    }
    //将每一秒的信息读入
    for(int i = 0; i < 86400; i++)
    {
        ifs>>satellite[number].x[i]>>satellite[number].y[i];//读入第number个卫星的第i时刻的位置信息
    }
    ifs.close();
}
//求平方
double culSquare(double a)
{
    return a*a;
}


//请在mainwindow的构造函数中执行sInit(),初始化,这样我们一运行就把所有城市的时间窗口求完了
void MainWindow::sInit()
{
    //其实半径甚至就是一样
    /*
    satellite[0].radius = 7.00681;
    satellite[1].radius = 7.00679;
    satellite[2].radius = 7.00681;
    satellite[3].radius = 7.0068;
    satellite[4].radius = 7.0068;
    satellite[5].radius = 7.00682;
    satellite[6].radius = 7.00679;
    satellite[7].radius = 7.0068;
    satellite[8].radius = 7.00682;
    */
    //一个循环  每个卫星每秒钟对应的x ,y读进来
    for(int i = 0; i < 9; i++)
    {
       ReadFile(i);
    }
    int start = 0;
    //将各城市的时间窗口计算出来,外循环遍历城市
    for(int i = 0; i < 19; i++)
    {
        bool flag = false;//记录城市是否被卫星星座覆盖
        //中间循环遍历时间
       for(int j = 0; j < 86400; j++)
       {
           //内层循环遍历卫星,时间复杂度较高。
           int number =0;//当前时间段未提供服务的卫星个数
           for(int k = 0; k < 9; k++)
           {
               //判断城市是否在该卫星的搜索范围内,看方程(x-x0)*(x-x0) + (y-y0)*(y-y0)是否小于等于radius
               //第i个城市的x-该卫星的x,即经度longitude,y即纬度latitude
               //第k个卫星在第j秒的数值
               if((culSquare(theCityList.city[i].longitude - satellite[k].x[j]) + culSquare(theCityList.city[i].latitude - satellite[k].y[j])) <= culSquare(radius))
               {
                   //如果该时间段处在星座范围内,且flag标记false,说明此时刻开始进入星座的范围
                   if(!flag)
                   {
                       start = j;//j就是当前时间
                       flag = true;//标记被覆盖
                   }
                   break;//退出当前卫星循环,因为只要有一个卫星提供就算星座在提供
               }
               //执行到这一步,说明此卫星没有提供服务,Number++
               number++;
               //若9个
              if(flag && number == 9)
              {
                  //此时不再星座内,但是flag却为true,说明有了一个时间窗口出现
                  //传入start,和结束时间j - 1
                  TimeSlot t(start, j - 1);
                  //把tpush给该城市的时间窗口VEctor
                  theCityList.city[i].slot.push_back(t);
                  //然后把flag置为false
                  flag = false;
              }

           }
       }
    }

    /*
    ifstream ifs1;
    ifs1.open("D:/vc/z/myEarth/us.txt");
    for(int i = 0; i < 3369; i++)
    {
        ifs1>>point[i].longitude>>point[i].latitude;
    }
    ifs1.close();
    */
}

3、最后

最后一篇文章我会把全部资源发到百度云,里面包含克莱姆法则的预处理、最重要的qt源程序本体、还有美国多边形的分割。如果你们现在弄的不是美国,也不要紧张,我会把这种类似问题的方法讲解的一清二楚。不过我看学长弄的是求区域时间间隙最小最大点啥的,用了粒子群优化算法那个,我怀疑你们应该搞的是别的。不过我还是会把这种某国、某区域多边形的分割方法放出来,万一你们今年搞的是什么英国、澳大利亚啥的呢hh。

本文含有隐藏内容,请 开通VIP 后查看