【测绘程序设计】C#伪距单点定位

news2024/12/26 10:46:05

文章目录

    • 一、题目解读
    • 二、界面设计
    • 三、矩阵计算实现
      • 1、矩阵定义Matrix
      • 2、矩阵构造Matrix()
      • 3、单位矩阵MatrixE()
      • 4、加减乘操作符重载+-*
      • 5、矩阵转置transposs()
      • 6、矩阵求逆Inverse()
    • 四、数据存储结构设计
      • 1、Sat类存一颗卫星的数据
      • 2、Epoch类存一个历元的数据
      • 3、DataCenter类存全部的数据
    • 五、文件读取
    • 六、最小二乘解算
      • 1、计算卫星到接收机近似点的距离R0
      • 2、构建设计矩阵B
      • 3、构建观测向量残差L
      • 4、构建权阵P
      • 5、计算协因数阵Q
      • 6、最小二乘计算增量dx
      • 7、估计位置pos
      • 8、计算验后残差V
      • 9、计算验后单位权中误差sigma0
      • 10、计算验后中误差sigma
      • 11、计算PDOP值
      • 12、循环解算过程
    • 七、输出结果文件
    • 八、代码汇总
      • 1、Sat.cs
      • 2、Epoch.cs
      • 3、DataCenter.cs
      • 4、Matrix.cs
      • 5、Algorithm.cs
      • 6、Form1.cs

一、题目解读

题目是简化了很多的GNSS伪距单点定位,而且比赛是单人四小时,可能到时候相比书上还要再简化一点。顺带一提,我的博客主要就是写GNSS,目前有RTKLIB、GraphGNSSLib、GNSS论文阅读,三个GNSS相关的专栏。

  • RTKLIB是最知名的GNSS定位算法开源程序;
  • GraphGNSSLib是一个因子图优化GNSS开源程序,基于ROS、Ceres非线性最小二乘库,用RTKLIB读取文件、然后重新用C++的方式组织数据、写算法。

伪距单点定位的基本原理就是用一台接收机同时接收4颗以上卫星,获取卫星到接收机之间的距离,根据空间后方交会的原理,构建伪距观测值和接收机位置间的方程组,解方程组得到接收机的位置。具体计算上还需要注意以下问题:

  • 伪距观测值存在着很多的误差,有些可以建立模型修正(如:电离层延迟、对流层延迟、卫星钟差)、有些需要作为参数(如:接收机钟差)一并估计。伪距模型修正是在构建观测方程组之前,然后用修正后的伪距观测值计算观测残差。由于钟差作为参数,所以总的参数个数为4。

  • 由于卫星数多于4颗,观测方程数大于参数个数,方程组超定,用最小二乘原理求出满足残差平方和最小的解。

  • 由于观测方程不是线性的,得先线性化,在近似坐标处展开,求B、L矩阵。

  • 由于各观测值的精度不同,引入权阵P来控制观测值对结果影响的权重,在本题中采用高度角定权模型

  • 解算完之后还要再求 σ 0 \sigma_0 σ0 σ d x \sigma_{dx} σdx σ d y \sigma_{dy} σdy σ d z \sigma_{dz} σdz,PDOP,来衡量结果的精度

    书上给的C++代码里,这部分我觉得有问题,多了平方:

    其中:res3是协因数阵 Q Q Q,由 ( B T P B ) − 1 (B^TPB)^{-1} (BTPB)1传播定律得来

    代码里说 Q Q Q是协方差阵,这也有问题,协因数阵为协方差阵除以单位权方差。所以求 σ d x \sigma_{dx} σdx σ d y \sigma_{dy} σdy σ d z \sigma_{dz} σdz是取协因数乘以单位权中误差。

二、界面设计

做的比较简单,只用两个控件:一个menustrip,一个datagridview,达不到比赛的要求,还可以再加状态栏,加画图,加富文本框。

三、矩阵计算实现

1、矩阵定义Matrix

定义Matrix类来表示矩阵,矩阵的数据用二维double数组arr表示,字段m表示矩阵的行数、n表示矩阵的列数。

public class Matrix
{
    public int m;
    public int n;
    public double[,] arr;
    ........
  • mn用于确定要开辟数组的大小,判断矩阵计算能否实现。
  • arr是矩阵数据存储的主体,操作数组元素:矩阵名.arr[行,列]

2、矩阵构造Matrix()

无参构造

public Matrix(){
    m = 0;
    n = 0;
    arr = new double[m, n];
}

拷贝构造:用另一个Matrix矩阵构造矩阵

public Matrix(Matrix s)
{
     this.m = s.m;
     this.n = s.n;
     arr = new double[m, n];
     this.arr = s.arr;
}

零矩阵构造:定义矩阵的行列数,元素全设为0

public Matrix(int mm, int nn)
{
     m = mm;
     n = nn;
     arr = new double[m, n];
}

3、单位矩阵MatrixE()

对角线元素全设置为1

public Matrix MatrixE(int mm, int nn)
{
     Matrix matrix = new Matrix(mm, nn);
     m = mm;
     n = nn;
     arr = new double[m, n];

     for (int i = 0; i < m; i++){
          for (int j = 0; j < n; j++){
              arr[i, j] = 1;
           }
     }
     return matrix;
}

4、加减乘操作符重载±*

:需要行列数相等

static public Matrix operator +(Matrix A, Matrix B)
{
    Matrix C = new Matrix(A.m, A.n);
    //判断是否可以运算    
    if (A.m != B.m || A.n != B.n || A.m != C.m || A.n != C.n){
        System.Windows.Forms.MessageBox.Show("矩阵维数不同");
    }
    for (int i = 0; i < C.m; i++){
        for (int j = 0; j < C.n; j++){
            C.arr[i, j] = A.arr[i, j] + B.arr[i, j];
        }
    }

    return C;
}

:同样,需要行列数相等

static public Matrix operator -(Matrix A, Matrix B)
{
    int i = 0;
    int j = 0;
    Matrix C = new Matrix(A.m, B.n);
    //判断是否可以运算    
    if (A.m != B.m || A.n != B.n ||
        A.m != C.m || A.n != C.n){
        Console.ReadKey();
    }
    for (i = 0; i < C.m; i++){
        for (j = 0; j < C.n; j++){
            C.arr[i, j] = A.arr[i, j] - B.arr[i, j];
        }
    }
    return C;
}

:前矩阵的列数等于后矩阵的行数,生成矩阵的维度为:前矩阵列数*后矩阵的行数

static public Matrix operator *(Matrix A, Matrix B)
{
    int i = 0;
    int j = 0;
    int k = 0;
    double temp = 0;
    Matrix C = new Matrix(A.m, B.n);
    //判断是否可以运算    
    if (A.m != C.m || B.n != C.n ||
        A.n != B.m){
        return C;
    }
    //运算    
    for (i = 0; i < C.m; i++){
        for (j = 0; j < C.n; j++){
            temp = 0;
            for (k = 0; k < A.n; k++){
                temp += A.arr[i, k] * B.arr[k, j];
            }
            C.arr[i, j] = temp;
        }
    }
    return C;
}

5、矩阵转置transposs()

public Matrix transposs(Matrix A)
{
    int i = 0;
    int j = 0;
    Matrix B = new Matrix(A.n, A.m);
    for (i = 0; i < B.m; i++){
        for (j = 0; j < B.n; j++){
            B.arr[i, j] = A.arr[j, i];
        }
    }
    return B;
}

6、矩阵求逆Inverse()

高斯-约旦法实现

public Matrix Inverse(Matrix matrix)
{
    matrix.arr = InverseMatrix(matrix.arr);
    return matrix;
}
        public double[,] InverseMatrix(double[,] matrix)
{
    int n = matrix.GetLength(0);
    double[,] result = new double[n, n];
    double[,] temp = new double[n, 2 * n];

    //将矩阵和单位矩阵拼接成一个2n*n的矩阵
    for (int i = 0; i < n; i++){
        for (int j = 0; j < n; j++){
            temp[i, j] = matrix[i, j];
            temp[i, j + n] = i == j ? 1 : 0;
        }
    }
    //高斯-约旦消元法
    for (int i = 0; i < n; i++){
        double tempValue = temp[i, i];
        for (int j = i; j < 2 * n; j++){
            temp[i, j] /= tempValue;
        }
        for (int j = 0; j < n; j++){
            if (j != i){
                tempValue = temp[j, i];
                for (int k = i; k < 2 * n; k++){
                    temp[j, k] -= tempValue * temp[i, k];
                }
            }
        }
    }
    //取出逆矩阵
    for (int i = 0; i < n; i++){
        for (int j = 0; j < n; j++){
            result[i, j] = temp[i, j + n];
        }
    }

    return result;
}

注意:

  • 判断矩阵能否计算可以计算之后的处理可以写的更完善一点,我这基本上没处理,比赛项目的代码量小,如果项目大,最好写完善一点,矩阵计算出错了要能知道在哪一步出错;而且如果不是比赛可以直接用现成的线性代数库。
  • 矩阵求逆用的高斯-约旦消元法,看起来代码量最小,好写但时间复杂度太大。正常计算都是用各种分解方法,再计算,性能好很多。
  • 操作符重载实现加减乘,调用起来方便,一行公式不用拆成几条语句。
  • 如果用C++,我可以重载括号运算符,实现矩阵元素的辅值和取值;C#我不熟,重载括号运算符可以实现取值,但由于C#没有指针,不知道能不能实现赋值,就没那么写。

四、数据存储结构设计

数据存储由SatEpochDataCenter三个层次实现:Sat卫星类存一个历元一颗卫星的数据,通过文件读取获取;Epoch类存一个历元的数据,包括一个历元内所有的Sat、最小二乘计算的中间矩阵、最终结果;DataCenter存全部的数据,包括所有的Epoch和近似坐标。在Form1类的开头需要先实例化DataCenter data = new DataCenter();,以便之后读写里面的数据,起到类似全局变量的作用。

1、Sat类存一颗卫星的数据

public class Sat
{
    public Matrix stapos;       //卫星位置
    public string PRN;          //卫星PRN
    public double satColck;     //卫星钟差
    public double elevation;    //卫星高度角
    public double cl;           //伪距
    public double tropDely;     //对流层延迟
    public double R0;           //估计几何距离
}

2、Epoch类存一个历元的数据

内含List<Sat> sats卫星列表

public class Epoch
{
    public int satNum;      //卫星数
    public int gpsTime;     //历元时间
    public List<Sat> sats;  //卫星观测值列表
    public Matrix dx;       //最小二乘求得的增量dx
    public Matrix pos;      //最小二乘估计的位置
    public double sigma0;   //延后单位权中误差
    public Matrix sigma;    //各个方向的中误差
    public double PDOP;     //PDOP
    public Matrix Q;        //协因数阵
    public Matrix P;        //权阵
    public Matrix B;        //设计矩阵
    public Matrix L;        //观测残差向量
    public Matrix V;        //后验残差
}

3、DataCenter类存全部的数据

内含List<Epoch> Epoches历元列表

public class DataCenter
{
    public List<Epoch> Epoches;     //历元列表
    public Matrix APPROX_POSITION;  //近似坐标
}

五、文件读取

private void 导入数据文件ToolStripMenuItem_Click(object sender, EventArgs e)
{
    try
    {
        OpenFileDialog opf = new OpenFileDialog();
        opf.Filter = "文本文件|*.txt";
        opf.Title = "请选择要导入的数据文件";
        if (opf.ShowDialog() == DialogResult.OK)
        {
            StreamReader sr = new StreamReader(opf.FileName);
            string[] lines = sr.ReadLine().Trim().Split(',', ':', ':', '(');
            data.APPROX_POSITION = new Matrix(3, 1, new double[3, 1] {
            {double.Parse( lines[1])},{ double.Parse( lines[2]) },{ double.Parse( lines[3])} });

            sr.ReadLine();  //第二行跳过

            //每一次while循环读取一个历元的数据
            Epoch epoch = new Epoch();
            Sat sat = new Sat();
            data.Epoches = new List<Epoch>();
            while (!sr.EndOfStream)
            {
                epoch = new Epoch();
                epoch.sats = new List<Sat>();

                lines = sr.ReadLine().Trim().Split(',', ':', ':');
                if (lines == null)
                {
                    break;
                }
                epoch.satNum = int.Parse(lines[1]);
                epoch.gpsTime = int.Parse(lines[3]);
                for (int i = 0; i < epoch.satNum; i++)
                {
                    sat = new Sat();
                    lines = sr.ReadLine().Trim().Split(',', ':', ':');
                    sat.PRN = lines[0];
                    sat.stapos = new Matrix(3, 1, new double[3, 1] {
                    {double.Parse( lines[1])},{ double.Parse( lines[2]) },
                    { double.Parse( lines[3])} });
                    sat.satColck = double.Parse(lines[4]);
                    sat.elevation = double.Parse(lines[5]);
                    sat.cl = double.Parse(lines[6]);
                    sat.tropDely = double.Parse(lines[7]);
                    epoch.sats.Add(sat);
                }
                data.Epoches.Add(epoch);
            }
        }
    }
    catch (Exception)
    {
        MessageBox.Show("文件读取出错,请检查文件是否正确!!");
        throw;
    }
    
}

执行流程

  • 写在一个大的try-catch里,以便异常捕获,正常开发应该写的跟细一点,但咱比赛就没这必要了。
  • 创建文件对话框opfShowDialog()显示文件,获取文件全路径opf.FileName,创建读文件流sr
  • 读取第一行,获取近似坐标。
  • 第二行跳过。
  • 进入while(!sr.EndOfStream)循环,每次循环读取一个历元的数据到epoch
    • 先读取历元的开头,获取卫星数,历元时间到epoch
    • 根据卫星数for循环,一次读取一颗卫星的数据到sat里,再加到epoch的卫星列表里。
    • epoch加到data的历元列表里。

注意:

  • 原始数据文件的编码最好转成UTF-8再读取,否则一些中文的标点符号读取不出来,产生错误。转UTF-8方法:①代码内转换,需要知道文件的原始编码,写对应的转换代码。②手动转换:用Windows自带的记事本打开文件,点”另存为“,保存按钮坐标有编码格式,默认是文件的原始格式,选择“UTF-8”,以UTF-8保存。
  • 标点符号要注意,给的乱的很,有中文全角、有英文乱角,split()的时候都选上吧。另外注意(也是需要加到split()里的。
  • while (!sr.EndOfStream)做文件是否读完的判断,结尾可能有空行检验不到,如果不进行处理会产生异常,所以我还进行了非空判断if (lines == null) {break}

六、最小二乘解算

1、计算卫星到接收机近似点的距离R0

R 0 j = ( X i − X 0 ) 2 + ( Y i − Y 0 ) 2 + ( Z i − Z 0 ) 2 R_{0}^{j}=\sqrt{\left(X^{i}-X_{0}\right)^{2}+\left(Y^{i}-Y_{0}\right)^{2}+\left(Z^{i}-Z_{0}\right)^{2}} R0j=(XiX0)2+(YiY0)2+(ZiZ0)2

double R0 = Math.Sqrt(Math.Pow(epoch.sats[i].stapos.arr[0, 0] - pos0.arr[0, 0], 2) +
Math.Pow(epoch.sats[i].stapos.arr[1, 0] - pos0.arr[1, 0], 2)
+ Math.Pow(epoch.sats[i].stapos.arr[2, 0] - pos0.arr[2, 0], 2));

2、构建设计矩阵B

B = [ l 1 m 1 n 1 − 1 l 2 m 2 n 2 − 1 ⋮ ⋮ ⋮ − 1 l n m n n n − 1 ] \boldsymbol{B}=\left[\begin{array}{cccc} l^{1} & m^{1} & n^{1} & -1 \\ l^{2} & m^{2} & n^{2} & -1 \\ \vdots & \vdots & \vdots & -1 \\ l^{n} & m^{n} & n^{n} & -1 \end{array}\right] B= l1l2lnm1m2mnn1n2nn1111

其中, l i = X i − X 0 R 0 i l^{i}=\frac{X^{i}-X_{0}}{R_{0}^{i}} li=R0iXiX0 m ′ = Y i − Y 0 R 0 i m^{\prime}=\frac{Y^{i}-Y_{0}}{R_{0}^{i}} m=R0iYiY0 n i = Z i − Z 0 R 0 i n^{i}=\frac{Z^{i}-Z_{0}}{R_{0}^{i}} ni=R0iZiZ0

epoch.B.arr[i, 0] = (epoch.sats[i].stapos.arr[0, 0] - pos0.arr[0, 0]) / R0;     
epoch.B.arr[i, 1] = (epoch.sats[i].stapos.arr[1, 0] - pos0.arr[1, 0]) / R0;
epoch.B.arr[i, 2] = (epoch.sats[i].stapos.arr[2, 0] - pos0.arr[2, 0]) / R0;
epoch.B.arr[i, 3] = -1;

3、构建观测向量残差L

L = [ P 1 P 2 ⋮ P n ] − [ R 0 1 R 0 2 ⋮ R 0 n ] + [ d t 1 d t 2 ⋮ d t 3 ] − [ d trop  1 d trop  2 ⋮ d trop  n ] \boldsymbol{L}=\left[\begin{array}{c} P^{1} \\ P^{2} \\ \vdots \\ P^{n} \end{array}\right]-\left[\begin{array}{c} R_{0}^{1} \\ R_{0}^{2} \\ \vdots \\ R_{0}^{n} \end{array}\right]+\left[\begin{array}{c} d t^{1} \\ d t^{2} \\ \vdots \\ d t^{3} \end{array}\right]-\left[\begin{array}{c} d_{\text {trop }}^{1} \\ d_{\text {trop }}^{2} \\ \vdots \\ d_{\text {trop }}^{n} \end{array}\right] L= P1P2Pn R01R02R0n + dt1dt2dt3 dtrop 1dtrop 2dtrop n

epoch.L.arr[i, 0] = epoch.sats[i].cl - R0 + epoch.sats[i].satColck - epoch.sats[i].tropDely;

注意正负号!!

4、构建权阵P

P j = [ p 1 0 0 0 0 p 2 0 0 0 0 ⋯ 0 0 0 0 p m ] \boldsymbol{P}_{j}=\left[\begin{array}{cccc} p_{1} & 0 & 0 & 0 \\ 0 & p_{2} & 0 & 0 \\ 0 & 0 & \cdots & 0 \\ 0 & 0 & 0 & p_{m} \end{array}\right] Pj= p10000p200000000pm

高度角定权,认为各卫星之间无相关性,只有对角线上有元素,其中 p i = 1 σ p i 2 = sin ⁡ ( θ i ) σ P , 0 2 p^{i}=\frac{1}{\sigma_{p^{i}}^{2}}=\frac{\sin \left(\theta^{i}\right)}{\sigma_{P, 0}^{2}} pi=σpi21=σP,02sin(θi) σ P , 0 2 = 0.04   m 2 \sigma_{P, 0}^{2}=0.04 \mathrm{~m}^{2} σP,02=0.04 m2

注意:计算正弦值需要将角度转为弧度

epoch.P.arr[i, i] = Math.Sin(epoch.sats[i].elevation * Math.PI / 180) / 0.04;

5、计算协因数阵Q

Q = ( B T P B ) − 1 \boldsymbol{Q}=\left(\boldsymbol{B}^{\mathrm{T}} P \boldsymbol{B}\right)^{-1} Q=(BTPB)1

epoch.Q = pos0.Inverse((pos0.transposs(epoch.B) * epoch.P * epoch.B));

6、最小二乘计算增量dx

d x = − ( B ⊤ P B ) − 1 B ⊤ P L = Q B ⊤ P L d x=-\left(B^{\top} P B\right)^{-1} B^{\top} P L=QB^{\top} P L dx=(BPB)1BPL=QBPL

Matrix zero = new Matrix(4, 1);
epoch.dx = zero - epoch.Q * pos0.transposs(epoch.B) * epoch.P * epoch.L;
Matrix _dx = new Matrix(3, 1);
_dx.arr[0, 0] = epoch.dx.arr[0, 0];
_dx.arr[1, 0] = epoch.dx.arr[1, 0];
_dx.arr[2, 0] = epoch.dx.arr[2, 0];

注意:不能直接在式子前面加负号-,只能用一个0向量来减去全式,实现取反。

7、估计位置pos

X = X 0 + d x = [ X 0 Y 0 Z 0 ] + [ d X d Y d Z ] \boldsymbol{X}=\boldsymbol{X}_{0}+\boldsymbol{d} \boldsymbol{x}=\left[\begin{array}{l} X_{0} \\ Y_{0} \\ Z_{0} \end{array}\right]+\left[\begin{array}{l} d X \\ d Y \\ d Z \end{array}\right] X=X0+dx= X0Y0Z0 + dXdYdZ

epoch.pos = pos0 + _dx;

8、计算验后残差V

V = B ⋅ d x + L {V=B \cdot d x+L} V=Bdx+L

epoch.V = epoch.B * epoch.dx + epoch.L;
Matrix vtpv = pos0.transposs(epoch.V) * epoch.P * epoch.V;

9、计算验后单位权中误差sigma0

σ 0 = V T P V n − 4 \sigma_{0}=\sqrt{\frac{\boldsymbol{V}^{\mathrm{T}} \boldsymbol{P} \boldsymbol{V}}{n-4}} σ0=n4VTPV

epoch.sigma0 = Math.Sqrt(vtpv.arr[0, 0] / (epoch.satNum - 4));

10、计算验后中误差sigma

σ d x = σ 0 ⋅ q d x , d x , σ d Y = σ 0 ⋅ q d x , d y , σ d z = σ 0 ⋅ q d x , d x , σ d t = σ 0 ⋅ q d t , d t {\sigma_{d x}=\sigma_{0} \cdot \sqrt{q_{d x, d x}}, \sigma_{d Y}=\sigma_{0} \cdot \sqrt{q_{d x, d y}}, \sigma_{d z}=\sigma_{0} \cdot \sqrt{q_{d x, d x}}, \sigma_{d t}=\sigma_{0} \cdot \sqrt{q_{d t, d t}}} σdx=σ0qdx,dx ,σdY=σ0qdx,dy ,σdz=σ0qdx,dx ,σdt=σ0qdt,dt

epoch.sigma = new Matrix(4, 1);
epoch.sigma.arr[0, 0] = epoch.sigma0 * Math.Sqrt(epoch.Q.arr[0, 0]) ;
epoch.sigma.arr[1, 0] = epoch.sigma0 * Math.Sqrt(epoch.Q.arr[1, 1]) ;
epoch.sigma.arr[2, 0] = epoch.sigma0 * Math.Sqrt(epoch.Q.arr[2, 2]) ;
epoch.sigma.arr[3, 0] = epoch.sigma0 * Math.Sqrt(epoch.Q.arr[3, 3]) ;

11、计算PDOP值

 PDOP  = q d x , d x + q d y , d y + q d z , d z \text { PDOP }=\sqrt{q_{d x, d x}+q_{d y, d y}+q_{d z, d z}}  PDOP =qdx,dx+qdy,dy+qdz,dz

epoch.PDOP = Math.Sqrt(epoch.Q.arr[0, 0] + epoch.Q.arr[1, 1] + epoch.Q.arr[2, 2]);

12、循环解算过程

最小二乘的算法我分别写在了两个函数里,CalBLP()Lsq(),计算的时候,就是简单的单历元平差,每个历元分别调用一遍两个函数:

for (int i = 0; i < data.Epoches.Count(); i++)
{
    algorithm.CalBLP(data.APPROX_POSITION, data.Epoches[i]);
    algorithm.Lsq(data.APPROX_POSITION, data.Epoches[i]);
}

注意:在我的计算过程中:

  • 每个历元的近似值都用的是题目给的近似值,正常解算需要上一个历元的结果作为下一个历元的初值。
  • 一个历元内只执行了一次最小二乘计算,正常解算需要一个历元内多次迭代,以弱化线性化和初值选取不准确的误差。
  • 题目没有具体规定,写简单点就好。想实现迭代也不难,加个循环、每次计算完把结果赋值给近似坐标就行。

七、输出结果文件

private void 输出结果文件ToolStripMenuItem_Click(object sender, EventArgs e)
   {
       if (data.Epoches == null){
           MessageBox.Show("请先导入数据");
           return;
       }
       if (data.Epoches[0].B == null)
       {
           MessageBox.Show("请先进行最小二乘解算");
           return;
       }

       string Report = "";

       Report += "——————————————————————————————————————\n" +
           "———————————————— 最小二乘解算结果 ——————————————" +
           "\n——————————————————————————————————————\n" +
           "观测历元         X / m           σx / m           Y / m             σy / m          Z / m          σz / m    PDOP  \n";
      
       
       for (int i = 0; i < data.Epoches.Count; i++)
       {
           Report += data.Epoches[i].gpsTime.ToString() +"  :  "+
               data.Epoches[i].pos.arr[0, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[0, 0].ToString("0.0000") +"    "+
                    data.Epoches[i].pos.arr[1, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[1, 0].ToString("0.0000") +"    "+
                    data.Epoches[i].pos.arr[2, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[2, 0].ToString("0.0000") +"    "+
            data.Epoches[i].PDOP.ToString("0.0000") + "\n";
    }


    SaveFileDialog svf = new SaveFileDialog();
    svf.Filter = "文本文件|*.txt";
    if (svf.ShowDialog() == DialogResult.OK)
    {
        StreamWriter sw = new StreamWriter(svf.FileName);
        sw.Write(Report);
        sw.Flush();
    }

}

执行流程:

  • 先进行判断,是否已经进行过文件读取,是否已经完成计算。
  • 定义string类型变量Report,将文件的开头写到Report里,在循环将每一个历元的数据都加到Report里。
  • 创建保存文件对话框svf,获取文件路径,创建文件IO流sw,将Report内容写入sw

八、代码汇总

1、Sat.cs

public class Sat
{
    public Matrix stapos;       //卫星位置
    public string PRN;          //卫星PRN
    public double satColck;     //卫星钟差
    public double elevation;    //卫星高度角
    public double cl;           //伪距
    public double tropDely;     //对流层延迟
    public double R0;           //估计几何距离
}

2、Epoch.cs

public class Epoch
{
    public int satNum;      //卫星数
    public int gpsTime;     //历元时间
    public List<Sat> sats;  //卫星观测值列表
    public Matrix dx;       //最小二乘求得的增量dx
    public Matrix pos;      //最小二乘估计的位置
    public double sigma0;   //延后单位权中误差
    public Matrix sigma;    //各个方向的中误差
    public double PDOP;     //PDOP
    public Matrix Q;        //协因数阵
    public Matrix P;        //权阵
    public Matrix B;        //设计矩阵
    public Matrix L;        //观测残差向量
    public Matrix V;        //延后残差
}

3、DataCenter.cs

public class DataCenter
{
    public List<Epoch> Epoches;     //历元列表
    public Matrix APPROX_POSITION;  //近似坐标
}

4、Matrix.cs

public class Matrix
{
    public int m;
    public int n;
    public double[,] arr;

    /// <summary>
    /// 创建一个矩阵0*0
    /// </summary>
    public Matrix(){
        m = 0;
        n = 0;
        arr = new double[m, n];
    }

    /// <summary>
    /// 拷贝构造
    /// </summary>
    /// <param name="s"></param>
    public Matrix(Matrix s)
    {
        this.m = s.m;
        this.n = s.n;
        arr = new double[m, n];
        this.arr = s.arr;
    }

    public Matrix(int mm, int nn, double[,] arr)
    {
        m = mm;
        n = nn;
        this.arr = arr;
    }

    public Matrix(int mm, int nn)
    {
        m = mm;
        n = nn;
        arr = new double[m, n];
    }

    /// <summary>
    /// 创建单位阵
    /// </summary>
    /// <param name="mm"></param>
    /// <param name="nn"></param>
    /// <returns></returns>
    public Matrix MatrixE(int mm, int nn)
    {
        Matrix matrix = new Matrix(mm, nn);
        m = mm;
        n = nn;
        arr = new double[m, n];

        for (int i = 0; i < m; i++){
            for (int j = 0; j < n; j++){
                arr[i, j] = 1;
            }
        }
        return matrix;
    }

    /// <summary>
    /// 重载操作符实现矩阵加法
    /// </summary>
    /// <param name="A"></param>
    /// <param name="B"></param>
    /// <returns></returns>
    static public Matrix operator +(Matrix A, Matrix B)
    {
        Matrix C = new Matrix(A.m, A.n);
        //判断是否可以运算    
        if (A.m != B.m || A.n != B.n || A.m != C.m || A.n != C.n){
            System.Windows.Forms.MessageBox.Show("矩阵维数不同");
        }
        for (int i = 0; i < C.m; i++){
            for (int j = 0; j < C.n; j++){
                C.arr[i, j] = A.arr[i, j] + B.arr[i, j];
            }
        }

        return C;
    }

    /// <summary>
    /// 重载操作符实现矩阵减法
    /// </summary>
    /// <param name="A"></param>
    /// <param name="B"></param>
    /// <returns></returns>
    static public Matrix operator -(Matrix A, Matrix B)
    {
        int i = 0;
        int j = 0;
        Matrix C = new Matrix(A.m, B.n);
        //判断是否可以运算    
        if (A.m != B.m || A.n != B.n ||
            A.m != C.m || A.n != C.n){
            Console.ReadKey();
        }
        for (i = 0; i < C.m; i++){
            for (j = 0; j < C.n; j++){
                C.arr[i, j] = A.arr[i, j] - B.arr[i, j];
            }
        }
        return C;
    }

    /// <summary>
    /// 重载操作符实现矩阵乘法
    /// </summary>
    /// <param name="A"></param>
    /// <param name="B"></param>
    /// <returns></returns>
    static public Matrix operator *(Matrix A, Matrix B)
    {
        int i = 0;
        int j = 0;
        int k = 0;
        double temp = 0;
        Matrix C = new Matrix(A.m, B.n);
        //判断是否可以运算    
        if (A.m != C.m || B.n != C.n ||
            A.n != B.m){
            return C;
        }
        //运算    
        for (i = 0; i < C.m; i++){
            for (j = 0; j < C.n; j++){
                temp = 0;
                for (k = 0; k < A.n; k++){
                    temp += A.arr[i, k] * B.arr[k, j];
                }
                C.arr[i, j] = temp;
            }
        }
        return C;
    }

    /// <summary>
    /// 矩阵转置
    /// </summary>
    /// <param name="A"></param>
    /// <returns></returns>
    public Matrix transposs(Matrix A)
    {
        int i = 0;
        int j = 0;
        Matrix B = new Matrix(A.n, A.m);
        for (i = 0; i < B.m; i++){
            for (j = 0; j < B.n; j++){
                B.arr[i, j] = A.arr[j, i];
            }
        }
        return B;
    }

    public double[,] InverseMatrix(double[,] matrix)
    {
        int n = matrix.GetLength(0);
        double[,] result = new double[n, n];
        double[,] temp = new double[n, 2 * n];

        //将矩阵和单位矩阵拼接成一个2n*n的矩阵
        for (int i = 0; i < n; i++){
            for (int j = 0; j < n; j++){
                temp[i, j] = matrix[i, j];
                temp[i, j + n] = i == j ? 1 : 0;
            }
        }
        //高斯-约旦消元法
        for (int i = 0; i < n; i++){
            double tempValue = temp[i, i];
            for (int j = i; j < 2 * n; j++){
                temp[i, j] /= tempValue;
            }
            for (int j = 0; j < n; j++){
                if (j != i){
                    tempValue = temp[j, i];
                    for (int k = i; k < 2 * n; k++){
                        temp[j, k] -= tempValue * temp[i, k];
                    }
                }
            }
        }
        //取出逆矩阵
        for (int i = 0; i < n; i++){
            for (int j = 0; j < n; j++){
                result[i, j] = temp[i, j + n];
            }
        }

        return result;
    }

    /// <summary>
    /// 矩阵求逆
    /// </summary>
    /// <param name="matrix"></param>
    /// <returns></returns>
    public Matrix Inverse(Matrix matrix)
    {
        matrix.arr = InverseMatrix(matrix.arr);
        return matrix;
    }


}

5、Algorithm.cs

public class Algorithm
{
    /// <summary>
    /// 计算BLP
    /// </summary>
    /// <param name="pos0"></param>
    /// <param name="epoch"></param>
    public void CalBLP(Matrix pos0, Epoch epoch)
    {
        epoch.B = new Matrix(epoch.satNum, 4);
        epoch.L = new Matrix(epoch.satNum, 1);
        epoch.P = new Matrix(epoch.satNum, epoch.satNum);
        for (int i = 0; i < epoch.satNum; i++)
        {
            //卫星到接收机近似点的距离R0
            double R0 = Math.Sqrt(Math.Pow(epoch.sats[i].stapos.arr[0, 0] - pos0.arr[0, 0], 2) +
               Math.Pow(epoch.sats[i].stapos.arr[1, 0] - pos0.arr[1, 0], 2)
               + Math.Pow(epoch.sats[i].stapos.arr[2, 0] - pos0.arr[2, 0], 2));

           //设计矩阵B
           epoch.B.arr[i, 0] = (epoch.sats[i].stapos.arr[0, 0] - pos0.arr[0, 0]) / R0;     
           epoch.B.arr[i, 1] = (epoch.sats[i].stapos.arr[1, 0] - pos0.arr[1, 0]) / R0;
           epoch.B.arr[i, 2] = (epoch.sats[i].stapos.arr[2, 0] - pos0.arr[2, 0]) / R0;
           epoch.B.arr[i, 3] = -1;

           //观测向量L
           epoch.L.arr[i, 0] = epoch.sats[i].cl - R0 + epoch.sats[i].satColck - epoch.sats[i].tropDely;

            //权阵P
            epoch.P.arr[i, i] = Math.Sin(epoch.sats[i].elevation * Math.PI / 180) / 0.04;
        }
    }


    /// <summary>
    /// 最小二乘解算
    /// </summary>
    /// <param name="pos0"></param>
    /// <param name="epoch"></param>
    public void Lsq(Matrix pos0, Epoch epoch)
    {
        Matrix zero = new Matrix(4, 1);

        //协因数Q
        epoch.Q = pos0.Inverse((pos0.transposs(epoch.B) * epoch.P * epoch.B));
        
        //增量dx
        epoch.dx = zero - epoch.Q * pos0.transposs(epoch.B) * epoch.P * epoch.L;
        Matrix _dx = new Matrix(3, 1);
        _dx.arr[0, 0] = epoch.dx.arr[0, 0];
        _dx.arr[1, 0] = epoch.dx.arr[1, 0];
        _dx.arr[2, 0] = epoch.dx.arr[2, 0];
        
        //估计位置
        epoch.pos = pos0 + _dx;

        //后验残差V
        epoch.V = epoch.B * epoch.dx + epoch.L;
        Matrix vtpv = pos0.transposs(epoch.V) * epoch.P * epoch.V;
        //单位权中误差
        epoch.sigma0 = Math.Sqrt(vtpv.arr[0, 0] / (epoch.satNum - 4));
        epoch.sigma = new Matrix(4, 1);
        epoch.sigma.arr[0, 0] = epoch.sigma0 * Math.Sqrt(epoch.Q.arr[0, 0]) ;
        epoch.sigma.arr[1, 0] = epoch.sigma0 * Math.Sqrt(epoch.Q.arr[1, 1]) ;
        epoch.sigma.arr[2, 0] = epoch.sigma0 * Math.Sqrt(epoch.Q.arr[2, 2]) ;
        epoch.sigma.arr[3, 0] = epoch.sigma0 * Math.Sqrt(epoch.Q.arr[3, 3]) ;

        //PDOP值
        epoch.PDOP = Math.Sqrt(epoch.Q.arr[0, 0] + epoch.Q.arr[1, 1] + epoch.Q.arr[2, 2]);
    }
}

6、Form1.cs

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    DataCenter data = new DataCenter();

    private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {

    }

    private void 导入数据文件ToolStripMenuItem_Click(object sender, EventArgs e)
    {
        try
        {
            OpenFileDialog opf = new OpenFileDialog();
            opf.Filter = "文本文件|*.txt";
            opf.Title = "请选择要导入的数据文件";
            if (opf.ShowDialog() == DialogResult.OK)
            {
                StreamReader sr = new StreamReader(opf.FileName);
                string[] lines = sr.ReadLine().Trim().Split(',', ':', ':', '(');
                data.APPROX_POSITION = new Matrix(3, 1, new double[3, 1] {
                {double.Parse( lines[1])},{ double.Parse( lines[2]) },{ double.Parse( lines[3])} });

                sr.ReadLine();  //第二行跳过

                //每一次while循环读取一个历元的数据
                Epoch epoch = new Epoch();
                Sat sat = new Sat();
                data.Epoches = new List<Epoch>();
                while (!sr.EndOfStream)
                {
                    epoch = new Epoch();
                    epoch.sats = new List<Sat>();

                    lines = sr.ReadLine().Trim().Split(',', ':', ':');
                    if (lines == null)
                    {
                        break;
                    }
                    epoch.satNum = int.Parse(lines[1]);
                    epoch.gpsTime = int.Parse(lines[3]);
                    for (int i = 0; i < epoch.satNum; i++)
                    {
                        sat = new Sat();
                        lines = sr.ReadLine().Trim().Split(',', ':', ':');
                        sat.PRN = lines[0];
                        sat.stapos = new Matrix(3, 1, new double[3, 1] {
                        {double.Parse( lines[1])},{ double.Parse( lines[2]) },
                        { double.Parse( lines[3])} });
                        sat.satColck = double.Parse(lines[4]);
                        sat.elevation = double.Parse(lines[5]);
                        sat.cl = double.Parse(lines[6]);
                        sat.tropDely = double.Parse(lines[7]);
                        epoch.sats.Add(sat);
                    }
                    data.Epoches.Add(epoch);
                }
            }
        }
        catch (Exception)
        {
            MessageBox.Show("文件读取出错,请检查文件是否正确!!");
            throw;
        }
        
    }

    private void 最小二乘解算ToolStripMenuItem_Click(object sender, EventArgs e)
    {
        if (data.Epoches==null)
        {
            MessageBox.Show("请先导入数据");
            return;
        }

        Algorithm algorithm = new Algorithm();

        //遍历每一个历元,最小二乘解算
        for (int i = 0; i < data.Epoches.Count(); i++)
        {
            algorithm.CalBLP(data.APPROX_POSITION, data.Epoches[i]);
            algorithm.Lsq(data.APPROX_POSITION, data.Epoches[i]);
        }

        //将解算结果输出到表格
        dataGridView1.RowCount = data.Epoches.Count;
        for (int i = 0; i < data.Epoches.Count; i++)
        {
            dataGridView1.Rows[i].Cells[0].Value = data.Epoches[i].gpsTime;
            dataGridView1.Rows[i].Cells[1].Value = data.Epoches[i].pos.arr[0, 0];
            dataGridView1.Rows[i].Cells[2].Value = data.Epoches[i].sigma.arr[0, 0];
            dataGridView1.Rows[i].Cells[3].Value = data.Epoches[i].pos.arr[1, 0];
            dataGridView1.Rows[i].Cells[4].Value = data.Epoches[i].sigma.arr[1, 0];
            dataGridView1.Rows[i].Cells[5].Value = data.Epoches[i].pos.arr[2, 0];
            dataGridView1.Rows[i].Cells[6].Value = data.Epoches[i].sigma.arr[2, 0];
            dataGridView1.Rows[i].Cells[7].Value = data.Epoches[i].PDOP;
        }

    }

    private void 输出结果文件ToolStripMenuItem_Click(object sender, EventArgs e)
    {
        if (data.Epoches == null){
            MessageBox.Show("请先导入数据");
            return;
        }
        if (data.Epoches[0].B == null)
        {
            MessageBox.Show("请先进行最小二乘解算");
            return;
        }

        string Report = "";

        Report += "——————————————————————————————————————\n" +
            "———————————————— 最小二乘解算结果 ——————————————" +
            "\n——————————————————————————————————————\n" +
            "观测历元         X / m           σx / m           Y / m             σy / m          Z / m          σz / m    PDOP  \n";
       
        
        for (int i = 0; i < data.Epoches.Count; i++)
        {
            Report += data.Epoches[i].gpsTime.ToString() +"  :  "+
                data.Epoches[i].pos.arr[0, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[0, 0].ToString("0.0000") +"    "+
                    data.Epoches[i].pos.arr[1, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[1, 0].ToString("0.0000") +"    "+
                    data.Epoches[i].pos.arr[2, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[2, 0].ToString("0.0000") +"    "+
                data.Epoches[i].PDOP.ToString("0.0000") + "\n";
        }


        SaveFileDialog svf = new SaveFileDialog();
        svf.Filter = "文本文件|*.txt";
        if (svf.ShowDialog() == DialogResult.OK)
        {
            StreamWriter sw = new StreamWriter(svf.FileName);
            sw.Write(Report);
            sw.Flush();
        }

    }
}
  "\n——————————————————————————————————————\n" +
            "观测历元         X / m           σx / m           Y / m             σy / m          Z / m          σz / m    PDOP  \n";
       
        
        for (int i = 0; i < data.Epoches.Count; i++)
        {
            Report += data.Epoches[i].gpsTime.ToString() +"  :  "+
                data.Epoches[i].pos.arr[0, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[0, 0].ToString("0.0000") +"    "+
                    data.Epoches[i].pos.arr[1, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[1, 0].ToString("0.0000") +"    "+
                    data.Epoches[i].pos.arr[2, 0].ToString("0.0000") +"    "+ data.Epoches[i].sigma.arr[2, 0].ToString("0.0000") +"    "+
                data.Epoches[i].PDOP.ToString("0.0000") + "\n";
        }


        SaveFileDialog svf = new SaveFileDialog();
        svf.Filter = "文本文件|*.txt";
        if (svf.ShowDialog() == DialogResult.OK)
        {
            StreamWriter sw = new StreamWriter(svf.FileName);
            sw.Write(Report);
            sw.Flush();
        }

    }
}

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

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

相关文章

Spring Cloud Alibaba - Sentinel(一)

目录 一、Sentinel介绍 1、什么是Sentinel 2、Sentinel好处 3、Sentinel下载和安装 二、搭建Sentinel项目 1、创建项目cloudalibaba-sentinel-service8401 三、Sentinel流控规则 1、流控规则基本介绍 2、新增流控 2.1、QPS直接失败案例 2.2、线程数直接失败案例 3、…

CF1839B Lamps

思路 这道题我们可以利用贪心的思想。 我们这里把 a i a_i ai​理解为第 a i a_i ai​层灯。 在层数相同的灯被破坏之后&#xff0c;灯的个数就会减少到 0 0 0&#xff0c;所以它一定不会影响到之后下一层的灯。 所以&#xff0c;我们贪心的思路是&#xff1a; 将每一层&am…

FreeRTOS学习(五)

延时函数 vTaskDelay()&#xff1a;相对延时&#xff0c;指每次延时都是从执行函数vTaskDelay()开始&#xff0c;直到延时指定的时间结束。vTaskDelayUntil()&#xff1a;绝对延时&#xff0c;整个任务的运行周期看成一个整体&#xff0c;适用于需要按照一定频率运行的任务。 …

卡尔曼滤波与组合导航原理笔记(一)第二部分 卡尔曼滤波方程的推导

文章目录 三、卡尔曼滤波1、随机系统状态空间模型2、状态预测3、状态量测4、增益矩阵K与状态估计5、Kalman滤波公式汇总6、Kalman滤波流程图1.划分为左右两部分&#xff08;一阶矩和二阶矩&#xff09;2.划分为上下两部分&#xff08;时间更新、量测更新&#xff09; 7、Kalman…

Windows11安装kohya_ss详细步骤(报错、踩坑)

文章目录 笔者环境所需环境安装kohya_ss方式一&#xff1a;带有GUI的kohya_ss仓库方式二&#xff1a;kohya_ss核心仓库 笔者环境 OS&#xff1a;windows11Python&#xff1a;3.10.6CUDA11.6 所需环境 Python3.10.6GitCUDA11.6 安装kohya_ss 方式一&#xff1a;带有GUI的ko…

mybatis执行流程分析

mybatis全局配置文件 mybatis全局配置文件中涉及的标签如下图所示 配置文件解析 public static void main(String[] args) throws IOException {// 读取配置文件InputStream is Resources.getResourceAsStream("org/apache/ibatis/builder/MapperConfig1.xml");//…

chatgpt赋能python:Python多种颜色——提升SEO排名的技巧

Python多种颜色——提升SEO排名的技巧 在网站设计中&#xff0c;使用多种颜色可以提高用户体验和页面美观度。但你是否知道使用多种颜色还可以提高SEO排名呢&#xff1f;本文将介绍如何在Python代码中使用多种颜色来提高你的SEO排名。 什么是SEO&#xff1f; SEO的全称为“S…

chatgpt赋能python:Python备份文件夹:保障数据安全的最佳方法

Python备份文件夹&#xff1a;保障数据安全的最佳方法 数据备份是确保所有重要信息安全的关键部分。对于IT专业人士和计算机爱好者而言&#xff0c;文件夹备份是一项必不可少的任务。而Python是备份文件夹最流行的语言之一&#xff0c;因为它易于学习和使用。 在这篇文章中&am…

stable-diffusion基础问题记录

一、windows安装 1、启动 如果自己是anaconda&#xff0c;python版本不是3.10.6 conda create -n python_3_10_6 python3.10.6&#xff0c;创建一个这样的环境 修改webui-user.bat set PYTHOND:/software/Anaconda3/envs/python_3_10_6/python&#xff0c;把python换成这个…

【走进Linux的世界】Linux---基本指令(3)

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【Linux专栏】&#x1f388; 本专栏旨在分享学习Linux的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 date指令cal指…

12代CPU启用SR-IOV vGPU,实现一台电脑当七台用

背景 虚拟桌面基础设施&#xff08;VDI&#xff09;技术一般部署在服务器&#xff0c;可以实现多个用户连接到服务器上的虚拟桌面。随着桌面计算机性能的日益提升&#xff0c;桌面计算机在性能在很多场景下已经非常富余&#xff0c;足够同时满足多个用户同时使用的需求。实际项…

Redis的持久化详解

目录 一、Redis的持久化二、RDB&#xff08;Redis DataBase&#xff09;1、RDB快照原理2、RDB配置3、redis.conf 其他一些配置4、RDB的备份恢复5、RDB优缺点 三、AOF&#xff08;Append Of File&#xff09;1、AOF原理2、AOF配置3、AOF的备份恢复4、重写流程5、AOF优缺点 四、A…

MySQL | Windows服务器部署ZIP免安装版MySQL8.0+数据库笔记

Windows服务器部署ZIP免安装版MySQL8.0数据库笔记 文章目录 Windows服务器部署ZIP免安装版MySQL8.0数据库笔记下载MySQL压缩包编写配置文件环境变量初始化数据库安装MySQL服务安装错误&#xff1a;VCRUNTIME140_1.dll 登录 MySQL 下载MySQL压缩包 打开官网的下载页面&#xff…

POI报表的入门

POI报表的入门 理解员工管理的的业务逻辑 能够说出Eureka和Feign的作用 理解报表的两种形式和POI的基本操作熟练使用POI完成Excel的导入导出操作 员工管理 需求分析 企业员工管理是人事资源管理系统中最重要的一个环节&#xff0c;分为对员工入职&#xff0c;转正&#xff0c;离…

chatgpt赋能python:Python如何处理AI文件

Python如何处理AI文件 什么是AI文件&#xff1f; AI文件是Adobe Illustrator的标准文件格式。它包含了图形设计师所创建的矢量图形&#xff0c;这些矢量图形可以根据需要进行缩放和文件大小的调整。AI文件是专业印刷和设计领域中最常用的格式之一。 为什么要处理AI文件&…

深入ReentrantReadWriteLock

ReentrantReadWriteLock出现的原因 首先synchronized和ReentrantLock都是互斥锁&#xff0c;一个线程在获取锁资源之后另一个线程只能等待假设有一种情况是读多写少&#xff0c;并且确保线程安全。可以使用ReentrantReadWriteLock实现ReentrantReadWriteLock的特点是读读不互斥…

基于随身wifi的Tiny linux debian搭建教程

基于随身wifi的Tiny linux debian搭建教程 基于随身wifi的Tiny linux debian搭建教程基本信息进9008miko备份Qualcomm Premium Tool全分区备份 开adb刷debianssh连接扩展应用原版镜像测速ServerBox自动登录校园网 bug 基于随身wifi的Tiny linux debian搭建教程 基本信息 12芯…

Java8环境安装及配置

Java8环境安装及配置 一、下载JDK8二、安装三、环境变量配置四、验证 一、下载JDK8 本教程使用的是8u202版本&#xff0c;若需要其他版本可点击下方链接跳转下载。 Oracle下载&#xff0c;点击跳转选择版本 如下图所示&#xff0c;选择自己需要的版本下载 点击8u202版本 下载…

JavaSE进阶(day14,复习自用)

XML、XML解析、设计模式等 XMLXML概述XML的创建、语法规则XML文档约束方式一-DTD约束[了解]XML文档约束方式二-schema约束[了解] XML解析技术XML解析技术概述Dom4J解析XML文件Dom4J解析XML文件-案例实战 XML检索技术&#xff1a;Xpath设计模式&#xff1a;工厂模式设计模式&am…

C++算法:排序之一(插入、冒泡、快速排序)

C算法&#xff1a;排序 排序之一&#xff08;插入、冒泡、快速排序&#xff09; 文章目录 C算法&#xff1a;排序前言一、十大排序法性能二、各算法实现1、插入排序2、冒泡排序3、快速排序 原创文章&#xff0c;未经许可&#xff0c;严禁转载 前言 排序算法很多&#xff0c;一…