GNRMC、GNVTG、GNGGA格式消息解析

import math
import numpy as np
from pathlib import Path
from pyproj import CRS, Transformer
from datetime import datetime, timedelta



# def dms_to_decimal(degrees, minutes, seconds):
#     decimal_degrees = degrees + minutes/60 + seconds/3600
#     return decimal_degrees


def gngga_to_decimal(latitude_dddmm, longitude_dddmm, radians=False):
    latitude_decimal = latitude_dddmm // 100 + (latitude_dddmm % 100) / 60
    longitude_decimal = longitude_dddmm // 100 + (longitude_dddmm % 100) / 60

    if radians:
        # 将度数转换为弧度
        latitude_decimal = math.radians(latitude_decimal)
        longitude_decimal = math.radians(longitude_decimal)

    return latitude_decimal, longitude_decimal


# def wgs84_to_cartesian_lib(latitude, longitude, altitude):
#     wgs84 = CRS.from_epsg(4326)
#     enu = CRS.from_epsg(4978)
#     transformer = Transformer.from_crs(wgs84, enu, always_xy=True)
#     x, y, z = transformer.transform(longitude, latitude, altitude)
#     return x, y, z


def wgs84_to_cartesian_math(latitude_rad, longitude_rad, altitude):
    # WGS84椭球体参数
    a = 6378137.0           # 长半轴
    f = 1 / 298.257223563   # 扁率  f = 1-b/a  298.25722293287096133065466877236
    e2 = 2 * f - f ** 2     # 离心率的平方 e = (a2 - b2)^0.5/a = 1- b^2/a^2

    # 计算曲率半径
    N = a / math.sqrt(1 - e2 * math.sin(latitude_rad) ** 2)

    # 计算三维坐标点 https://www.cnblogs.com/charlee44/p/15202473.html
    x = (N + altitude) * math.cos(latitude_rad) * math.cos(longitude_rad)
    y = (N + altitude) * math.cos(latitude_rad) * math.sin(longitude_rad)
    z = (N * (1 - e2) + altitude) * math.sin(latitude_rad)

    return np.array([[x, y, z]])


def ecef_to_enu(ref_latitude, ref_longitude):
    R = np.array([[-1*math.sin(ref_longitude),                           math.cos(ref_longitude),                         0],
                  [-1*math.sin(ref_latitude)*math.cos(ref_longitude), -1*math.sin(ref_latitude)*math.sin(ref_longitude), math.cos(ref_latitude)],
                  [   math.cos(ref_latitude)*math.cos(ref_longitude),    math.cos(ref_latitude)*math.sin(ref_longitude), math.sin(ref_latitude)]])
    return R


if __name__ == '__main__':
    log_path = Path("log/LOG00024.TXT")
    log_txt_list = log_path.open('r').readlines()

    save_name = f"{log_path.stem}_filter.txt"
    save_path = log_path.with_name(save_name)


    time_list = []
    GNRMC_list = []
    GNVTG_list = []
    GNGGA_list = []
    trajectory_list = []

    for log in log_txt_list:
        if 'GNRMC' in log:
            GNRMC_list.append(log)
        if 'GNVTG' in log:
            GNVTG_list.append(log)
        if 'GNGGA' in log:
            GNGGA_list.append(log)
    print(len(GNRMC_list))
    print(len(GNVTG_list))
    print(len(GNGGA_list))


    cnt = 0
    for gnrmc, gnvtg, gngga in zip(GNRMC_list, GNVTG_list, GNGGA_list):

        gnrmc_fields = gnrmc.split(',')
        gngga_fields = gngga.split(',')
        if gnrmc_fields[12] == 'R' and gngga_fields[6] == '4':

            # 速度
            gnvtg_fields = gnvtg.split(',')
            speed = float(gnvtg_fields[7])
            # print(speed)

            # 日期和时间
            utc_time = gnrmc_fields[1]
            utc_date = gnrmc_fields[9]
            format = f"20{utc_date[4:6]}-{utc_date[2:4]}-{utc_date[0:2]} {utc_time[0:2]}:{utc_time[2:4]}:{utc_time[4:]}0"
            utc_date_time = datetime.fromisoformat(format)
            beijing_date_time = utc_date_time + timedelta(hours=8)
            timestamp = beijing_date_time.strftime('%Y%m%d%H%M%S%f')
            time_list.append(beijing_date_time)
            # print(timestamp)
            # break

            # 经纬度高程
            latitude = float(gngga_fields[2])
            longitude = float(gngga_fields[4])
            altitude = float(gngga_fields[9])
            latitude_rad, longitude_rad = gngga_to_decimal(latitude, longitude, radians=True)
            current_point = wgs84_to_cartesian_math(latitude_rad, longitude_rad, altitude)

            if cnt == 0:
                rotation = ecef_to_enu(latitude_rad, longitude_rad)
                start_point = current_point

            enu_point = np.dot(rotation, (current_point-start_point).T).T
            x, y, z = enu_point[0]


            # 检查轨迹连续性
            if len(time_list) >= 2:
                if time_list[-1] == time_list[-2]+timedelta(milliseconds=100):
                    pass
                else:
                    print(time_list[-1])
                    print(time_list[-2])
                    print(time_list[-2]+timedelta(milliseconds=100))
                    print()
                    with open(save_path, 'a') as f:
                        f.write("\nbreak\n")
            else:
                pass

            # 保存
            info = f"{timestamp},{x},{y},{z},{speed}\n"
            with open(save_path, 'a') as f:
                f.write(info)

            cnt += 1


        else:
            pass


测试数据

$GNRMC,023255.10,A,3030.9246016,N,11425.8607692,E,0.100,,230524,,,R,V*0F
$GNVTG,,T,,M,0.100,N,0.186,K,D*36
$GNGGA,023255.10,3030.9246016,N,11425.8607692,E,4,12,1.17,31.586,M,-10.499,M,1.1,0659*7D
$GNRMC,023255.20,A,3030.9246012,N,11425.8607698,E,0.082,,230524,,,R,V*09
$GNVTG,,T,,M,0.082,N,0.152,K,D*34
$GNGGA,023255.20,3030.9246012,N,11425.8607698,E,4,12,1.17,31.586,M,-10.499,M,1.2,0659*73
$GNRMC,023255.30,A,3030.9246018,N,11425.8607700,E,0.047,,230524,,,R,V*0B
$GNVTG,,T,,M,0.047,N,0.086,K,D*35
$GNGGA,023255.30,3030.9246018,N,11425.8607700,E,4,12,1.17,31.585,M,-10.499,M,1.3,0659*7A
$GNRMC,023255.40,A,3030.9246014,N,11425.8607678,E,0.032,,230524,,,R,V*0C
$GNVTG,,T,,M,0.032,N,0.059,K,D*35
$GNGGA,023255.40,3030.9246014,N,11425.8607678,E,4,12,1.17,31.584,M,-10.499,M,0.4,0659*78
$GNRMC,023255.50,A,3030.9246009,N,11425.8607705,E,0.054,,230524,,,R,V*0A
$GNVTG,,T,,M,0.054,N,0.100,K,D*38
$GNGGA,023255.50,3030.9246009,N,11425.8607705,E,4,12,1.17,31.581,M,-10.499,M,0.5,0659*7A
$GNRMC,023255.60,A,3030.9246022,N,11425.8607721,E,0.039,,230524,,,R,V*0D
$GNVTG,,T,,M,0.039,N,0.071,K,D*34
$GNGGA,023255.60,3030.9246022,N,11425.8607721,E,4,12,1.17,31.575,M,-10.499,M,0.6,0659*7E
$GNRMC,023255.70,A,3030.9246015,N,11425.8607719,E,0.090,,230524,,,R,V*00
$GNVTG,,T,,M,0.090,N,0.167,K,D*31
$GNGGA,023255.70,3030.9246015,N,11425.8607719,E,4,12,1.17,31.580,M,-10.499,M,0.7,0659*7B
$GNRMC,023255.80,A,3030.9246012,N,11425.8607699,E,0.078,,230524,,,R,V*07
$GNVTG,,T,,M,0.078,N,0.144,K,D*36
$GNGGA,023255.80,3030.9246012,N,11425.8607699,E,4,12,1.17,31.582,M,-10.499,M,0.8,0659*77
$GNRMC,023255.90,A,3030.9246017,N,11425.8607698,E,0.051,,230524,,,R,V*09
$GNVTG,,T,,M,0.051,N,0.095,K,D*30
$GNGGA,023255.90,3030.9246017,N,11425.8607698,E,4,12,1.17,31.582,M,-10.499,M,0.9,0659*73
$GNRMC,023256.00,A,3030.9246017,N,11425.8607696,E,0.022,,230524,,,R,V*09
$GNVTG,,T,,M,0.022,N,0.041,K,D*3D
$GNGGA,023256.00,3030.9246017,N,11425.8607696,E,4,12,1.17,31.583,M,-10.499,M,1.0,0659*7E
$GNRMC,023256.10,A,3030.9246022,N,11425.8607728,E,0.012,,230524,,,R,V*09
$GNVTG,,T,,M,0.012,N,0.022,K,D*3B
$GNGGA,023256.10,3030.9246022,N,11425.8607728,E,4,12,1.17,31.576,M,-10.499,M,1.1,0659*76
在Verilog中解析GNRMC消息,通常指的是对NMEA 0183标准中定义的GNRMC(全球定位系统推荐最小定位信息)数据进行解析GNRMC消息包含了诸如时间、日期、经纬度、速度、方向等导航信息,通常用于GPS接收器和其他导航设备。 要在Verilog中解析GNRMC消息,可以按照以下步骤进行: 1. **定义状态机**:设计一个有限状态机(FSM)来解析GNRMC消息的结构。GNRMC消息通常以"$GPRMC"开始,以回车换行结束,中间包含了多个以逗号分隔的字段。 2. **接收和缓冲数据**:设计逻辑接收串行数据流,并将接收到的字符存储在缓冲区中。可以使用UART(通用异步接收/发送器)模块接收串行数据。 3. **解析字段**:根据GNRMC格式,设计逻辑来识别和解析每个字段。字段包括: - 时间(hhmmss.sss) - 状态(A=有效,V=无效) - 纬度(ddmm.mmmm) - 纬度方向(N=北半球,S=南半球) - 经度(dddmm.mmmm) - 经度方向(E=东半球,W=西半球) - 地面速度(以节为单位) - 地面航向角(以度为单位) - 日期(ddmmyy) - 磁偏角(dd.xxxxx) - 磁偏角方向(E=东,W=西) - 模式指示(空或A=自主定位,D=差分校正,E=估算,N=数据无效) 4. **提取和验证数据**:在解析每个字段时,提取相应的数据,并根据需要验证数据的有效性。 5. **输出解析结果**:根据解析出的数据,设计输出逻辑,可以是将解析出的数据送入其他模块进行处理,或是直接在顶层输出。 示例伪代码可能如下: ```verilog // 状态定义 localparam S_IDLE = 0, S_WAIT_PREFIX = 1, S_COLLECT_DATA = 2, S_PARSE_FIELDS = 3, S_OUTPUT = 4; // 接收和解析逻辑 always @(posedge clk) begin if (reset) begin state <= S_IDLE; // 其他状态机相关的变量初始化 end else begin case (state) S_IDLE: begin // 等待GNRMC前缀出现 end S_WAIT_PREFIX: begin // 检测到前缀后,进入数据收集状态 end S_COLLECT_DATA: begin // 收集字符到缓冲区,并在适当的时候分割字段 end S_PARSE_FIELDS: begin // 解析每个字段,并进行必要的验证 end S_OUTPUT: begin // 输出解析结果 end default: begin // 异常状态处理 end endcase end end ``` 解析GNRMC消息是一项复杂的任务,需要对NMEA 0183协议有深入的理解,并且对Verilog编程有较高的熟练度。此外,还需要考虑错误处理和异常情况,如数据丢失或格式不正确。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值