L76X 精准定位
来自Waveshare Wiki
软件准备
原理分析
SIM7600X用到的坐标系是WGS-84坐标系,SIM820X用到的是火星坐标系(gcj_02);而我们常用的百度或者高德地图用到的坐标系是经过加密的百度坐标(bd09)和火星坐标系(gcj_02)。因此,如果直接将WGS-84坐标系放到百度或者高德等地图是有很大误差的(放到谷歌地图可以);此外,SIMXXX获取到的经纬度是分为单位,需要先转换常用的度单位:
![]()
获取经纬度等位置信息
由于 GPS 室内搜星不稳定,请将模块或者天线放到阳台或窗户旁,或者直接在户外进行实验。
插上GPS天线,并将接收器标签面朝下置于空旷的室外,在正常情况下(户外,天气良好,没有大型建筑遮挡)上电需要等待约1分钟才能接收到定位信号;如果天气条件不好,可能需要更长的定位时间,甚至无法定位
配置串口
sudo raspi-config 选择Interfacing Options -> Serial,关闭shell访问,打开硬件串口
树莓派5/2B/zero,用户串口设备号为ttyAMA0;可以用以下命令行确认,serial0为选用的串口设备号,例如下图这样的是ttyS0为串口:
ls -l /dev/serial*
- 打开NEMA获取详细信息
sudo minicom -D /dev/ttyS0 -b 9600 dd.mm.ss: 度数不变,分数*100/60 ;如2232.448620--> 22.(32448620*100/60)= 22.54081033
-->
示例程序
- 库安装
pip3 install pyserial pip3 install pynmea2 pip3 install pynmeagps pip3 install RPi.GPIO pip3 install chardet
- 下载和运行程序
wget https://www.waveshare.net/w/upload/a/a5/LXXX-GNSS.zip sudo unzip LXXX-GNSS.zip sudo python3 LXXX-GNSS.py
114.0832857092161,22.53842762954979
- 示例代码
from socket import socket
import sys
import re
import pynmea2
import serial
import chardet
import time
import math
import json
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 loop():
ser1 = serial.Serial("/dev/ttyS0", 9600)
print("ttyDEV Open!!!")
while True:
line = str(ser1.readline(), encoding='utf-8')
if line.startswith("$GNRMC"):
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
print("longitude,latitude") # longitude,latitude
print(f"{longitude},{latitude}") # 经度,纬度
time.sleep(2)
def destroy():
ser1.close()
print("ttydev Close!!!")
try:
loop()
except KeyboardInterrupt:
destroy()
运行示例程序程序后生成的坐标复制到高德地图API
sudo apt install python3-venv python3 -m venv myenv &&source myenv/bin/activate source myenv/bin/activate
- 如果bookworm无法安装库,运行以下指令
sudo rm /usr/lib/python3.11/EXTERNALLY-MANAGED sudo rm /usr/lib/python3.12/EXTERNALLY-MANAGED sudo rm /usr/lib/python3.13/EXTERNALLY-MANAGED
我的位置
在世界贸易贸易广场的南侧窗边定位,定位和小编所在位置有1-2米的误差;不同的测试环境和转换算法,可能误差有所差异。
简单测试
- 如果安装gnss库异常,也可以用这个串口程序简单测试下初始,指定串口和波特率:
import serial
import time
# 打开串口 ttyAMA0,波特率 9600
uart = serial.Serial('/dev/ttyAMA0', baudrate=9600, timeout=1)
def read_gps(uart):
print("Reading GPS data... (Press Ctrl+C to stop)")
try:
while True:
if uart.in_waiting:
line = uart.readline()
if line:
try:
line = line.decode('ascii').strip()
if line.startswith('$'):
print(line)
except Exception:
pass # 解码错误忽略
time.sleep(0.1)
except KeyboardInterrupt:
print("\nExiting...")
read_gps(uart)
- 多串口常见波特率
#!/usr/bin/env python3
import glob
import os
import select
import termios
import threading
import time
DEVICE_PATTERNS = [
"/dev/ttyUSB*",
"/dev/ttyACM*",
"/dev/ttyTHS*",
"/dev/ttyAMA*",
"/dev/ttyS[0-9]*",
"/dev/a[0-9]*",
"/dev/serial/by-id/*",
]
BAUD_RATES = [
9600,
38400,
115200,
460800,
]
BAUD_MAP = {
9600: termios.B9600,
38400: termios.B38400,
115200: termios.B115200,
460800: termios.B460800,
}
print_lock = threading.Lock()
def set_baud(fd, baud):
attr = termios.tcgetattr(fd)
attr[0] = termios.IGNPAR
attr[1] = 0
attr[2] = termios.CS8 | termios.CREAD | termios.CLOCAL
attr[3] = 0
attr[4] = BAUD_MAP[baud]
attr[5] = BAUD_MAP[baud]
attr[6][termios.VMIN] = 0
attr[6][termios.VTIME] = 1
termios.tcsetattr(fd, termios.TCSANOW, attr)
termios.tcflush(fd, termios.TCIFLUSH)
def read_lines(fd, timeout):
buffer = b""
end_time = time.monotonic() + timeout
while time.monotonic() < end_time:
ready, _, _ = select.select([fd], [], [], 0.1)
if not ready:
continue
try:
data = os.read(fd, 4096)
except OSError:
return
if not data:
continue
buffer += data
while b"\n" in buffer:
raw, buffer = buffer.split(b"\n", 1)
line = raw.decode("ascii", errors="ignore").strip()
if line.startswith("$"):
yield line
def read_port(port):
try:
fd = os.open(
port,
os.O_RDONLY | os.O_NOCTTY | os.O_NONBLOCK
)
except OSError:
return
try:
while True:
for baud in BAUD_RATES:
try:
set_baud(fd, baud)
except Exception:
continue
detected = False
for line in read_lines(fd, 0.8):
detected = True
with print_lock:
print(f"[{port} @ {baud}] {line}", flush=True)
if not detected:
continue
while True:
for line in read_lines(fd, 2):
with print_lock:
print(
f"[{port} @ {baud}] {line}",
flush=True
)
finally:
os.close(fd)
ports = []
seen = set()
for pattern in DEVICE_PATTERNS:
for port in glob.glob(pattern):
real_port = os.path.realpath(port)
if real_port not in seen:
seen.add(real_port)
ports.append(port)
for port in ports:
threading.Thread(
target=read_port,
args=(port,),
daemon=True
).start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
pass



