EC600U_esp32_iap_uart/EC600U_rtk/coordtrans.c

345 lines
12 KiB
C
Raw Permalink Normal View History

2024-02-05 17:39:56 +08:00
#include "coordtrans.h"
#include <math.h>
struct Datum g_coord_p[12]; // 椭球参数集保存结构
/*! 椭球参数初始化
* @param a
* @param f
* @param L0 线
*/
void DatumInitialize(struct Datum *newd, double a, double f, int l0) {
if (newd == NULL)
return;
double b = a - f * a;
double e12 = (a * a - b * b) / (a * a);
double e22 = (a * a - b * b) / (b * b);
// local BJ54 = new Datum(6378245, 1/298.3)
// local XA80 = new Datum(6378140, 1/298.257)
// local WGS84 = new Datum(6378137, 1/298.257223563)
// local WGS2000 = new Datum(6378137, 1/298.257222101)
newd->A = a; // 椭球长半轴
newd->F = f; // 椭球扁率
newd->B = b; // 椭球短半轴
newd->E12 = e12; // 椭球第一偏心率平方
newd->E22 = e22; // 椭球第二偏心率平方
newd->L0 = l0; // 本初子午线
newd->zoneWidth = 6; // 度带
}
/*! 设置度带
* @param zoneWidth
*/
void DatumSetZoneWidth(struct Datum *newd, int zoneWidth) {
if (newd == NULL)
return;
if (zoneWidth != 6 && zoneWidth != 3) {
printf("带宽应当为6或者为3");
} else {
newd->zoneWidth = zoneWidth;
}
}
/*! 获取度带带号
* @param l
* @return
*/
int DatumGetZoneNo(struct Datum *newd, double l) {
// 6度带
// 这里应为向下取整
if (newd->zoneWidth == 6) {
return floor((l + 6) / 6);
}
// 3度带
return floor((l + 1.5) / 3);
}
/*! 椭球参数计算
* @param val
* @param L0 线
* @return
*/
void DatumEllipsoidal(int L0, enum GEODETIC_COORD_SYSTEM val, struct Datum *out) {
DatumInitialize(&g_coord_p[BJ54], 6378245, 1 / 298.3, L0);
DatumInitialize(&g_coord_p[XA80], 6378140, 1 / 298.257, L0);
DatumInitialize(&g_coord_p[WGS84], 6378137, 1 / 298.257223563, L0);
DatumInitialize(&g_coord_p[WGS2000], 6378137, 1 / 298.257222101, L0);
/*
double A; // 椭球长半轴
double F; // 椭球扁率
double B; // 椭球短半轴
double E12; // 椭球第一偏心率平方
double E22; // 椭球第二偏心率平方
int L0; // 本初子午线
int zoneWidth; // 度带
*/
/*
printf("XA80: A:%lf, B:%lf, F:%lf, E12:%lf, E22:%lf, L0:%d, zoneWidth:%d\n",
g_coord_p[XA80].A, g_coord_p[XA80].B, g_coord_p[XA80].F, g_coord_p[XA80].E12,
g_coord_p[XA80].E22, g_coord_p[XA80].L0, g_coord_p[XA80].zoneWidth);
printf("WGS84: A:%lf, B:%lf, F:%lf, E12:%lf, E22:%lf, L0:%d, zoneWidth:%d\n",
g_coord_p[WGS84].A, g_coord_p[WGS84].B, g_coord_p[WGS84].F, g_coord_p[WGS84].E12,
g_coord_p[WGS84].E22, g_coord_p[WGS84].L0, g_coord_p[WGS84].zoneWidth);
*/
if (out == NULL)
return;
*out = g_coord_p[val];
}
/*! 输出平面坐标
* @param p p.B:p.L:
* @param config
* @return
*/
struct Point d2p(struct Point *p, struct ConfigParam *config) {
struct Point zj = BLH2XYZ(p, &g_coord_p[config->gcsSrc]);
struct Point xzj = XYZ2XYZ(&zj, config->dx, config->dy, config->dz, config->wx, config->wy, config->wz, config->k);
struct Point xdd = XYZ2BLH(&xzj, &g_coord_p[config->gcsDst]);
struct Point xpm = BL2xy(&xdd, &g_coord_p[config->gcsDst], config->height, NULL, NULL);
return xpm;
}
/*! 地心大地坐标系 转换到 地心直角坐标系
* @param pointBLH B: L: H:
* @param datum
*/
struct Point BLH2XYZ(struct Point *pointBLH, struct Datum *datum) {
double a = datum->A;
double e12 = datum->E12;
double radB = pointBLH->x / 180 * MATH_PI;
double radL = pointBLH->y / 180 * MATH_PI;
double H = pointBLH->z;
double N = a / sqrt(1 - e12 * sin(radB) * sin(radB)); // 卯酉圈半径
struct Point res;
res.x = (N + H) * cos(radB) * cos(radL);
res.y = (N + H) * cos(radB) * sin(radL);
res.z = (N * (1 - e12) + H) * sin(radB);
return res;
}
/*! 空间直角坐标系间的 七参数转换
* @note:
* @param: dX, dY, dZ:
* @param: wX, wY, wZ: ()
* @param: Kppm: ppm 1000000
*/
struct Point
XYZ2XYZ(struct Point *source, double dX, double dY, double dZ, double wX, double wY, double wZ, double Kppm) {
double X = source->x;
double Y = source->y;
double Z = source->z;
// wX = wX or 0
// wY = wY or 0
// wZ = wZ or 0
// Kppm = Kppm or 0
wX = wX / 3600 / 180 * MATH_PI;
wY = wY / 3600 / 180 * MATH_PI;
wZ = wZ / 3600 / 180 * MATH_PI;
// wX = wX / math.PI * 3600 * 180
// wY = wY / math.PI * 3600 * 180
// wZ = wZ / math.PI * 3600 * 180
// Kppm = Kppm - 1
struct Point res;
res.x = X + dX + Kppm * X - wY * Z + wZ * Y; // Kppm 公式中尺度因子为 1+Kppm
res.y = Y + dY + Kppm * Y + wX * Z - wZ * X;
res.z = Z + dZ + Kppm * Z - wX * Y + wY * X;
return res;
}
/*! 地心直角坐标系 转换到 地心大地坐标系
* @note: 2 https://wenku.baidu.com/view/30a08f9ddd88d0d233d46a50.html
*/
struct Point
XYZ2BLH(struct Point *pointXYZ, struct Datum *datum) {
double X = pointXYZ->x;
double Y = pointXYZ->y;
double Z = pointXYZ->z;
double L = atan(Y / X);
// 弧度转角度
double degL = L * 180 / MATH_PI;
// Y值为正 东半球, 否则西半球
if (Y > 0) {
while (degL < 0) {
degL = degL + 180;
}
while (degL > 180) {
degL = degL - 180;
}
} else {
while (degL > 0) {
degL = degL - 180;
}
while (degL < -180) {
degL = degL + 180;
}
}
double a = datum->A;
double b = datum->B;
double e12 = datum->E12;
double e22 = datum->E22;
double tgU = Z / (sqrt(X * X + Y * Y) * sqrt(1 - e12));
double U = atan(tgU);
double tgB = (Z + b * e22 * pow(sin(U), 3)) / (sqrt(X * X + Y * Y) - a * e12 * pow(cos(U), 3));
double B = atan(tgB);
double degB = B * 180 / MATH_PI; // 弧度转角度
if (Z > 0) { // Z值为正 北半球, 否则南半球
while (degB < 0) {
degB = degB + 90;
}
while (degB > 90) {
degB = degB - 90;
}
} else {
while (degB > 0) {
degB = degB - 90;
}
while (degB < -90) {
degB = degB + 90;
}
}
while (degB < 0) {
degB = degB + 360;
}
while (degB > 360) {
degB = degB - 360;
}
double N = a / sqrt(1 - e12 * sin(B) * sin(B)); // 卯酉圈半径
double H = 0;
// B接近极区 在±90°附近
if (fabs(degB) > 80) {
H = Z / sin(B) - N * (1 - e12);
} else {
H = sqrt(X * X + Y * Y) / cos(B) - N;
}
struct Point res;
res.x = degB;
res.y = degL;
res.z = H;
return res;
}
/*! 地心大地坐标系 转换到 大地平面坐标系
* @note:
* @param: prjHeight:
* http://www.cnblogs.com/imeiba/p/5696967.html
*/
struct Point
BL2xy(struct Point *pointBLH, struct Datum *datum, double prjHeight, double *offsetX_o, double *offsetY_o) {
double a = datum->A;
double b = datum->B;
double e12 = datum->E12;
double e22 = datum->E22;
double offsetX;
double offsetY;
// prjHeight = prjHeight or 0
// offsetX = offsetX or 0
if (offsetX_o == NULL)
offsetX = 0;
else
offsetX = *offsetX_o;
// offsetY = offsetY or 500000
if (offsetY_o == NULL)
offsetY = 500000;
else
offsetY = *offsetY_o;
int L0 = datum->L0;
if (L0 == 0) {
int zoneNo = DatumGetZoneNo(datum, pointBLH->y);
L0 = (zoneNo - 0.5) * datum->zoneWidth;
datum->L0 = L0;
}
double radL0 = (((double)L0 / (double)180 )* MATH_PI);
double radB = pointBLH->x / 180 * MATH_PI;
double radL = pointBLH->y / 180 * MATH_PI;
double N = a / sqrt(1 - e12 * sin(radB) * sin(radB)); // 卯酉圈半径
double T = tan(radB) * tan(radB);
double C = e22 * cos(radB) * cos(radB);
double A = (radL - radL0) * cos(radB);
double M = a * ((1 - e12 / 4 - 3 * e12 * e12 / 64 - 5 * e12 * e12 * e12 / 256) * radB -
(3 * e12 / 8 + 3 * e12 * e12 / 32 + 45 * e12 * e12 * e12 / 1024) * sin(2 * radB) +
(15 * e12 * e12 / 256 + 45 * e12 * e12 * e12 / 1024) * sin(4 * radB) -
(35 * e12 * e12 * e12 / 3072) * sin(6 * radB));
// x,y的计算公式见孔祥元等主编武汉大学出版社2002年出版的《控制测量学》的第72页书的的括号有问题( 和 [ 应该交换
double x = M + N * tan(radB) *
(A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 +
(61 - 58 * T + T * T + 600 * C - 330 * e22) * A * A * A * A * A * A / 720); // ??x和y的计算公式
double y = N * (A + (1 - T + C) * A * A * A / 6 + (5 - 18 * T * T * T + 72 * C - 58 * e22) * A * A * A * A * A / 120);
struct Point res;
res.x = offsetX + x * (b + prjHeight) / b;
res.y = offsetY + y * (b + prjHeight) / b;
res.z = pointBLH->z;
return res;
}
/*! 输出大地坐标
*/
struct Point p2d(struct Point *pointxy, struct ConfigParam *config) {
struct Point bl = xy2BL(pointxy, &g_coord_p[config->gcsSrc], config->height, NULL, NULL);
struct Point zj = BLH2XYZ(&bl, &g_coord_p[config->gcsSrc]);
struct Point xzj =
XYZ2XYZ(&zj, -config->dx, -config->dy, -config->dz, -config->wx, -config->wy, -config->wz, -config->k);
struct Point xdd = XYZ2BLH(&xzj, &g_coord_p[config->gcsDst]);
return xdd;
}
/*! 大地平面坐标系 转换到 地心大地坐标系
* @param: prjHeight:
* http://www.cnblogs.com/imeiba/p/5696967.html
*/
struct Point xy2BL(struct Point *pointxy, struct Datum *datum, double prjHeight, double *offsetX_o, double *offsetY_o) {
double a = datum->A;
double b = datum->B;
double e12 = datum->E12;
double e22 = datum->E22;
double offsetX;
double offsetY;
// prjHeight = prjHeight or 0
// offsetX = offsetX or 0
if (offsetX_o == NULL)
offsetX = 0;
else
offsetX = *offsetX_o;
// offsetY = offsetY or 500000
if (offsetY_o == NULL)
offsetY = 500000;
else
offsetY = *offsetY_o;
double e1 = (1 - sqrt(1 - e12)) / (1 + sqrt(1 - e12));
int L0 = datum->L0;
double radL0 = (((double)L0 / (double)180) * MATH_PI);
// 带内大地坐标
double Y = fmod(pointxy->y, 1000000.0000000); // pointxy->y % 1000000.0000000;
double x = (pointxy->x - offsetX) * b / (b + prjHeight);
double y = (Y - offsetY) * b / (b + prjHeight);
double u = x / (a * (1 - e12 / 4 - 3 * e12 * e12 / 64 - 5 * e12 * e12 * e12 / 256));
double fai = u + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * u) +
(21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * u) + (151 * e1 * e1 * e1 / 96) * sin(6 * u) +
(1097 * e1 * e1 * e1 * e1 / 512) * sin(8 * u);
double C = e22 * cos(fai) * cos(fai);
double T = tan(fai) * tan(fai);
double N = a / sqrt(1 - e12 * sin(fai) * sin(fai));
double R = a * (1 - e12) /
sqrt((1 - e12 * sin(fai) * sin(fai)) * (1 - e12 * sin(fai) * sin(fai)) * (1 - e12 * sin(fai) * sin(fai)));
double D = y / N;
double L = radL0 + (D - (1 + 2 * T + C) * D * D * D / 6 +
(5 - 2 * C + 28 * T - 3 * C * C + 8 * e22 + 24 * T * T) * D * D * D * D * D / 120) /
cos(fai);
double B = fai - (N * tan(fai) / R) *
(D * D / 2 - (5 + 3 * T + 10 * C - 4 * C * C - 9 * e22) * D * D * D * D / 24 +
(61 + 90 * T + 298 * C + 45 * T * T - 256 * e22 - 3 * C * C) * D * D * D * D * D * D / 720);
struct Point res;
res.x = B * 180 / MATH_PI;
res.y = L * 180 / MATH_PI;
res.z = pointxy->z;
return res;
}