导航时间与坐标转换


前言:

该章节代码均在Gitee中开源:
因为这章是学校作业,所以稍微正经点. 

时空位置转换icon-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%AF%BC%E8%88%AA%E6%97%B6%E7%A9%BA%E8%BD%AC%E6%8D%A2/time-space_transform


时间

在时间系统中,一般分为三种时间:

  • 通用时,便是我们日常生活中使用的时间
  • GPS时,便是GPS卫星时间钟产生的时间
  • unix时,在unix系统中,时间戳开始的初始时间到现在的时间戳总数

通用时

通用时(common time),就是我们日常生活中使用的时间。日常生活中的时间包含什么?

年月日 时分秒

所以通用时的定义也是如此:

struct commontime
{
    commontime()
    {}

    commontime(vector<unsigned> data, double second)
        :_year(data[0]),
        _month(data[1]),
        _day(data[2]),
        _hour(data[3]),
        _minute(data[4]),
        _second(second)
    {}

    unsigned _year;
    unsigned _month;
    unsigned _day;
    unsigned _hour;
    unsigned _minute;
    double _second;
};

GPS时

GPS时相对来说就比较陌生了。我们来回忆一下,日月年是怎么定义的?

  • 日,地球自转一周,叫做一天
  • 月,月亮绕地球转一周,叫做一月
  • 年,地球绕太阳公转一周,叫做一年

我们使用的一周是多久?7天。但是,日月年我们都知道,却从未想过为何会有周这个概念。
其实,这里的周便是GPS周,GPS一周为7天,当七天一过,计数器就从0开始计数,
就如同我们每一分钟是60秒一样,60秒再过一秒,就计数成了一分一秒而非61秒;
GPS周到了604800秒(7天*24小时*60分钟*60秒)后再过一秒,就成了一周一秒。

所以,GPS周由两部分构成:

  1. 周数
  2. 一周内的秒数(范围0~604800)

但是,和通用时也有所不同,通用时的计数是从公元0年0时0分0秒开始的;GPS时却是从1980年0月0日0时0分0秒开始计时的,也就是在1980年0月0日0时0分0秒的时候,此时卫星上的计数器为:

struct GPStime
{
    GPStime()
    {}

    GPStime(unsigned week,double second)
        :_week(week),
        _second(second)
    {}

    unsigned _week;//周数
    double _second;//一周内的秒数,叫做周内秒
};

Unix时

经常深究游戏机制的人都知道,有个概念叫时间刻
啥意思?

计算机里的所有随机都是伪随机。通过给计算机一个种子,计算机就会根据这个种子,用固定的算法生成一个序列,这个序列就是伪随机序列。打个比方:

伪随机数列是一个数组:

对每一个数组中的元素,都有一个固定的算法:

比如:第一空的算法为f(x)=x^2+5x

我们在给定种子为x=2的时候,第一个空的数据就是f(2)=14

同理,一直填满所有的空

而当我们想取随机数的时候,就按顺序依次从这个数组里取,

比如我们在玩掷硬币,假设为奇数是正面,偶数是反面

那么第一次掷硬币,我们取第一个元素,14,为反面

第二次掷硬币,我们取第二个元素,g(2)

过了很长时间,我们再掷第三次硬币,只要我们不重新设置种子,还是从上一次的位置开始往下取,即取第三个元素h(2)

但是,我们很容易发现,每个种子 ,因为算法是固定的,2对应的f(2),g(2),h(2)永远不变,所以只要种子相同,最后产生的伪随机序列也是相同的。而这就不叫伪随机了,这就变成了可预测的随机,所以为了满足伪随机的要求,我们必须要找到一个永远在变化的数来当种子。

对,就是时间,我们把时间设成种子。但是, 时间不管是年月日还是几周几秒都不是一个整数,把时间变成整数最简单的方法,就是过一秒,数+1,不去向周或者分钟小时进位,最后会得到一个非常大的整数,这个整数,就叫时间刻

所以unix时是什么?就是从1970年0月0日0时0分0秒开始, 计数器为0,过一秒加1,一直加到现在的总秒数。

因此,unix时只由秒数构成。但是,为了让他更细一点,为他加入了一秒内的毫秒数
不过,在Linux中,时间刻的类型就是using time_t = long,仅此而已。

struct gtime_t
{
    gtime_t()
    {}

    gtime_t(time_t time,double second)
        :_time(time),
        _second(second)
    {}

    time_t _time;
    double _second;
};

三种时间的相互转换

首先,我们必须要弄清楚,每个时间转换成其他时间的具体函数是什么吗?
当然不必,因为,完全可以用一个时间作为中间变量,将所有时间无论类型都转换成这个中间变量,然后向其他转换。

打个比方,正常情况下,写出每个时间转换成其他时间的具体函数,需要2x3=6个函数。
但是,如果我们知道通用时转换成unix时的函数,又知道unix时转换为GPS时的函数,那么,通用时转换为GPS时就可以先转换unix时,再通过unix时转换成GPS时,这是典型的函数复用

gtime_t ComToUnix(const commontime& com);//已经实现
GPStime UnixToGPS(const gtime_t& gt)//已经实现

GPStime ComToGPS(const commontime& com)
{
    gtime_t tmp = ComToUnix(com);
    GPStime ret = UnixToGPS(tmp);
    return ret;
}//直接函数复用

在这里,我们把Unix时设为这个中间变量。 

其次,还要明晰一个概念:三种时间关系的对应是绝对一对一固定的。
啥意思?

比如我现在是1980年0月0日0时0分0秒,这是通用时。此时GPS时是0周0秒,Unix时是315964800;
而此时时间过了一秒,通用时是1980年0月0日0时0分1秒,GPS时是0周1秒,Unix时是315964801;
之后,再不管过多长时间,通用时,GPS时,Unix时所加的时间是相同的。用专业的话说,三种时间的映射是唯一的,用人话说,只要告诉了我一个时间,其他两个时间一定是固定的,所以只要有一个时间,就能把另外两个时间求出来。

所以,我们不妨定义一个类,把所有的时间类型都包含进来

class atime //all time
{
    commontime _com;
    GPStime _gps;
    gtime_t _gt;
}

所以,构造函数可以写三个,当我们传入一种时间的时候,另外两个时间在构造函数中可以直接构造出来:

atime()
{}

atime(commontime& com)
    :_com(com)
{
    _gt = ComToUnix(_com);
    _gps = UnixToGPS(_gt);
}

atime(GPStime& gps)
    :_gps(gps)
{
    _gt = GPSToUnix(_gps);
    _com = UnixToCom(_gt);
}

atime(gtime_t& gt)
    :_gt(gt)
{
    _com = UnixToCom(_gt);
    _gps = UnixToGPS(_gt);
}

最后,通用时时间日期的加减就不多说了,直接从网上copy一份date的类吧,在我的gitee里也有

over,准备工作就做好了,那具体的时间转换怎么办?

把所有的时间转换为Unix时

通用时转换为unix时

  1. unix时是从1970年0月0日0时0分0秒到现在的时间,那么首先肯定要算出,现在到unix时的起点有多少天
  2. 算出多少天之后,用天数乘以一天的秒数,就是日期转换为秒数
    time1=day*86400
  3. 然后再来看一天以内的时分秒。时分秒的转换就是常识了:
    time2=hour*3600+minute*60+second
  4. 两者相加,就是unix时的时间刻
    gtime=time1+time2

#define DAY_SECOND 86400//一天的秒数

gtime_t ComToUnix(const commontime& com)
    {
        gtime_t ret;
        int day = Date(com._year, com._month, com._day) - Date(1970, 1, 1);
        ret._time = day * DAY_SECOND + com._hour * 3600 + com._minute * 60 + (time_t)floor(com._second);
        ret._second = com._second - floor(com._second);
        return ret;
    }

 GPS时转换为unix时

unix时和GPS时起点相差的秒数,为1980年与1970年相差的秒数,可以让计算机算,但是因为时间是固定的,不如直接定义出来:

#define UG_GAP 315964800//unix时和GPS时起点相差的秒数

然后,和通用时转换为unix时是一样的。

  • 首先,算出周数,然后周数乘以7,就是相差的天数。就对应了通用时里的time1
  • 然后,算出一天以内的秒数,就对应了通用时里的time2
  • 最后,两者相加,就是gtime 的时间刻

gtime_t GPSToUnix(const GPStime& gps)
    {
        gtime_t ret;
        ret._time = gps._week * 7 * DAY_SECOND + (time_t)gps._second + UG_GAP;
        ret._second = gps._second - (int)gps._second;

        return ret;
    }

把unix时转换为其他时间

unix时转换为通用时

这便是一个逆过程,那么求法也是逆过来就可以了。

  1. 首先,用时间刻除以一天的秒数,可以算出天数
  2. 然后,时间刻就被分为了两个部分:
    天数
    一天内的秒数
  3. 关于天数,我们直接进行Date的加减,可以得到通用时的具体哪一天
  4. 关于一天内的秒数,我们采用和求天数相同不断除余的处理方法,一直求小时,分钟和秒

commontime UnixToCom(const gtime_t& gt)
    {
        commontime ret;
        ret._day = (int)(gt._time / DAY_SECOND);
        int second = (int)(gt._time - ret._day * DAY_SECOND);
        ret._hour = second % 3600;
        ret._minute = (second - ret._hour * 3600) % 60;
        ret._second = (second - ret._hour * 3600 - ret._minute * 60) % 60;

        Date tmp = Date(1970, 1, 1) + ret._day;
        ret._year = tmp._year;
        ret._month = tmp._month;
        ret._day = tmp._day;

        return ret;
}

unix时转换为GPS时

一样,首先考虑unix时和GPS时的起点差。

然后,用减去时间差后的秒数除以一周内的秒数,可以得到周数。而除余的数,就是一周内的周内秒

GPStime UnixToGPS(const gtime_t& gt)
    {
        GPStime ret;
        double sec = gt._time - UG_GAP;
        ret._week = (int)(sec / ROUND_SECOND);
        ret._second = (double)(sec - ret._week * ROUND_SECOND) + gt._second;

        return ret;
    }

时间汇总代码:

#define DAY_SECOND 86400//一天的时间
#define ROUND_SECOND 604800//一周的时间
#define UG_GAP 315964800//Unix时起点到GPS时起点

using namespace std;
typedef long long time_t;

struct commontime
{
    commontime()
    {}

    commontime(vector<unsigned> data, double second)
        :_year(data[0]),
        _month(data[1]),
        _day(data[2]),
        _hour(data[3]),
        _minute(data[4]),
        _second(second)
    {}

    unsigned _year;
    unsigned _month;
    unsigned _day;
    unsigned _hour;
    unsigned _minute;
    double _second;
};

struct GPStime
{
    GPStime()
    {}

    GPStime(unsigned week,double second)
        :_week(week),
        _second(second)
    {}

    unsigned _week;
    double _second;
};

struct gtime_t
{
    gtime_t()
    {}

    gtime_t(time_t time,double second)
        :_time(time),
        _second(second)
    {}

    time_t _time;
    double _second;
};

//总的时间系统
//当传入一个时间之后,自动同步另外两个时间
class atime
{
public:
    atime()
    {}

    atime(commontime& com)
        :_com(com)
    {
        _gt = ComToUnix(_com);
        _gps = UnixToGPS(_gt);
    }

    atime(GPStime& gps)
        :_gps(gps)
    {
        _gt = GPSToUnix(_gps);
        _com = UnixToCom(_gt);
    }

    atime(gtime_t& gt)
        :_gt(gt)
    {
        _com = UnixToCom(_gt);
        _gps = UnixToGPS(_gt);
    }

    static gtime_t ComToUnix(const commontime& com)
    {
        gtime_t ret;
        int day = Date(com._year, com._month, com._day) - Date(1970, 1, 1);
        ret._time = day * DAY_SECOND + com._hour * 3600 + com._minute * 60 + (time_t)floor(com._second);
        ret._second = com._second - floor(com._second);
        return ret;
    }

    static gtime_t GPSToUnix(const GPStime& gps)
    {
        gtime_t ret;
        ret._time = gps._week * 7 * DAY_SECOND + (time_t)gps._second + UG_GAP;
        ret._second = gps._second - (int)gps._second;

        return ret;
    }

    static commontime UnixToCom(const gtime_t& gt)
    {
        commontime ret;
        ret._day = (int)(gt._time / DAY_SECOND);
        int second = (int)(gt._time - ret._day * DAY_SECOND);
        ret._hour = second % 3600;
        ret._minute = (second - ret._hour * 3600) % 60;
        ret._second = (second - ret._hour * 3600 - ret._minute * 60) % 60;

        Date tmp = Date(1970, 1, 1) + ret._day;
        ret._year = tmp._year;
        ret._month = tmp._month;
        ret._day = tmp._day;

        return ret;
    }

    static GPStime UnixToGPS(const gtime_t& gt)
    {
        GPStime ret;
        double sec = gt._time - UG_GAP;
        ret._week = (int)(sec / ROUND_SECOND);
        ret._second = (double)(sec - ret._week * ROUND_SECOND) + gt._second;

        return ret;
    }

    const commontime& GetCommontime()
    {
        return _com;
    }

    const GPStime& GetGPStime()
    {
        return _gps;
    }

    const gtime_t& GetGtime()
    {
        return _gt;
    }
private:
    commontime _com;
    GPStime _gps;
    gtime_t _gt;
};

为了方便大家测试,给一个时间计算的网站:

日期计算器 (time.org.cn)icon-default.png?t=N7T8https://time.org.cn/riqi/当然,肯定不是我写的。


空间

对于导航的空间系统,太具体的划分就不多说了,大体可以分为两大类:

  • 空间坐标系,就是xyz系
  • 大地坐标系,BLH系,就是我们常说的纬度,经度,大地高。 

大地坐标系可能因为在生活中用的比较少,所以在这里简单再说说。

经度和纬度

经纬度,如果记不住的话,就想想平时经常用的词:
北纬,东经

所以,这样看就很明显了:把地球看作一个球体,从正面看过去,纬度就是南北方向,经度就是东西方向。但是,经纬度一定得有个起点,那么哪里是经纬度0度方向呢?

纬度很好理解,纬度的0度起点就是赤道,往南为南纬0~90度,往北为北纬0~90度。南90度为南极,北90度为北极,这是生活中常用的。

但是,经度起点就很少听说了。经度的起点在本初子午线,说人话就是英国有个格林尼治天文台,在国际上以这个天文台的原地址为经度的起点,也就是0度,然后一直转回这个天文台形成一个360度的球。

大地高

如果把地球看作一个标准的椭球体,即参考椭球体
那么在这个椭球体上,每一个点都可以做一个唯一的法平面,并且方程很容易就可以求出来。
而因为知道法平面方程,又知道地球上的某一个点的具体坐标,我们可以求出,每一个法平面,且过地球上点,对应的的法线方程。

在这所有法线方程里,总有一条是经过卫星的,而这个法线方程对应的点,与卫星的距离,就叫做大地高。

或者简单说是什么?把地球参考椭球面看作大地,大地高就是距离这个大地的高度。 

所以,空间坐标系和大地坐标系都只需要三个参数便可以表示:

double coordinate[3];

大地坐标系和空间坐标系的相互转换

两个坐标系的相互转换,实际上是一个几何问题。
一个物体,无论其在哪个坐标系下,其物理形状和特征都是固定的。比如描述一个圆,我们用极坐标和空间坐标分别表示:

  • 空间坐标系:x^2+y^2=1
  • 极坐标系:p=1

虽然他们的形式不同,但是无论在哪个坐标系下,他们描述的是同一个东西,不会因为坐标系发生了变化而形状发生变化。所以,和三种时间系统一样,每一个坐标系统都是唯一映射关系,我们可以用与atime相同思想的类,当传入一种坐标系统的时候,直接初始化另一个坐标系统。

template<class Base_Coordinates>
class Position
{
public:
	//若基础坐标系是空间坐标系,则传入X/Y/Z
	//若基础坐标系是大地坐标系,则传入B/L/H
	Position(double data1, double data2, double data3);

	//获取空间坐标系的坐标X/Y/Z
	double* Get__Space_Coordinates()
	{
		return _Space_Coordinates;
	}

	//获取大地坐标系的坐标B/L/H
	double* Get__Geodetic_Coordinate_System()
	{
		return _Geodetic_Coordinate_System;
	}


private:
	double _Space_Coordinates[3];
	double _Geodetic_Coordinate_System[3];
	Base_Coordinates _Base_Coordinates;
};

但是因为,两个坐标系统的类型都是double[3],所以用了个模板的方法,现在想想其实挺谢特的... 

大地坐标系和空间坐标系的关系 

想要转换两个坐标系,最首先的问题就是:两个坐标系怎么联系起来。
比如,空间坐标系的x轴是什么,y轴是什么,z轴是什么,和经纬度有什么关系。这个就不是由我说了算了,在国际上有统一标准:

  • Z轴:Z轴指向地球北极
  • X轴:X轴指向0纬0经
  • Y轴:由右手法则确定  

大地坐标系转空间坐标系

具体的计算过程推导,因为电脑实在不好写公式,所以只能拍一下笔记,见谅:

 最后可以得出公式:

#define Pi 3.1415926535898
#define LONG_AXIS 6378137
#define SHORT_AXIS 6356755
#define f 1.0/298.257223563


class Geodetic_Coordinate_System
{
public:
	Geodetic_Coordinate_System(double Long_Axis = LONG_AXIS, double Short_Axis = SHORT_AXIS)
	{
		_Long_Axis = Long_Axis;
		_Short_Axis = Short_Axis;
		/*_Eccentricity = pow((pow(_Long_Axis, 2) - pow(_Short_Axis, 2)), 0.5) / _Long_Axis;*/
		_Eccentricity = sqrt(2 * f - f * f);//两个公式都可以,只是形式不同,答案相同
	}
	double To_X(double B, double L, double H)
	{
		double Curvature_Radius = _Long_Axis / pow(1 - pow(_Eccentricity * sin(B * Pi / 180), 2), 0.5);
		return (Curvature_Radius + H) * cos(B * Pi / 180) * cos(L * Pi / 180);
	}
	double To_Y(double B, double L, double H)
	{
		double Curvature_Radius = _Long_Axis / pow(1 - pow(_Eccentricity * sin(B * Pi / 180), 2), 0.5);
		return (Curvature_Radius + H) * cos(B * Pi / 180) * sin(L * Pi / 180);
	}
	double To_Z(double B, double L, double H)
	{
		double Curvature_Radius = _Long_Axis / pow(1 - pow(_Eccentricity * sin(B * Pi / 180), 2), 0.5);
		return (Curvature_Radius * (1 - pow(_Eccentricity, 2)) + H) * sin(B * Pi / 180);
	}
private:
	double _Long_Axis;//长轴
	double _Short_Axis;//短轴
	double _Eccentricity;//偏心率
};

空间坐标系转大地坐标系

计算方法是大地坐标系转空间坐标系的逆过程,就不列出具体求法了。计算公式如图:

但是,要详细说一下,B需要进行迭代计算
为什么?

我们发现,等式左边是B,右边又有sinB,我们怎么可以用自己来求自己呢?但是,这是计算机实现的一种思想。

为了方便计算机计算,我们要把大多数计算变为线性的,说人话就是不要去让计算机解方程,而是直接告诉计算机结果表达式,比如:

x^2+3a+2a^2=0

尽管这样很简洁,但是你这样告诉计算机,计算机根本不知道要干什么,也不知道怎么解这个方程。所以,我们必须要把结果先预处理好,使等式为一个可以直接相等的表达式:

x1=-a; x2=-2a;

所以,这里的B,就是预处理后的结果。我们对B取一个初值,B会根据这个初值,一直得出新的B,一直到B收敛到了一个具体的值,即B的变化在很小很小几乎可以忽略不计的时候,此时得出的B就是我们所要求的B。

而且,一定要进行迭代计算!!!因为B就算只相差一点点,对大地高H的影响也是巨大的,所以必须要把B锁定在很高的精度,否则会极大影响大地高的精度。

#define Pi 3.1415926535898
#define LONG_AXIS 6378137
#define SHORT_AXIS 6356755
#define f 1.0/298.257223563


class Space_Coordinates
{
public:
	Space_Coordinates(double Long_Axis = LONG_AXIS, double Short_Axis = SHORT_AXIS)
	{
		_Long_Axis = Long_Axis;
		_Short_Axis = Short_Axis;
		/*_Eccentricity = pow((pow(_Long_Axis, 2) - pow(_Short_Axis, 2)), 0.5) / _Long_Axis;*/
		_Eccentricity = sqrt(2 * f - f * f);
	}
	double To_Latitude(double X, double Y, double Z)
	{
		double R = sqrt(X * X + Y * Y);
		double B1 = atan2(Z , R);
		double B2;
		while (1)
		{
			double W1 = sqrt(1 - _Eccentricity * _Eccentricity * sin(B1) * sin(B1));
			double N1 = LONG_AXIS / W1;
			B2 = atan((Z + N1 * _Eccentricity * _Eccentricity * sin(B1)) / R);
			if (abs(B2 - B1) <= 1e-11)	break;
			B1 = B2;
		}
		return B2 * 180 / Pi;
	}//迭代计算
	double To_Longitude(double X, double Y, double Z)
	{
		return atan2(Y, X) * 180 / Pi;
	}
	double To_Height(double X, double Y, double Z)
	{
		double B = To_Latitude(X, Y, Z);
		double N = LONG_AXIS / sqrt(1 - pow(_Eccentricity * sin(B * Pi / 180), 2));
		double FAI = atan(Z/pow(pow(X, 2) + pow(Y, 2), 0.5));
		return sqrt(X * X + Y * Y + Z * Z) * cos(FAI) / cos(B * Pi / 180) - N;
	}
private:
	double _Long_Axis;
	double _Short_Axis;
	double _Eccentricity;
};

并且,求出经纬度的时候,我们算出的结果是弧度,我们要对所有经纬度进行*180/Pi,这样得出来的东西才是给人看的角度。


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

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

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

相关文章

Idea-Linux远程开发部署

第一步&#xff1a;File->Remote Development 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a;在Host位置填写Linux虚拟机的IP地址&#xff0c;在Username、Password填写对应的账号密码后点击Test Connection测试连接。 第五步&#xff1a; 第六步&#xff1a;在…

【leetcode--文本对齐(还没整理完)】

根据题干描述的贪心算法&#xff0c;对于每一行&#xff0c;我们首先确定最多的是可以放置多少单词&#xff0c;这样可以得到该行的空格个数&#xff0c;从而确定该行单词之间的空格个数。 根据题目中填充空格的细节&#xff0c;我们分以下三种情况讨论&#xff1a; 当前行是…

Vue——样式绑定的几种方式

文章目录 前言往期回顾绑定对象绑定对象的另一种写法绑定数组数组与对象的嵌套 前言 样式绑定在vue中属于一种很常见的操作。在之前博客中针对样式的绑定操作&#xff0c;介绍了一个指令v-bind。缩写为:xxx。 vue 官网 样式绑定 往期回顾 先简单回顾下最开始绑定标签样式的操…

搭建gateway网关

1.创建springBoot项目 可以将Server URL换成start.aliyun.com 2.配置路由与跨域处理 路由&#xff1a; server:port: 10010 # 网关端口 spring:application:name: gateway # 服务名称cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 网关路由配置- i…

Java的冷知识你知道吗?

1、方法参数不能超过255个 在Java中&#xff0c;方法的参数数量是有限制的&#xff0c;最多不能超过255个。这个知识点可能对于大多数程序员来说并不常用&#xff0c;因此即使是经验丰富的Java开发者也可能不清楚这一点。2、Java中的自动装箱与拆箱 自动装箱是Java 5引入的新特…

站点被篡改快照被劫持解决服务方法教程_一招制敌

站点被篡改快照被劫持解决服务方法教程_一招制敌 被篡改表现形式&#xff1a; 站点打不开或跳转到别的网站。 攻击者目的&#xff1a; 报复、勒索、卖防御产品&#xff08;如DDOS防御产品&#xff09;。 攻击成本&#xff1a; 工具&#xff08;如VPN购买&#xff09;成本、人…

当新手小白有了一块【香橙派OrangePi AIpro】.Demo

当新手小白有了一块【香橙派OrangePi AIpro】.Demo 文章目录 当新手小白有了一块【香橙派OrangePi AIpro】.Demo一、香橙派OrangePi AIpro概述1.简介2.引脚图 二、“点亮”香橙派OrangePi AIpro1.官方工具下载2.官方镜像下载3.镜像烧录4.访问香橙派 AIpro 三、香橙派OrangePi A…

数据结构第三篇【链表的相关知识点一及在线OJ习题】

数据结构第三篇【链表的相关知识点一及在线OJ习题】 链表链表的实现链表OJ习题顺序表和链表的区别和联系 本文章主要讲解关于链表的相关知识&#xff0c;喜欢的可以三连喔 &#x1f600;&#x1f603;&#x1f604;&#x1f604;&#x1f60a;&#x1f60a;&#x1f643;&#…

Dubbo 自定义 Filter 编码实例

Dubbo的Filter机制为我们做应用的扩展设计提供了很多可能性&#xff0c;这里的Filter也是“责任链”机制的一种实现场景&#xff0c;作为Java码农&#xff0c;我们也经常接触到很多责任链的实现场景&#xff0c;如Tomcat进入Servlet前的filter&#xff0c;如Spring Aop代理的链…

性能飙升50%,react-virtualized-list如何优化大数据集滚动渲染

在处理大规模数据集渲染时&#xff0c;前端性能常常面临巨大的挑战。本文将探讨 react-virtualized-list 库如何通过虚拟化技术和 Intersection Observer API&#xff0c;实现前端渲染性能飙升 50% 的突破&#xff01;除此之外&#xff0c;我们一同探究下该库还支持哪些新的特性…

自友科技破解走班教育排课难题

新高考后&#xff0c;校园教务都面临着晋级&#xff0c;其中走班教育的分班排课是个巨大的挑战。 所以在分班排课的时候要清楚一下几个问题 一是&#xff1a;清楚的核算学生的选考科目。学生选科提交后做好并承认&#xff0c;最好是在分班后不要改或很少的一部分人改动。 二是…

手写防抖debounce

手写防抖debounce 应用场景 当需要在事件频繁触发时&#xff0c;只执行最后一次操作&#xff0c;可以使用防抖函数来控制函数的执行频率,比如窗口resize事件和输入框input事件&#xff1b; 这段代码定义了一个名为 debounce 的函数&#xff0c;它接收两个参数&#xff1a;fn…

linux中最基础使用的命令

小白学习记录&#xff1a; 前情提要&#xff1a;Linux命令基础格式!查看 ls看目录的小技巧 进入指定目录 cd查看当前工作目录 pwd创建一个新的目录(文件夹&#xff09; mkdir创建文件 touch查看文件内容 cat、more操作文件、文件夹- 复制 cp- 移动 mv- 删除【危险操作&#xff…

Scrum 的速度如何衡量和提高

了解你的 Scrum 团队的实际开发速度是非常多敏捷团队的诉求&#xff0c;而速度&#xff08;Velocity&#xff09;作为敏捷项目的度量工具&#xff0c;为管理者提供了对团队工作能力深入了解的机会。 这份指南将深入探讨 Scrum 中速度的概念&#xff0c;指导你如何进行计算&…

cURL error 60: SSL certificate problem: unable to get local issuer certifica

本地小程序把接口换到本地的服务器接口&#xff0c;然后就报错了&#xff1a; cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) 经查询查到&#xff1a;此问题的出现是由于没有配…

5月更新!优维EasyOps®平台7大新功能上线~

5月&#xff0c;优维EasyOps全平台产品能力又升级啦&#xff01;&#x1f44f; 快来看看都有新增的功能与优化吧&#xff01;&#x1f447; 重点升级 架构可观测 1.系统监控态势感知 过去&#xff0c;用户在使用监控平台的过程中&#xff0c;存在如下问题&#xff1a; 告警…

基于单片机的超声波倒车雷达设计

摘 要&#xff1a;文 章设计了一种基于单片机的超声波倒车雷达系统&#xff0c;以 AT89C51 型单片机作为控制核心&#xff0c;集距离测量、显示&#xff0c;方位显示和危险报警于一体&#xff0c;以提高驾驶者在倒车泊车时的安全性和舒适性。本设计采用 Keil 软件对系统程序…

详解:重庆耶非凡的选品师项目有哪些优势?

在竞争激烈的电商市场中&#xff0c;重庆耶非凡科技有限公司凭借其独特的选品师项目&#xff0c;成功地在众多企业中脱颖而出。这一项目不仅体现了公司对市场趋势的敏锐洞察力&#xff0c;更彰显了其专业的选品能力和对消费者需求的深刻理解。 首先&#xff0c;耶非凡的选品师项…

军用电源性能测试有哪些测试项目?需要遵循什么标准?

为了确保军用电源在极端条件下能够正常工作&#xff0c;必须对其进行一系列严格的性能测试。这些测试不仅包括效率、电压调整率和负载调整率等基本参数的测试&#xff0c;还包括动态响应能力、绝缘电阻、耐压测试、温度系数以及高低温循环等综合性能的评估。 测试项目 效率 电压…

【Python Cookbook】S01E15 将名称映射到序列的元素中

目录 问题解决方案讨论 问题 对于访问列表或元组中的元素&#xff0c;我们通常使用索引或者下标的方法。但是这明显会降低代码的可阅读性。如果我们想通过命名来提高代码的可阅读性&#xff0c;减少结构中对位置的依赖&#xff0c;怎么做&#xff1f; 解决方案 python 提供 …