C语言空间直角坐标与大地坐标的相互转换实验报告

本文主要是介绍C语言空间直角坐标与大地坐标的相互转换实验报告,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C语言空间直角坐标与大地坐标的相互转换实验报告


源代码附文末或见Github:⬇️
直角坐标与大地坐标的相互转化

branch:

  • master: 正常读取输入。
  • file_read: 从csv文件获取输入。

一、实验名称:空间直角坐标与大地坐标的相互转换

二、实验目的与要求

三、实验内容

  1. 已知某点的空间直角坐标(X、Y、Z), X = 1546823.34,Y = -3879765.13,Z = 4804185.05,椭球参数a= 6378137m,b= 6356752.3141m,采用下式将其转换为大地坐标(L、B)
    在这里插入图片描述

  2. 根据1所求的大地坐标(L、B)及大地高H及相同的椭球参数将其转化为空间直角坐标(X、Y、Z),计算式为:在这里插入图片描述

四、试验设备与环境

五、实验步骤

1 && 2 分析和算法设计

公理1 :由于 L B H LBH LBH X Y Z XYZ XYZ 是相同物理量在相同参考系下的不同表示方法,因此在空间直角坐标系上的某点 ( X Y Z ) (XYZ) (XYZ),有且只有一个 L B H LBH LBH在大地坐标系与之对应。

推论1 只要 X Y Z XYZ XYZ确定, L B H LBH LBH的值就只与 X Y Z XYZ XYZ的取值有关,而与任何其他变量无关。

下面展开讨论:

由方程可知,虽然 L L L 可以直接求解,但求 B B B 的方程中,包含 B B B 本身,为了验证方程(2)是否可以求解,我们先将方程(2)化简:
B = arctan ⁡ Z + N e 2 sin ⁡ B X 2 + Y 2 tan ⁡ B = Z + N e 2 sin ⁡ B X 2 + Y 2 (2) \begin{matrix} B = \arctan\dfrac{Z + Ne^2\sin B}{\sqrt{X^2 + Y^2}}\\\tan B = \dfrac{Z + Ne^2\sin B}{\sqrt{X^2 + Y^2}} \tag{2} \end{matrix} B=arctanX2+Y2 Z+Ne2sinBtanB=X2+Y2 Z+Ne2sinB(2)
为了方便化简,我们令
X 2 + Y 2 = R (3) \sqrt{X^2 + Y^2} = R \tag{3} X2+Y2 =R(3)
又由于
N = a 1 − e 2 sin ⁡ 2 B (4) N = \frac{a}{\sqrt{1-e^2\sin^2B}} \tag{4} N=1e2sin2B a(4)
将(4)、(3)带入(2)中
tan ⁡ B = Z R + e 2 R N s i n B = Z R + e 2 R sin ⁡ B 1 − e 2 sin ⁡ 2 B = Z R + a e 2 R 1 1 sin ⁡ 2 B − e 2 (5) \begin{matrix} \tan B &= \dfrac{Z}{R} + \dfrac{e^2}{R}NsinB\\&=\dfrac{Z}{R} + \dfrac{e^2}{R}\dfrac{\sin B}{\sqrt{1-e^2\sin^2B}} \\&= \dfrac{Z}{R} + \dfrac{ae^2}{R}\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}} \tag{5} \end{matrix} tanB=RZ+Re2NsinB=RZ+Re21e2sin2B sinB=RZ+Rae2sin2B1e2 1(5)
推论1 可知,上式中 Z R \dfrac ZR RZ a e 2 R \dfrac{ae^2}{R} Rae2 B B B 的值无关,为了方便表示,我们令
Z R = C , a e 2 R = K . \frac{Z}{R} = C, \quad \dfrac{ae^2}{R}=K. RZ=C,Rae2=K.

上式化简为:

tan ⁡ B = K 1 1 sin ⁡ 2 B − e 2 + C (6*) \tan B = K\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}}+C \tag{6*} tanB=Ksin2B1e2 1+C(6*)

在上式中,由于 tan ⁡ B sin ⁡ B = cos ⁡ B \dfrac{\tan B}{\sin B} = \cos B sinBtanB=cosB 不为常量,故无法找到一组 k 1 , k 2 ∈ R k_1,k_2\in R k1,k2R 满足 k 1 tan ⁡ B + k 2 sin ⁡ B = 0 k_1\tan B + k_2 \sin B = 0 k1tanB+k2sinB=0。因此上式左右两端含有变量 B B B 的因子是非线性相关的,所以不可以直接通过方程求解。

由此,为得到 B B B 的值,必须通过其他方式求解。

利用计算机程序可以通过不断猜测 B B B 的值,带入(6)式中,如果(6)左右两端的值足够接近,且由 推论1, 此时猜测到的值 B 猜 B_猜 B 必然也会接近实际的值 B 实 B_实 B

我们将上述求 B B B 的方法称为遍历法,接下来我们设计遍历法的算法。

遍历法

由题设条件,可知 B ∈ ( − π 2 , π 2 ) B \in (-\dfrac{\pi}{2}, \dfrac{\pi}{2}) B(2π,2π)

  • 一个可执行的算法必须由有限步构成。

但是可能出现的 B B B 的值确是无限的,为此,我们将 B B B 进行离散化 ,即每隔很小的值 δ ( d e l t a ) \delta (delta) δ(delta) ,取一个新的值 B B B

为了方便表示,我们设计损失函数 L o s s Loss Loss用来计算方程左右两端的差值。
L o s s ( B ) = tan ⁡ B − K 1 1 sin ⁡ 2 B − e 2 + C (7) Loss(B) = \tan B - K\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}}+C \tag{7} Loss(B)=tanBKsin2B1e2 1+C(7)
当函数Loss的值很小时,则认为此时的 B B B达到了目标值 B 实 B_实 B

用数学语言可以描述为:

若取一个足够小的 ε > 0 \varepsilon > 0 ε>0

对于
B 0 = − π 2 + k δ , 且 ( k ∈ N ∗ ) B 0 < π 2 B_0 = -\frac{\pi}{2} + k\delta, 且(k\in N^*)B_0 < \dfrac{\pi}{2} B0=2π+kδ,(kN)B0<2π
时,如果满足
∣ L o s s ( B 0 ) ∣ < ε |Loss(B_0)| <\varepsilon Loss(B0)<ε
则认为 B = B 0 B = B_0 B=B0


梯度下降法

使用遍历法​求解时,往往会由于需要遍历的变量的数量太多,导致程序的运行时间太长,难以得到正确的结果。当我们遍历时,只是盲目的遍历,我们并不知道到底经过多少个 δ \delta δ 后,能够使 L o s s Loss Loss函数的绝对值变小。这是因为,我们只用 L o s s Loss Loss 函数的值与0比较,这样的比较获得的信息量是十分少的,为了从 L o s s Loss Loss 函数中获取更多信息,我们可以考虑将 L o s s Loss Loss 函数的绝对值进行求导,将其导函数记为 L o s s ′ Loss' Loss

我们的目标是使 L o s s Loss Loss 函数的值趋近于0,对于一个点 ( B , L o s s ( B ) ) (B, Loss(B)) (B,Loss(B)) ,如果 L o s s ( B ) > 0 Loss(B) > 0 Loss(B)>0,我们应该调整 B B B 的值,使得 L o s s ( B ) Loss(B) Loss(B)减少,相反,如果 L o s s ( B ) < 0 Loss(B) < 0 Loss(B)<0, 我们应该调整 B B B的值,使 L o s s ( B ) Loss(B) Loss(B)增加。

即:

  • L o s s ( B ) > 0 Loss(B) > 0 Loss(B)>0, B B B L o s s ′ ( B ) < 0 Loss'(B) < 0 Loss(B)<0的方向移动。
  • L o s s ( B ) < 0 , B Loss(B) < 0, B Loss(B)<0,B L o s s ′ ( B ) > 0 Loss'(B) > 0 Loss(B)>0 的方向移动。

以点 X = − 2758534 , Y = − 4132154 , Z = 3986124 X = -2758534, Y = -4132154, Z = 3986124 X=2758534,Y=4132154,Z=3986124为例,

绘制出 L o s s Loss Loss函数的图像:

在这里插入图片描述

发现 L o s s Loss Loss函数并不是简单的单调函数,用上述算法有可能达到一个使 L o s s ( B ) Loss(B) Loss(B)为0的点,也有可能达到函数的一个极值点,并不是在所有情况下都能符合条件。


二分法

虽然梯度下降法无法实现,但这给我们进一步实现程序提供了思路,即让 B B B 的取值变得更加具有方向性

由于当 B → − π 2 B\to -\dfrac{\pi}{2} B2π 时,
L o s s ( B ) → − ∞ Loss(B) \to -\infin Loss(B)
B → π 2 B\to \dfrac{\pi}{2} B2π时,
L o s s ( B ) → + ∞ Loss(B) \to +\infin Loss(B)+
又由推论2可知,在区间 ( − π 2 , π 2 ) (-\dfrac{\pi}{2}, \dfrac{\pi}{2}) (2π,2π内, L o s s ( B ) Loss(B) Loss(B)有且只有一个一个零点。可以很容易想到二分法的求解方式:

  • L o o p S t a r t : Loop Start: LoopStart:
  • m i d = r i g h t + l e f t 2 mid = \dfrac{right +left}2 mid=2right+left
  • i f L o s s ( m i d ) > ε , l e f t = m i d if \quad Loss(mid) > \varepsilon , left = mid ifLoss(mid)>ε,left=mid
  • e l s e i f L o s s ( m i d ) < − ε , r i g h t = m i d else \quad if \quad Loss(mid) < -\varepsilon, right = mid elseifLoss(mid)<ε,right=mid
  • e l s e B = m i d else\quad B = mid elseB=mid
  • : L o o p E n d :LoopEnd :LoopEnd

迭代法

由于式(6)
tan ⁡ B = K 1 1 sin ⁡ 2 B − e 2 + C (6*) \tan B = K\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}}+C \tag{6*} tanB=Ksin2B1e2 1+C(6*)
如果我们令方程左边的 B = B n + 1 B = B_{n+1} B=Bn+1 方程右边的 B = B n B = B_n B=Bn,就可以得到一个关于 B B B 的迭代方程。
B n + 1 = arctan ⁡ ( K 1 1 sin ⁡ 2 B n − e 2 + C ) (8*) B_{n+1} = \arctan\left(K\frac{1}{\sqrt{\dfrac{1}{\sin^2B_n}}-e^2}+C\right) \tag{8*} Bn+1=arctanKsin2Bn1 e21+C(8*)
我们称上面的递推式为函数 F o r w a r d ( B ) Forward(B) Forward(B)

以点 X = − 2758534 , Y = − 4132154 , Z = 3986124 X = -2758534, Y = -4132154, Z = 3986124 X=2758534,Y=4132154,Z=3986124为例,

绘制出 F o r w a r d Forward Forward函数的图像:

上图中粗线为 F o r w a r d Forward Forward函数,虚线为直线 B n + 1 = B n B_{n+1} = B_n Bn+1=Bn

设曲线 F o r w a r d Forward Forward 与 直线 B n + 1 = B n B_{n+1} = B_n Bn+1=Bn的交点为 A A A A A A的横坐标为 x 0 x_0 x0,很显然 A A A是函数 F o r w a r d Forward Forward一阶不动点。由递推函数的性质可知,

  • B n < x 0 B_n < x_0 Bn<x0 时, B n + 1 > B n B_{n+1} > B_n Bn+1>Bn

  • B n > x 0 B_n > x_0 Bn>x0时, B n + 1 < B n B_{n+1} < B_n Bn+1<Bn

  • B n = x 0 B_n = x_0 Bn=x0时, B n + 1 = B n B_{n+1} = B_n Bn+1=Bn

    因此如果迭代次数无穷大,那么 B n B_n Bn的值会无限接近于 x 0 x_0 x0或等于 x 0 x_0 x0

    我们用 P y t h o n Python Python模拟一下迭代过程:

    from math import *
    import matplotlib.pyplot as plta = 6378137
    b = 6356752
    e = sqrt((a * a - b * b) / (a * a))
    X = -2758534
    Y = -4132154
    Z = 3986124
    R = sqrt(X * X + Y * Y)
    K = a * e * e / R
    C = Z / R
    x = []
    y = []def forWard(x):N = a / sqrt(1 - e * e * sin(B) * sin(B))tmp = sqrt(1/(sin(x) * sin(x)) - e * e)return atan(K / tmp + 0.1) + CB = C
    times = 100
    t = 0
    while True:t += 1nextB = forWard(B)#plt.plot(t, nextB, '+')B = nextBif t >= times:breakx.append(t)y.append(B)
    plt.xlabel("times")
    plt.ylabel("B")
    plt.plot(x, y)
    plt.show()
    

在这里插入图片描述

理论和实践都告诉我们,使用迭代公式计算时, B B B 会趋近于某一个特殊的值 x 0 x_0 x0

下证:该 x 0 x_0 x0就是我们的目标值 B 实 B_实 B

​ 当 B = x 0 B = x_0 B=x0时,由于 x 0 x_0 x0 F o r w a r d Forward Forward 函数的一阶不动点,故有
tan ⁡ B = K 1 1 sin ⁡ 2 B − e 2 + C \tan B = K\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}}+C tanB=Ksin2B1e2 1+C
​ 将上式带入 L o s s Loss Loss函数中,可以得到 L o s s ( B ) = 0 Loss(B) = 0 Loss(B)=0,又由推论1知这样的 B B B是唯一的,因此 B = x 0 B = x_0 B=x0 就是所求值。

​ 证毕。

(上述证明其实显而易见,用迭代法的关键是证明一阶不动点存在, 但是该证明过程较为复杂,我们可以在物理领域内使用反证法:如果不存在一阶不动点,那么函数 L o s s Loss Loss就没有解,这与实际情况不符,故一定存在一阶不动点)。

第二小问直接带入公式计算。

3流程图

遍历法:在这里插入图片描述

二分法:
在这里插入图片描述

迭代法:
在这里插入图片描述

4、 程序代码

本部分只使用迭代法完成代码。

//
//  main.cpp
//  ChangePosition
//
//  Created by Sumbrella on 2020/4/28.
//  Copyright © 2020 Sumbrella All rights reserved.
//
# include <stdio.h>
# include <math.h>typedef long long ll;const ll a = 6378137;
const ll b = 6356752;
const double epslion = 1e-15;
double e, R, K, C;// 配置需要使用的中间参数。
void configure(double X, double Y, double Z)
{e = sqrt((a * a - b * b) / (a * a));R = sqrt(X * X + Y * Y);K = a * e * e / R;C = Z / R;
}// 迭代法迭代一次
double Forward(double B)
{double tmp = sqrt(1 / (sin(B) * sin (B)) - e * e );return atan(K / tmp + C);
}void inputXYZ(double *X,double *Y, double *Z)
{printf("请输入X,Y,Z的值>>>(空格隔开)");scanf("%lf %lf %lf", X, Y, Z);
}void getLBH(double X, double Y, double Z, double *pN, double *pL, double *pB, double *pH)
{double B = C;                   // 让B的初始值为Cwhile (1){double nB = Forward(B);if (nB - B < epslion && nB - B > -epslion)break;B = nB;}double N = a / sqrt(1 - e * e * sin(B) * sin(B));double L = atan2(Y, X);double H = R / cos(B) - N;*pN = N; *pL = L; *pB = B; *pH = H;
}void showLBH(double L, double B, double H)
{printf("转换得到的大地坐标:\n");printf("L = %.3f, B = %.3f, H = %.3f\n", L, B, H);
}void showXYZ(double N, double L, double B, double H)
{double X = (N + H) * cos(B) * cos(L);double Y = (N + H) * cos(B) * sin(L);double Z = (N * (1 - e * e) + H) * sin(B);printf("转换后得到的空间坐标:\n");// printf("N = %.6f", N);printf("X = %.6f, Y = %.6f, Z = %.6f\n", X, Y, Z);
}int main()
{double X, Y, Z;double N, L, B, H;                // 定义变量inputXYZ(&X, &Y, &Z);             // 获取XYZ的值configure(X, Y, Z);               // 配置参数getLBH(X, Y, Z, &N, &L, &B, &H);  // 计算LBH的值showLBH(L, B, H);                 // 输出LBH的值showXYZ(N, L, B, H);              // 输出计算得到的XYZ的值return 0;
}

5、 主要函数和变量说明

主要函数说明见上图。
变量和常量说明:
(X,Y,Z)、(L,B,H)分别对应直角坐标和大地坐标下的坐标值。
C = Z / R, K = ae^2 / R。

6、 程序运行结果

在这里插入图片描述
转换后得到的坐标与原始数据完全一致,认为程序通过。

这篇关于C语言空间直角坐标与大地坐标的相互转换实验报告的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/703918

相关文章

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

使用SQL语言查询多个Excel表格的操作方法

《使用SQL语言查询多个Excel表格的操作方法》本文介绍了如何使用SQL语言查询多个Excel表格,通过将所有Excel表格放入一个.xlsx文件中,并使用pandas和pandasql库进行读取和... 目录如何用SQL语言查询多个Excel表格如何使用sql查询excel内容1. 简介2. 实现思路3

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

基于C#实现将图片转换为PDF文档

《基于C#实现将图片转换为PDF文档》将图片(JPG、PNG)转换为PDF文件可以帮助我们更好地保存和分享图片,所以本文将介绍如何使用C#将JPG/PNG图片转换为PDF文档,需要的可以参考下... 目录介绍C# 将单张图片转换为PDF文档C# 将多张图片转换到一个PDF文档介绍将图片(JPG、PNG)转