L76X 精准定位

来自Waveshare Wiki
跳转至: 导航搜索

软件准备


原理分析

SIM7600X用到的坐标系是WGS-84坐标系,SIM820X用到的是火星坐标系(gcj_02);而我们常用的百度或者高德地图用到的坐标系是经过加密的百度坐标(bd09)和火星坐标系(gcj_02)。因此,如果直接将WGS-84坐标系放到百度或者高德等地图是有很大误差的(放到谷歌地图可以);此外,SIMXXX获取到的经纬度是分为单位,需要先转换常用的度单位:
SIM7600-GNSS-flow chat.png

获取经纬度等位置信息

由于 GPS 室内搜星不稳定,请将模块或者天线放到阳台或窗户旁,或者直接在户外进行实验。 插上GPS天线,并将接收器标签面朝下置于空旷的室外,在正常情况下(户外,天气良好,没有大型建筑遮挡)上电需要等待约1分钟才能接收到定位信号;如果天气条件不好,可能需要更长的定位时间,甚至无法定位
Visible-sky.png

配置串口

sudo raspi-config
选择Interfacing Options -> Serial,关闭shell访问,打开硬件串口
L76X GPS Module rpi serial.png

树莓派5/2B/zero,用户串口设备号为ttyAMA0;可以用以下命令行确认,serial0为选用的串口设备号,例如下图这样的是ttyS0为串口:

ls -l /dev/serial*

RM500U-serial.png

  • 打开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

L76-lib.png

  • 下载和运行程序
 wget https://www.waveshare.net/w/upload/a/a5/LXXX-GNSS.zip
 sudo unzip LXXX-GNSS.zip
 sudo python3 LXXX-GNSS.py

SIMXXX longitude,latitude.png

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米的误差;不同的测试环境和转换算法,可能误差有所差异。
SIMXXX gaode.png

简单测试

  • 如果安装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

更多相关教程