SIMXXX 在高德地图定位到我的位置

张开发
2026/4/12 8:04:43 15 分钟阅读

分享文章

SIMXXX 在高德地图定位到我的位置
摘要SIM7600X 4G HAT因其完善的功能以及稳定的性能深受世人的钟爱但SIM7600X 4G HAT上网是很犀利可为什么SIM7600定位和我的位置差异这么大将SIM7600X获取到的坐标系放到百度或者高德地图坐标反查有几条街的差距呢。下面让我们走进GNSS的大街里走一走看看是谁用了时空转移:硬件准备SIM7600X 4G HAT树莓派4B硬件连接图软件准备高德地图API原理分析SIM7600X用到的坐标系是WGS-84坐标系而我们常用的百度或者高德地图用到的坐标系是经过加密的百度坐标bd09和火星坐标系gcj_02。因此如果直接将WGS-84坐标系放到百度或者高德等地图是有很大误差的放到谷歌地图可以此外SIM7600获取到的经纬度是分为单位需要先转换常用的单位度SIMXXX获取经纬度等位置信息温馨提示由于 GPS 室内搜星不稳定请将模块或者天线放到阳台或窗户旁或者直接在户外进行实验。插上GPS天线并将接收器标签面朝下置于空旷的室外在正常情况下户外天气良好没有大型建筑遮挡上电需要等待约1分钟才能接收到定位信号如果天气条件不好可能需要更长的定位时间甚至无法定位。发送以下指令打开GPSATCGPS1用以指令获取经纬度等位置信息ATCGPSINFO打开NEMA获取详细信息sudo minicom -D /dev/ttyUSB1单位转换ddmm.mm-- dd.mm.ss: 度数不变分数*100/60 如2232.448620-- 22.(32448620*100/60) 22.54081033编辑并保存示例程序sudo nano SIMXXX.pyfrom socket import socket import sys import re import pynmea2 import serial import chardet import time import math import json global Latitude global Longitude global line global line1 global ser2 x_pi 3.14159265358979324 * 3000.0 / 180.0 pi 3.1415926535897932384626 # π a 6378245.0 # Semi-major axis ee 0.00669342162296594323 # Eccentricity squared def _transformlng(longitude, latitude): ret 300.0 longitude 2.0 * latitude 0.1 * longitude * longitude \ 0.1 * longitude * latitude 0.1 * math.sqrt(math.fabs(longitude)) ret (20.0 * math.sin(6.0 * longitude * pi) 20.0 * math.sin(2.0 * longitude * pi)) * 2.0 / 3.0 ret (20.0 * math.sin(longitude * pi) 40.0 * math.sin(longitude / 3.0 * pi)) * 2.0 / 3.0 ret (150.0 * math.sin(longitude / 12.0 * pi) 300.0 * math.sin(longitude / 30.0 * pi)) * 2.0 / 3.0 return ret def _transformlat(longitude, latitude): ret -100.0 2.0 * longitude 3.0 * latitude 0.2 * latitude * latitude \ 0.1 * longitude * latitude 0.2 * math.sqrt(math.fabs(longitude)) ret (20.0 * math.sin(6.0 * longitude * pi) 20.0 * math.sin(2.0 * longitude * pi)) * 2.0 / 3.0 ret (20.0 * math.sin(latitude * pi) 40.0 * math.sin(latitude / 3.0 * pi)) * 2.0 / 3.0 ret (160.0 * math.sin(latitude / 12.0 * pi) 320 * math.sin(latitude * pi / 30.0)) * 2.0 / 3.0 return ret def setup(): # global response ser2 serial.Serial(/dev/ttyUSB2,115200) print(ttyUSB2 Open!!!) ser2.write(ATCGPS1\r.encode()) print(ATCGPS1) ser2.close() print(ttyUSB2 Close!!!) def loop(): global ser1 ser1 serial.Serial(/dev/ttyUSB1,115200) print(ttyUSB1 Open!!!) while True: line str(ser1.readline(),encodingutf-8) if line.startswith($GPRMC): global Longitude global Latitude rmc pynmea2.parse(line) if re.match(^\d?\.\d?$, rmc.lat)is not None: print(rmc) latitude rmc.latitude longitude rmc.longitude # SIM820X uses the gcj_02 coordinate system, no coordinate conversion is required dlat _transformlat(longitude - 105.0, latitude - 35.0) dlng _transformlng(longitude - 105.0, latitude - 35.0) radlat latitude / 180.0 * pi magic math.sin(radlat) magic 1 - ee * magic * magic sqrtmagic math.sqrt(magic) dlat (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi) dlng (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi) mglat latitude dlat mglng longitude dlng # Please comment out the conversion part print (longitude,latitude)#longitude,latitude print (str(mglng),str(mglat))#经度,纬度 time.sleep(2) def destroy(): ser1.close() print(ttyUSB1 Close!!!) ser2 serial.Serial(/dev/ttyUSB2,115200) print(ttyUSB2 Open!!!) ser1.close() print(ttyUSB1 Close!!!) ser2.close() print(ttyUSB2 Close!!!) try: setup() loop() except KeyboardInterrupt: destroy()运行示例程序后生成的坐标复制到高德地图APIsudo pip3 install pynmea2 sudo pip3 install pynmeagps sudo python3 SIM7600X-GNSS.py114.0832857092161,22.53842762954979我的位置小编在世界贸易贸易广场的南侧窗边(不小心暴露了小编的位置找到我请您喝茶哈哈)定位和我有1-2米的误差有转换和硬件本身决定的不同的测试环境和转换算法可能误差有所差异。或者可以用这个 UI 界面程序可以直接跳转#!/usr/bin/env python3 # -*- coding: utf-8 -*- from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from urllib.parse import urlparse, parse_qs import json import threading import time import glob import re import math import serial HOST 0.0.0.0 PORT 8888 BAUD_LIST [9600, 115200, 460800] ser None ser_lock threading.Lock() buf status { serial: Disconnected, port: , baud: , fix: Invalid, lat_wgs: , lon_wgs: , lat_gcj: , lon_gcj: , last_gga: , message: Ready, } PI math.pi A 6378245.0 EE 0.00669342162296594323 GGA_RE re.compile(r\$(?:GNGGA|GPGGA),[^$]*?\*[0-9A-Fa-f]{2}) def out_of_china(lat, lon): return not (72.004 lon 137.8347 and 0.8293 lat 55.8271) def transform_lat(x, y): ret -100 2*x 3*y 0.2*y*y 0.1*x*y 0.2*math.sqrt(abs(x)) ret (20*math.sin(6*x*PI) 20*math.sin(2*x*PI)) * 2/3 ret (20*math.sin(y*PI) 40*math.sin(y/3*PI)) * 2/3 ret (160*math.sin(y/12*PI) 320*math.sin(y*PI/30)) * 2/3 return ret def transform_lon(x, y): ret 300 x 2*y 0.1*x*x 0.1*x*y 0.1*math.sqrt(abs(x)) ret (20*math.sin(6*x*PI) 20*math.sin(2*x*PI)) * 2/3 ret (20*math.sin(x*PI) 40*math.sin(x/3*PI)) * 2/3 ret (150*math.sin(x/12*PI) 300*math.sin(x*PI/30)) * 2/3 return ret def wgs84_to_gcj02(lat, lon): if out_of_china(lat, lon): return lat, lon dlat transform_lat(lon - 105.0, lat - 35.0) dlon transform_lon(lon - 105.0, lat - 35.0) radlat lat / 180.0 * PI magic math.sin(radlat) magic 1 - EE * magic * magic sqrtmagic math.sqrt(magic) dlat (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrtmagic) * PI) dlon (dlon * 180.0) / (A / sqrtmagic * math.cos(radlat) * PI) return lat dlat, lon dlon def parse_gga(gga): f gga.split(,) if len(f) 7 or not f[2] or not f[4] or f[6] 0: return None try: lat int(f[2][:2]) float(f[2][2:]) / 60.0 lon int(f[4][:3]) float(f[4][3:]) / 60.0 if f[3] S: lat -lat if f[5] W: lon -lon return lat, lon, f[6] except: return None def get_ports(): ports [] for p in (/dev/ttyUSB*, /dev/ttyACM*, /dev/ttyS*, /dev/ttyAMA*): ports glob.glob(p) ports sorted(set(ports)) return ports or [/dev/ttyUSB2] def open_serial(port, baud): global ser, buf with ser_lock: if ser: try: ser.close() except: pass ser None ser serial.Serial(port, int(baud), timeout0.1) buf status[serial] Connected status[port] port status[baud] str(baud) status[message] Serial opened def close_serial(): global ser with ser_lock: if ser: try: ser.close() except: pass ser None status[serial] Disconnected status[message] Serial closed def reader_loop(): global buf while True: try: with ser_lock: s ser if s is None: time.sleep(0.2) continue data s.read(512) if not data: time.sleep(0.02) continue buf data.decode(ascii, errorsignore) for m in GGA_RE.finditer(buf): gga m.group() status[last_gga] gga r parse_gga(gga) if not r: continue lat, lon, fix r lat_gcj, lon_gcj wgs84_to_gcj02(lat, lon) status[fix] fix status[lat_wgs] f{lat:.8f} status[lon_wgs] f{lon:.8f} status[lat_gcj] f{lat_gcj:.8f} status[lon_gcj] f{lon_gcj:.8f} status[message] Receiving GGA buf buf[-2048:] except Exception as e: status[serial] Disconnected status[message] fRead error: {e} close_serial() time.sleep(0.5) threading.Thread(targetreader_loop, daemonTrue).start() HTML !doctype html html langzh-CN head meta charsetutf-8 titleGNSS → AMap/title meta nameviewport contentwidthdevice-width,initial-scale1 style body{margin:0;padding:24px;font-family:Arial,Microsoft YaHei;background:#e9edf3;color:#24364b} .wrap{max-width:1100px;margin:auto} .title{font-size:34px;font-weight:700;margin-bottom:18px} .card{background:#f6f8fb;border-radius:18px;padding:22px;box-shadow:0 2px 12px rgba(0,0,0,.08)} .grid{display:grid;grid-template-columns:1fr 1fr;gap:14px} label{display:block;font-size:14px;font-weight:700;margin-bottom:8px;color:#52657a} select,button,input{width:100%;padding:13px 14px;border-radius:12px;border:1px solid #d7dfe8;background:#fff;font-size:14px;box-sizing:border-box} button{background:#3b6fd8;color:#fff;border:none;cursor:pointer} a.btn{display:inline-block;padding:12px 16px;border-radius:12px;border:1px solid #d7dfe8;background:#fff;color:#24364b;text-decoration:none} .links{display:flex;gap:12px;flex-wrap:wrap;margin-top:14px} pre{background:#fff;border:1px solid #d7dfe8;border-radius:12px;padding:14px;min-height:220px;white-space:pre-wrap;word-break:break-word} media (max-width:800px){.grid{grid-template-columns:1fr}} /style /head body div classwrap div classtitleGNSS → AMap/div div classcard div classgrid div label串口/label select idport/select /div div label波特率/label select idbaud option9600/option option selected115200/option option460800/option /select /div /div div classlinks button onclickopenPort()打开串口/button a classbtn href/close关闭串口/a a classbtn idamap href# target_blank打开高德/a /div div stylemargin-top:14pxpre idsloading.../pre/div /div /div script async function loadPorts(){ const rawait fetch(/api); const dawait r.json(); const seldocument.getElementById(port); sel.innerHTML; d.ports.forEach(p{ const odocument.createElement(option); o.valuep; o.textContentp; if(d.portp) o.selectedtrue; sel.appendChild(o); }); if(d.baud) document.getElementById(baud).valued.baud; } async function refresh(){ const rawait fetch(/api); const dawait r.json(); document.getElementById(s).textContent serial : d.serial \\n port : d.port \\n baud : d.baud \\n fix : d.fix \\n lat_wgs : d.lat_wgs \\n lon_wgs : d.lon_wgs \\n lat_gcj : d.lat_gcj \\n lon_gcj : d.lon_gcj \\n message : d.message \\n\\n (d.last_gga || ); document.getElementById(amap).href d.amap || #; } async function openPort(){ const portdocument.getElementById(port).value; const bauddocument.getElementById(baud).value; location.href/open?port encodeURIComponent(port) baud encodeURIComponent(baud); } loadPorts(); refresh(); setInterval(refresh,1000); setInterval(loadPorts,5000); /script /body /html class Handler(BaseHTTPRequestHandler): def send_text(self, code, text, ctypetext/html; charsetutf-8): data text.encode(utf-8) self.send_response(code) self.send_header(Content-Type, ctype) self.send_header(Content-Length, str(len(data))) self.end_headers() self.wfile.write(data) def send_json(self, obj): data json.dumps(obj, ensure_asciiFalse).encode(utf-8) self.send_response(200) self.send_header(Content-Type, application/json; charsetutf-8) self.send_header(Content-Length, str(len(data))) self.end_headers() self.wfile.write(data) def redirect(self, location/): self.send_response(302) self.send_header(Location, location) self.end_headers() def do_GET(self): p urlparse(self.path) qs parse_qs(p.query) if p.path /: self.send_text(200, HTML) return if p.path /api: amap if status[lat_gcj] and status[lon_gcj]: amap ( fhttps://ditu.amap.com/regeo?lng{status[lon_gcj]} flat{status[lat_gcj]}srcuriapiinnersrcuriapi ) self.send_json({**status, ports: get_ports(), amap: amap}) return if p.path /open: port qs.get(port, [])[0] baud qs.get(baud, [115200])[0] try: open_serial(port, baud) except Exception as e: status[serial] Disconnected status[message] fOpen failed: {e} self.redirect(/) return if p.path /close: close_serial() self.redirect(/) return self.send_text(404, 404) def log_message(self, format, *args): pass if __name__ __main__: print(fServer: http://0.0.0.0:{PORT}) ThreadingHTTPServer((HOST, PORT), Handler).serve_forever()HTML 转换高德工具!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleGPS坐标转换与查看/title style body { font-family: Arial, sans-serif; background-color: #f4f7fc; padding: 20px; } .container { max-width: 600px; margin: 0 auto; background-color: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); padding: 30px; } h1 { text-align: center; font-size: 28px; margin-bottom: 20px; color: #333; } label { display: block; margin-bottom: 10px; color: #555; font-weight: bold; } input, button { width: 100%; padding: 12px; margin-bottom: 20px; border-radius: 8px; border: 1px solid #dfe3e8; font-size: 16px; box-sizing: border-box; } button { background-color: #4CAF50; color: white; border: none; cursor: pointer; } button:hover { background-color: #45a049; } .results { margin-top: 20px; } .results p { font-size: 16px; color: #555; } .link-btn { display: block; width: 100%; text-align: center; padding: 12px; background-color: #008CBA; color: white; border: none; font-size: 16px; border-radius: 8px; cursor: pointer; margin-top: 10px; } .link-btn:hover { background-color: #007bb5; } /style script // 将经纬度的度分格式转换为十进制 function convertToDecimal(degreesMinutes) { const degrees Math.floor(degreesMinutes / 100); const minutes degreesMinutes - degrees * 100; return degrees (minutes / 60); } // WGS-84 转 GCJ-02 坐标转换算法 const PI Math.PI; const A 6378245.0; // WGS-84半长轴 const EE 0.00669342162296594323; // 偏心率平方 function out_of_china(lat, lon) { return !(lon 72.004 lon 137.8347 lat 0.8293 lat 55.8271); } function transformLat(x, y) { let ret -100 2 * x 3 * y 0.2 * y * y 0.1 * x * y 0.2 * Math.sqrt(Math.abs(x)); ret (20 * Math.sin(6 * x * PI) 20 * Math.sin(2 * x * PI)) * 2 / 3; ret (20 * Math.sin(y * PI) 40 * Math.sin(y / 3 * PI)) * 2 / 3; ret (160 * Math.sin(y / 12 * PI) 320 * Math.sin(y * PI / 30)) * 2 / 3; return ret; } function transformLon(x, y) { let ret 300 x 2 * y 0.1 * x * x 0.1 * x * y 0.1 * Math.sqrt(Math.abs(x)); ret (20 * Math.sin(6 * x * PI) 20 * Math.sin(2 * x * PI)) * 2 / 3; ret (20 * Math.sin(x * PI) 40 * Math.sin(x / 3 * PI)) * 2 / 3; ret (150 * Math.sin(x / 12 * PI) 300 * Math.sin(x * PI / 30)) * 2 / 3; return ret; } function wgs84_to_gcj02(lat, lon) { if (out_of_china(lat, lon)) { return { lat: lat, lon: lon }; } const dlat transformLat(lon - 105.0, lat - 35.0); const dlng transformLon(lon - 105.0, lat - 35.0); const radlat lat / 180.0 * PI; const magic Math.sin(radlat); const sqrtmagic Math.sqrt(1 - EE * magic * magic); const dlatFinal (dlat * 180.0) / ((A * (1 - EE)) / (sqrtmagic * Math.sqrt(sqrtmagic)) * PI); const dlngFinal (dlng * 180.0) / (A / sqrtmagic * Math.cos(radlat) * PI); return { lat: lat dlatFinal, lon: lon dlngFinal }; } // 坐标转换并展示 function convertCoordinates() { const latitudeInput parseFloat(document.getElementById(latitudeInput).value); const longitudeInput parseFloat(document.getElementById(longitudeInput).value); if (isNaN(latitudeInput) || isNaN(longitudeInput)) { alert(请输入有效的经纬度); return; } // 将经纬度转换为十进制 const latitude convertToDecimal(latitudeInput); const longitude convertToDecimal(longitudeInput); // WGS-84坐标显示 document.getElementById(wgsLat).textContent latitude; document.getElementById(wgsLon).textContent longitude; // 转换为GCJ-02坐标 const { lat: gcjLat, lon: gcjLon } wgs84_to_gcj02(latitude, longitude); // GCJ-02坐标显示 document.getElementById(gcjLat).textContent gcjLat; document.getElementById(gcjLon).textContent gcjLon; // 更新高德地图链接 document.getElementById(amapLink).href https://ditu.amap.com/regeo?lng${gcjLon}lat${gcjLat}srcuriapiinnersrcuriapi; } /script /head body div classcontainer h1GPS坐标转换与查看/h1 div label forlatitudeInput请输入纬度/label input typetext idlatitudeInput placeholder例如2232.490386 /div div label forlongitudeInput请输入经度/label input typetext idlongitudeInput placeholder例如11404.688746 /div button onclickconvertCoordinates()转换坐标/button div classresults h3WGS-84坐标/h3 p纬度: span idwgsLat等待数据.../span/p p经度: span idwgsLon等待数据.../span/p h3转换后的GCJ-02坐标/h3 p纬度: span idgcjLat等待数据.../span/p p经度: span idgcjLon等待数据.../span/p a classlink-btn idamapLink target_blank查看高德地图/a /div /div /body /html

更多文章