物联网-ATGM336H-6N-22-定位芯片

■ 经度维度

科普-经度维度

■ ATGM336H-6N 单北斗定位导航模块简介

单北斗定位导航模块用户手册
该系列模块产品基于中科微第六代SOC单北斗芯片AT6668B,支持北斗二号和北斗三号(B1I和B1C)信号。
ATGM336H-6N单北斗系列模块应用了全新的导航一体化SOC单芯片技术,可以满足高精度定位、高精度授时的应用
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

■ NMEA-0183 协议解析(NMEA4.1)

■ 帧格式形如:$aaccc,ddd,ddd,…,ddd*hh(CR)(LF)

1、“$”:帧命令起始位
2、aaccc:地址域,前两位为识别符(aa),后三位为语句名(ccc)
3、ddd…ddd:数据
4、“*”:校验和前缀(也可以作为语句数据结束的标志)
5、hh:校验和(check sum),$与*之间所有字符 ASCII 码的校验和(各字节做异或运算,得到校验和后,再转换 16 进制格式的 ASCII 字符)
6(CR)(LF):帧结束,回车和换行符

■ 地址域-识别符(aa)表示(GPS/BD/GLONASS/多星联合定位)

1)单GPS
$GPGGA,062938.00,3110.4700719,N,12123.2657056,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*502)单BD
$BDGGA,062938.00,3110.4700719,N,12123.2657056,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*43)单GLONASS
$GLGGA,062938.00,3110.4700719,N,12123.2657056,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*44)多星联合定位
$GNGGA,062938.00,3110.4700719,N,12123.2657056,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*4

■ NMEA-0183 常用命令如表所示:

在这里插入图片描述

■ XXGGA GPS定位数据

数据详解:$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*xx<CR><LF>
$GPGGA:起始引导符及语句格式说明(本句为GPS定位数据);
  <1> UTC 时间,格式为hhmmss.sss;
  <2> 纬度,格式为ddmm.mmmm(第一位是零也将传送);
  <3> 纬度半球,N 或S(北纬或南纬)
  <4> 经度,格式为dddmm.mmmm(第一位零也将传送);
  <5> 经度半球,E 或W(东经或西经)
  <6> 定位质量指示,0=定位无效,1=定位有效;
  <7>使用卫星数量,从0012(第一个零也将传送)
  <8>水平精确度,0.599.9
  <9>天线离海平面的高度,-9999.99999.9米M指单位米
  <10>大地水准面高度,-9999.99999.9米M指单位米
  <11>差分GPS数据期限(RTCMSC-104),最后设立RTCM传送的秒数量
  <12>差分参考基站标号,从00001023(首位0也将传送)。

解析内容:
  第910 个字段,海平面高度和大地水准面高度,单位是米
GGA信息:UTC时分秒、经纬度、GPS状态、卫星数量、高程、差分延迟、基站号此字段主要表示时间、经纬度位置、解算状态、卫星颗数等相关信息,XX因模式的不同而不同
(1)单GPS
$GPGGA,062938.00,3110.4700719,N,12123.2657056,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*502)单BD
$BDGGA,062938.00,3110.4700719,N,12123.2657056,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*43)单GLONASS
$GLGGA,062938.00,3110.4700719,N,12123.2657056,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*44)多星联合定位
$GNGGA,062938.00,3110.4700719,N,12123.2657056,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*4

在这里插入图片描述

■ XXGSA 当前卫星信息

GSA信息:模式、定位状态、

例:$GPGSA,A,3,01,20,19,13,,,,,,,,,40.4,24.4,32.2*0A
 
字段0:$GPGSA,语句ID,表明该语句为GPS DOP and Active Satellites(GSA)当前卫星信息   
字段1:定位模式,A=自动手动2D/3D,M=手动2D/3D   
字段2:定位类型,1=未定位,2=2D定位,3=3D定位   
字段3:PRN码(伪随机噪声码),第1信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段4:PRN码(伪随机噪声码),第2信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段5:PRN码(伪随机噪声码),第3信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段6:PRN码(伪随机噪声码),第4信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段7:PRN码(伪随机噪声码),第5信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段8:PRN码(伪随机噪声码),第6信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段9:PRN码(伪随机噪声码),第7信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段10:PRN码(伪随机噪声码),第8信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段11:PRN码(伪随机噪声码),第9信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段12:PRN码(伪随机噪声码),第10信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段13:PRN码(伪随机噪声码),第11信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段14:PRN码(伪随机噪声码),第12信道正在使用的卫星PRN码编号(00)(前导位数不足则补0)   
字段15:PDOP综合位置精度因子(0.5 - 99.9)   
字段16:HDOP水平精度因子(0.5 - 99.9)   
字段17:VDOP垂直精度因子(0.5 - 99.9)   
字段18:校验值
用于解算卫星信息。
(1) 单GPS
$GPGSA,M,3,25,14,15,18,31,27,09,21,22,12,1.5,0.9,1.3*30
(2) 单BD
$BDGSA,M,3,141,143,144,146,147,148,149,150,2.7,1.7,2.2*2B
(3) 单GLONASS
$GLGSA,M,3,47,58,55,46,53,57,56,1.7,1.1,1.3*2D
(4) 多星联合定位
$GNGSA,M,3,27,03,16,21,06,19,31,13,23,1.0,0.6,0.822
$GNGSA,M,3,141,142,143,144,147,148,150,1.0,0.6,0.810
$GNGSA,M,3,47,56,55,46,53,44,57,1.0,0.6,0.829

在这里插入图片描述

■ XXVTG 地面速度信息

$GPVTG,<1>,T,<2>,M,<3>,N,<4>,K,<5>*hh
  <1> 以正北为参考基准的地面航向(000~359度,前面的0也将被传输)
  <2> 以磁北为参考基准的地面航向(000~359度,前面的0也将被传输)
  <3> 地面速率(000.0~999.9节,前面的0也将被传输)
  <4> 地面速率(0000.0~1851.8公里/小时,前面的0也将被传输)
  <5> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效

■ XXGSV 可视卫星状态

GSA信息:卫星数量、编号、信噪比(信号强度)、

例:$GPGSV,2108063324045103607447162107844173631342*78
  标准格式: $GPGSV,(1)(2)(3)(4)(5)(6)(7),…(4),(5)(6)(7)*hh(CR)(LF) 
各部分含义为:
  (1)总的GSV语句电文数;2;
  (2)当前GSV语句号:1;
  (3)可视卫星总数:08;
  (4)PRN码(伪随机噪声码) 也可以认为是卫星编号
  (5)仰角(0090):33;
  (6)方位角(000359):240;
  (7)信噪比(0099dB):45dB(后面依次为第101617号卫星的信息);   *总和校验域;    hh 总和校验数:78;   (CR)(LF)回车,换行。   
注:每条语句最多包括四颗卫星的信息,每颗卫星的信息有四个数据项,即:
    (4)-卫星号,(5)-仰角,(6)-方位角,(7)-信噪比。   
例:
  $GPGSV,3,1,10,24,82,023,40,05,62,285,32,01,62,123,00,17,59,229,28*70
  每条语句包含四部分内容,例如:第一部分是“24,82,023,40”,第二部分是“05,62,285,32”等等。
每部分的第一个词为PRC,第二个词为卫星高程,跟着为方位角和信号强度。
  这个语句里最重要的指标应该算是“信号躁声比(signal-to-noise ratio)”(以下简称为SNR)。
这个数值标示卫星信号的接收率。我们知道,卫星是以相同的强度发射信号,但是传播过程中难免会遇到诸如树和墙之类的 障碍物,这样就影响了信号的识别。
典型的SNR值在050之间,其中50表示非常好的信号。(SNR可以达到99)。
每条GSV语句最多可以显示4个可见卫星的信息,其他的卫星都会在下一条语句中输出显示。每种卫星系统都会单独显示,如下:GPS有3条GSV报文,北斗有2条GSV报文,GLONASS有3条GSV报文。
$GPGSV,3,1,09,19,46,206,41,16,55,015,43,31,24,120,37,06,82,060,4373
$GPGSV,3,2,09,27,82,183,45,21,15,061,33,23,47,268,43,03,72,228,44*72
$GPGSV,3,3,09,13,34,304,38,*49
$BDGSV,2,1,07,143,53,202,37,145,15,257,40,148,72,234,38,141,49,145,37*67
$BDGSV,2,2,07,151,24,048,41,150,70,327,38,147,77,113,39,*65
$GLGSV,3,1,09,47,07,193,46,43,46,101,50,59,28,309,45,49,28,305,43*68
$GLGSV,3,2,09,48,35,239,45,42,21,040,44,57,24,063,39,44,24,161,47*67
$GLGSV,3,3,09,58, 46,012,45,51

在这里插入图片描述

■ XXRMC 最小定位信息

GSA信息:解状态、经纬度、地面速度、地面航向角、UTC时间、位置、速度、时间等信息。

数据详解:$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
  <1> UTC 时间,hhmmss(时分秒)格式
  <2> 定位状态,A=有效定位,V=无效定位
  <3>纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
  <4> 纬度半球N(北半球)S(南半球)
  <5>经度dddmm.mmmm(度分)格式(前面的0也将被传输)
  <6> 经度半球E(东经)W(西经)
  <7>地面速率(000.0~999.9节,前面的0也将被传输)
  <8>地面航向(000.0~359.9度,以真北为参考基准,前面的0也将被传输)
  <9> UTC 日期,ddmmyy(日月年)格式
  <10>磁偏角(000.0~180.0度,前面的0也将被传输)
  <11> 磁偏角方向,E()W(西)
  <12>模式指示(仅NMEA01833.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)

解析内容:
1.时间,这个是格林威治时间,是世界时间(UTC),我们需要把它转换成北京时间(BTC),BTC和UTC差了8个小时,要在这个时间基础上加8个小时。
2. 定位状态,在接收到有效数据前,这个位是‘V’,后面的数据都为空,接到有效数据后,这个位是‘A’,后面才开始有数据。
3. 纬度,我们需要把它转换成度分秒的格式,计算方法:如接收到的纬度是:4546.40891
  4546.40891/100=45.4640891 可以直接读出45, 
  4546.4089145*100=46.40891, 可以直接读出46分
  46.4089146 =0.40891*60=24.5346 读出24, 所以纬度是:454624秒。
4. 南北纬,这个位有两种值‘N’(北纬)和‘S’(南纬)
5. 经度的计算方法和纬度的计算方法一样
6. 东西经,这个位有两种值‘E’(东经)和‘W’(西经)
7.速率,这个速率值是海里/时,单位是节,要把它转换成千米/时,根据:1海里=1.85公里,把得到的速率乘以1.858. 航向,指的是偏离正北的角度
9. 日期,这个日期是准确的,不需要转换
(1) 单GPS
$GPRMC,064457.90,A,3110.4691141,N,12123.2667676,E,0.157,63.0,300713,0.0,W,A05
(2) 单BDS
$BDRMC,064457.90,A,3110.4691241,N,12123.2667666,E,0.157,63.0,300713,0.0,W,A*05
(3) 单GLONASS
$GLRMC,064457.90,A,3110.4691141,N,12123.2667646,E,0.157,63.0,300713,0.0,W,A*05
(4) 双星或多星联合定位
$GNRMC,064401.65,A,3110.4706987,N,12123.2653375,E,0.604,243.2,300713,0.0,W,A*3E

在这里插入图片描述

$GNRMC,082443.000,A,2240.8299056,N,11349.6730027,E,1.08,271.40,210426,,,A,V*0D
$GNVTG,271.40,T,,M,1.08,N,2.00,K,A*28
$GNGGA,082443.000,2240.8299056,N,11349.6730027,E,1,07,2.7,73.24,M,0.00,M,,,3.5*4B
$GNGSA,A,3,05,15,29,,,,,,,,,,4.4,2.7,3.5,1*38
$GNGSA,A,3,,,,,,,,,,,,,4.4,2.7,3.5,2*31
$GNGSA,A,3,,,,,,,,,,,,,4.4,2.7,3.5,3*30
$GNGSA,A,3,02,03,21,31,,,,,,,,,4.4,2.7,3.5,4*37
$GNGSA,A,3,,,,,,,,,,,,,4.4,2.7,3.5,0*33
$GPGSV,2,1,05,05,54,312,36,15,30,209,37,19,17,148,10,20,52,186,,1*63
$GPGSV,2,2,05,29,21,320,37,1*5C
$GLGSV,1,1,00,1*78
$GAGSV,1,1,00,7*73
$BDGSV,2,1,08,02,44,241,30,03,64,189,34,06,,,28,09,,,28,1*71
$BDGSV,2,2,08,19,,,28,21,49,230,32,31,64,343,38,35,,,28,1*72
$GQGSV,1,1,00,1*65
$GNGLL,2240.8299056,N,11349.6730027,E,082443.000,A,A*4A
$GNGST,082443.000,3.7,,,,2.3,7.8,10.6*63
$GNZDA,082443.000,21,04,2026,00,00*40
$GNDHV,082443.000,0.56,0.476,0.291,-0.026,0.55,,,,,M*2E
$GPTXT,01,01,01,ANTENNA OK*35
000444.900,0.00,0.000,0.000,0.000,0.00,,,,,M*0F
$GPTXT,01,01,01,ANTENNA OK*35

■ NMEA数据格式

■ $PCAS00-设置保存配置指令

拓展指令只有当前上电有效,重启后恢复默认。如果想要配置一次永久生效,可使用该指令。

$PCAS00*01

■ $PCAS01-设置串口波特率

例如: $PCAS01,1*1D     //9600bps

在这里插入图片描述

■ $PCAS02-设置定位数据输出的频率

例:$PCAS02,1000*2E  //1000ms

在这里插入图片描述

■ $PCAS03-设置输出数据NMEA语句过滤

例:$PCAS03,0,0,0,0,1,0,0,0*03

在这里插入图片描述

■ 实例一:只选择RMC

uint8_t txBuffer[] = "$PCAS03,0,0,0,0,1,0,0,0*03\r\n"; (只选择RMC)
sAPI_UartWrite(SC_UART3, txBuffer, strlen((char*)txBuffer));

串口输出:
[2025-06-26 18:56:34.829]# RECV ASCII>
$BDRMC,,V,,,,,,,,,,N,V*38
$BDRMC,,V,,,,,,,,,,N,V*38
$BDRMC,,V,

■ $PCAS04-设置(GPS/BDS/GLONASS)工作模式

例:$PCAS04,3*1A(GPS、BDS混合定位)

在这里插入图片描述

■ $PCAS05- NMEA 协议类型选择

例:$PCAS05,1*19

在这里插入图片描述

■ $PCAS06-查询模块信息

例:$PCAS06,1*1A

在这里插入图片描述

■ $PCAS10-设置重启

例:$PCAS10,0*1C  //热启动

在这里插入图片描述

■ $PCAS12-设置低功耗

可以通过拉低ON_OFF管脚来进入低功耗模式,有些模块暂不支持低功耗指令。

■ $PCAS15-配置是否接收系统中任何一颗卫星

例:
"$PCAS15,2,FFFFFFFF*37\r\n" //开启北斗的 1-32 号卫星
"$PCAS15,2,FFFFFFE0*42\r\n" //开启北斗的 6-32 号卫星, 北斗 1-5 号卫星关闭
"$PCAS15,4,FFFF*31\r\n"     //开启SBAS的 1-16 号卫星, 即 PRN=120-135 $PCAS15,5,1F47, 开启 QZSS 的 1-5 号卫星, 即 PRN=193, 194, 195, 199, 197

在这里插入图片描述

■ NMEA 校验

NMEA 校验和是将从 $ 后第一个字符开始到 * 之前的所有字符进行异或运算得到的值

■ NMEA 校验计算代码

#include <stdio.h>
#include <string.h>

// 计算 NMEA 校验和
unsigned char nmea_checksum(const char *sentence) {
    unsigned char checksum = 0;
    // 查找 '$' 和 '*' 的位置
    const char *start = strchr(sentence, '$');
    const char *end   = strchr(sentence, '*');

    if (start == NULL) return 0; // 没有找到 $
    start++; // 跳过 $

    const char *p = start;

    // 如果存在 '*',则只计算到 '*' 前一个字符
    if (end != NULL) {
        while (p < end) {
            checksum ^= (unsigned char)*p++;
        }
    } else {
        // 如果没有 '*',则计算到字符串结尾
        while (*p != '\0') {
            checksum ^= (unsigned char)*p++;
        }
    }
    return checksum;
}

int main()
{
    char sentence[] = "$PCAS03,0,0,0,0,1,0,0,0*03\r\n"; // 示例RMC语句
	unsigned char cksum = nmea_checksum(sentence);
    printf("Checksum: %02X\n", cksum);  // 应输出:
    return 0;
}

在这里插入图片描述

■ ATGM336H-6N获取到的数据

■ 01. 单BDS

[2025-08-07 16:32:49.503]# RECV ASCII>
$BDRMC,083248.00,A,2240.83741,N,11349.67966,E,0.58,,070825,,,A,V34,42,42,338,18,3*41E,0.58,,070825,,,A,V34,42,42,338,18,3*41
$BDGLL,2240.83741,N,11349.67966,E,083225.00,A,A*7D
$BDZDA,083225.00,07,08,2025,00,00*73
$GPTXT,01,01,01,ANTENNA OK*35
,01,01,ANTENNA OK*35
NA OK*35
3,02,06,09,16,24,39,60,,,,,,8.7,5.3,6.8,4*3C
$BDGSV,2,1,08,02,50,241,36,06,72,244,31,09,59,229,37,13,,,19,1*4A
$BDGSV,2,2,08,16,75,251,36,24,24,203,40,39,79,302,34,60,46,243,40,1*79
$BDGSV,1,1,02,24,24,203,42,39,79,302,38,3*7C
$BDGLL,2240.84635,N,11349.68236,E,080554.00,A,A*7B
$BDZDA,080554.00,07,08,2025,00,00*71
$GPTXT,01,01,01,ANTENNA OK*35

■ 02. 双星或多星联合定位

$GNRMC,082443.000,A,2240.8299056,N,11349.6730027,E,1.08,271.40,210426,,,A,V*0D
$GNVTG,271.40,T,,M,1.08,N,2.00,K,A*28
$GNGGA,082443.000,2240.8299056,N,11349.6730027,E,1,07,2.7,73.24,M,0.00,M,,,3.5*4B
$GNGSA,A,3,05,15,29,,,,,,,,,,4.4,2.7,3.5,1*38
$GNGSA,A,3,,,,,,,,,,,,,4.4,2.7,3.5,2*31
$GNGSA,A,3,,,,,,,,,,,,,4.4,2.7,3.5,3*30
$GNGSA,A,3,02,03,21,31,,,,,,,,,4.4,2.7,3.5,4*37
$GNGSA,A,3,,,,,,,,,,,,,4.4,2.7,3.5,0*33
$GPGSV,2,1,05,05,54,312,36,15,30,209,37,19,17,148,10,20,52,186,,1*63
$GPGSV,2,2,05,29,21,320,37,1*5C
$GLGSV,1,1,00,1*78
$GAGSV,1,1,00,7*73
$BDGSV,2,1,08,02,44,241,30,03,64,189,34,06,,,28,09,,,28,1*71
$BDGSV,2,2,08,19,,,28,21,49,230,32,31,64,343,38,35,,,28,1*72
$GQGSV,1,1,00,1*65
$GNGLL,2240.8299056,N,11349.6730027,E,082443.000,A,A*4A
$GNGST,082443.000,3.7,,,,2.3,7.8,10.6*63
$GNZDA,082443.000,21,04,2026,00,00*40
$GNDHV,082443.000,0.56,0.476,0.291,-0.026,0.55,,,,,M*2E
$GPTXT,01,01,01,ANTENNA OK*35
000444.900,0.00,0.000,0.000,0.000,0.00,,,,,M*0F
$GPTXT,01,01,01,ANTENNA OK*35

■ 03. 提取经纬度代码

void AT336H_Handle(ComCtrl* com)
{
	if(com->hardware.comrxBuff.writePos>270)
	{
		char *start = strstr((char*)com->hardware.comrxBuff.data, "RMC,"); //推荐定位信息
//		printf("%s\r\n",com->hardware.comrxBuff.data);
		if(start != NULL){
			float data1=0,data3=0,data5=0;
			char  f2=0,f4=0,f6=0;
			sscanf(start,"RMC,%f,%c,%f,%c,%f,%c",&data1,&f2,&data3,&f4,&data5,&f6);

			if(f2=='A'){  //有效定位
				//纬度
				int dushu1 = data3/100;
				double fen1 = (data3 - (dushu1*100))/60;
				double wgs_lat = dushu1 + fen1;

				//经度
				int dushu2 = data5/100;
				double fen2 = (data5 - (dushu2*100))/60;
				double wgs_lon = dushu2 + fen2;

				//double wgs_lon = 113.82811;  //示例经度
				//double wgs_lat = 22.680635;  //示例纬度
				static double gcj02[2];
				wgs84_to_gcj02(wgs_lon, wgs_lat, gcj02);
				gSystemInfor.sGps.lng_bd = gcj02[0];
				gSystemInfor.sGps.lat_bd = gcj02[1];
//				printf("----lng=%f,lat=%f\n",gSystemInfor.sGps.lng_bd,gSystemInfor.sGps.lat_bd);
			}
		}
		com->hardware.comrxBuff.writePos=0;
	}
}

■ GNSS工具GnssToolKit3

■ 坐标表示格式(数值格式)

■ 01. 十进制度(度)(Decimal Degrees, DD)

格式:经度, 纬度(注意:先经度,后纬度)
示例:113.82811, 22.680635
特点:
最常见于 API、数据库、JSON
易于计算和存储
高德、百度、Google API 都使用此格式(但坐标系不同)

■ 02. 度分秒(Degrees, Minutes, Seconds, DMS)

格式:dd° mm′ ss.sss″ N/S, ddd° mm′ ss.sss″ E/W
示例:22°4050.286″N, 113°4941.276″E
用法:传统测绘、航海
编程中需转换为十进制度使用:

■ 03. 度分(Degrees Minutes, DM)

格式:dd mm.mmmm N, ddd mm.mmmm E
示例:22 40.8451, 113 49.6866
常见于 NMEA 语句(如 $GPGGA)

■ 常见数据交换格式

■ 01. NMEA 0183(GPS 模块输出)

$GPGGA,062938.00,2240.8451,N,11349.6866,E,1,25,0.6,58.9666,M,0.000,M,99,AAAA*50
纬度:2240.8451,N → 22°40.8451′N
经度:11349.6866,E → 113°49.6866′E

需转换为十进制度:
const lat = 22 + 40.8451/60;     // 22.6807517
const lng = 113 + 49.6866/60;    // 113.8281100

■ 坐标系类型

模块地图 坐标系类型
来自 GPS 模块(如 ATGM336H) WGS-84
来自高德、腾讯地图 API 返回 GCJ-02
来自百度地图 API 返回 BD-09
在高德地图上显示偏移大 很可能用了 WGS-84 未转换

■ ATGM336H 获取的坐标是 WGS-84坐标系,用高德解析位置会出现偏差几百米问题。

高德地图使用的是 GCJ-02 坐标系(俗称“火星坐标系”),这是中国对地理数据进行加密偏移后的坐标系统。
因此,如果你要将此坐标用于 高德开发平台(如显示在高德地图上、做逆地理编码等),必须先将其从 WGS-84 转换为 GCJ-02,否则会出现 几百米到几公里的偏差。

■ WGS-84 坐标系 转 GCJ-02 坐标系

■ 01. C

#include <math.h>
#include <stdio.h>
#define PI 3.14159265358979324
#define A 6378245.0
#define EE 0.00669342162296594323

// 判断是否在中国境内
int out_of_china(double lon, double lat) {
   if (lon < 72.004 || lon > 137.8347) return 1;
   if (lat < 0.8293 || lat > 55.8271) return 1;
   return 0;
}
// 纬度转换
double transform_latitude(double lon, double lat) {
   double ret = -100.0 + 2.0 * lon + 3.0 * lat + 0.2 * lat * lat + 0.1 * lon * lat + 0.2 * sqrt(fabs(lon));
   ret += (20.0 * sin(6.0 * lon * PI) + 20.0 * sin(2.0 * lon * PI)) * 2.0 / 3.0;
   ret += (20.0 * sin(lat * PI) + 40.0 * sin(lat / 3.0 * PI)) * 2.0 / 3.0;
   ret += (160.0 * sin(lat / 12.0 * PI) + 320 * sin(lat / 30.0 * PI)) * 2.0 / 3.0;
   return ret;
}
// 经度转换
double transform_longitude(double lon, double lat) {
   double ret = 300.0 + lon + 2.0 * lat + 0.1 * lon * lon + 0.1 * lon * lat + 0.1 * sqrt(fabs(lon));
   ret += (20.0 * sin(6.0 * lon * PI) + 20.0 * sin(2.0 * lon * PI)) * 2.0 / 3.0;
   ret += (20.0 * sin(lon * PI) + 40.0 * sin(lon / 3.0 * PI)) * 2.0 / 3.0;
   ret += (150.0 * sin(lon / 12.0 * PI) + 300.0 * sin(lon / 30.0 * PI)) * 2.0 / 3.0;
   return ret;
}
// WGS-84 转 GCJ-02
void wgs84_to_gcj02(double lon, double lat, double* output) {
   if (out_of_china(lon, lat)) {
       output[0] = lon;
       output[1] = lat;
       return;
   }
   double d_lat = transform_latitude(lon - 105.0, lat - 35.0);
   double d_lon = transform_longitude(lon - 105.0, lat - 35.0);
   double rad_lat = lat / 180.0 * PI;
   double magic = sin(rad_lat);
   magic = 1 - EE * magic * magic;
   double sqrt_magic = sqrt(magic);
   d_lat = (d_lat * 180.0) / ((A * (1 - EE)) / (magic * sqrt_magic) * PI);
   d_lon = (d_lon * 180.0) / (A / sqrt_magic * cos(rad_lat) * PI);
   output[1] = lat + d_lat;
   output[0] = lon + d_lon;
}
int main() {
   double wgs_lon = 120.616662; // 示例经度
   double wgs_lat = 31.233945; // 示例纬度
   double gcj02[2];
   wgs84_to_gcj02(wgs_lon, wgs_lat, gcj02);
   printf("GCJ-02 坐标: 经度=%f, 纬度=%f\n", gcj02[0], gcj02[1]);
   return 0;
}

■ 02. C++

h

#ifndef QGCTRANSFORM_H
#define QGCTRANSFORM_H
#include <QPointF>
#include <QtMath>
 
class QGCTransform
{
private:
	// 克拉索夫斯基椭球参数
	static constexpr const double a = 6378245.0;              // 长半轴
	static constexpr const double ee = 0.00669342162296594323; // 扁率
	//判断是否在国内,不在国内则不做偏移
	static bool outOfChina(double lat, double lon);
	//度分格式转十进制度
	static double dmToDegrees(double dm);
	//纬度偏移计算
	static double transformLat(double x, double y);
	//经度偏移计算
    static double transformLon(double x, double y);
public:
	//wgs84转gcj02 返回lng,lat
	static QPointF wgs84Togcj02(double wgsLat, double wgsLon);
};
 
#endif 
 

cpp

#include "QGCTransform.h"
// 类外定义静态常量(C++11标准要求)
constexpr double QGCTransform::a;
constexpr double QGCTransform::ee;
 
bool QGCTransform::outOfChina(double lat, double lon)
{
	if (lon < 72.004 || lon > 137.8347) return true;
	if (lat < 0.8293 || lat > 55.8271) return true;
	return false;
}
 
double QGCTransform::dmToDegrees(double dm)
{
	// 分离度和分
	int degrees = static_cast<int>(dm / 100);
	double minutes = dm - degrees * 100;
	// 转换公式:十进制度 = 度 + 分/60
	return degrees + minutes / 60.0;
}
 
double QGCTransform::transformLat(double x, double y)
{
	double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y +
		0.1 * x * y + 0.2 * sqrt(fabs(x));
	ret += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
	ret += (20.0 * sin(y * M_PI) + 40.0 * sin(y / 3.0 * M_PI)) * 2.0 / 3.0;
	ret += (160.0 * sin(y / 12.0 * M_PI) + 320 * sin(y * M_PI / 30.0)) * 2.0 / 3.0;
	return ret;
}
 
double QGCTransform::transformLon(double x, double y)
{
	double ret = 300.0 + x + 2.0 * y + 0.1 * x * x +
		0.1 * x * y + 0.1 * sqrt(fabs(x));
	ret += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
	ret += (20.0 * sin(x * M_PI) + 40.0 * sin(x / 3.0 * M_PI)) * 2.0 / 3.0;
	ret += (150.0 * sin(x / 12.0 * M_PI) + 300.0 * sin(x / 30.0 * M_PI)) * 2.0 / 3.0;
	return ret;
}
 
QPointF QGCTransform::wgs84Togcj02(double wgsLat, double wgsLon)
{
	double wgsLat1 = dmToDegrees(wgsLat);
	double wgsLon1 = dmToDegrees(wgsLon);
	if (outOfChina(wgsLat1, wgsLon1)) 
	{
		return QPointF(wgsLon1, wgsLat1);
	}
 
	double dLat = transformLat(wgsLon1 - 105.0, wgsLat1 - 35.0);
	double dLon = transformLon(wgsLon1 - 105.0, wgsLat1 - 35.0);
 
	const double radLat = wgsLat1 / 180.0 * M_PI;
	double magic = sin(radLat);
	magic = 1 - ee * magic * magic;
 
	const double sqrtMagic = sqrt(magic);
	dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * M_PI);
	dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * M_PI);
 
	return QPointF(wgsLon1 + dLon, wgsLat1 + dLat);
}

■ 在线地图

在线地图

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐