wireshark/epan/dissectors/packet-ubx-gps_l1_lnav.c

404 lines
21 KiB
C

/* packet-ubx-gps_l1_lnav.c
* Dissection of Global Positioning System (GPS) L1 C/A LNAV navigation messages
* (as provided by UBX-RXM-SFRBX).
*
* By Timo Warns <timo.warns@gmail.com>
* Copyright 2023 Timo Warns
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@unicom.net>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include <epan/expert.h>
#include <epan/packet.h>
#include "packet-ubx.h"
/*
* Dissects GPS L1 C/A LNAV navigation messages as encoded by UBX (in
* UBX-RXM-SFRBX messages).
*
* UBX encodes the 30 bit words of GPS subframes as 32 bit words in little-endian.
*
* The two most significant bits of the 32 bit word can be ignored.
*
* UBX takes care of the word parity checks. So, parity checks do not have to be
* repeated for dissection. UBX inverts the bits of a word if the least
* significant parity bit (D30) of the previous word is 1 (i.e. UBX undoes the
* bit inverting of GPS removing the need to do so for dissection).
*/
/*
* mapping from L2 channel code ID to description
* see IS-GPS-200N, Section 20.3.3.3.1.2
*/
static const value_string L2_CHANNEL_CODE[] = {
{0x0, "invalid"},
{0x1, "P-code ON"},
{0x2, "C/A-code ON"},
{0x3, "invalid"},
{0, NULL},
};
/*
* mapping from URA index to URA
* see IS-GPS-200N, Section 20.3.3.3.1.3
*/
static const value_string URA_INDEX[] = {
{ 0, "0.00 < URA <= 2.40"},
{ 1, "2.40 < URA <= 3.40"},
{ 2, "3.40 < URA <= 4.85"},
{ 3, "4.85 < URA <= 6.85"},
{ 4, "6.85 < URA <= 9.65"},
{ 5, "9.65 < URA <= 13.65"},
{ 6, "13.65 < URA <= 24.00"},
{ 7, "24.00 < URA <= 48.00"},
{ 8, "48.00 < URA <= 96.00"},
{ 9, "96.00 < URA <= 192.00"},
{10, "192.00 < URA <= 384.00"},
{11, "384.00 < URA <= 768.00"},
{12, "768.00 < URA <= 1536.00"},
{13, "1536.00 < URA <= 3072.00"},
{14, "3072.00 < URA <= 6144.00"},
{15, "6144.00 < URA"},
{0, NULL},
};
/*
* mapping for SV health summary
* see IS-GPS-200N, Section 20.3.3.3.1.4
*/
static const value_string SV_HEALTH_SUMMARY[] = {
{0, "all LNAV data are OK"},
{1, "some or all LNAV data are bad"},
{0, NULL},
};
/*
* mapping for codes for health of SV signal components
* see IS-GPS-200N, Section 20.3.3.5.1.3
*/
static const value_string SV_HEALTH_CODE[] = {
{0, "All Signals OK"},
{1, "All Signals Weak"},
{2, "All Signals Dead"},
{3, "All Signals Have No Data Modulation"},
{4, "L1 P Signal Weak"},
{5, "L1 P Signal Dead"},
{6, "L1 P Signal Has No Data Modulation"},
{7, "L2 P Signal Weak"},
{8, "L2 P Signal Dead"},
{9, "L2 P Signal Has No Data Modulation"},
{10, "L1C Signal Weak"},
{11, "L1C Signal Dead"},
{12, "L1C Signal Has No Data Modulation"},
{13, "L2C Signal Weak"},
{14, "L2C Signal Dead"},
{15, "L2C Signal Has No Data Modulation"},
{16, "L1 & L2 P Signal Weak"},
{17, "L1 & L2 P Signal Dead"},
{18, "L1 & L2 P Signal Has No Data Modulation"},
{19, "L1 & L2C Signal Weak"},
{20, "L1 & L2C Signal Dead"},
{21, "L1 & L2C Signal Has No Data Modulation"},
{22, "L1 Signal Weak"},
{23, "L1 Signal Dead"},
{24, "L1 Signal Has No Data Modulation"},
{25, "L2 Signal Weak"},
{26, "L2 Signal Dead"},
{27, "L2 Signal Has No Data Modulation"},
{28, "SV Is Temporarily Out (Do not use this SV during current pass)"},
{29, "SV Will Be Temporarily Out (Use with caution)"},
{30, "One Or More Signals Are Deformed, However The Relevant URA Parameters Are Valid"},
{31, "More Than One Combination Would Be Required To Describe Anomalies"},
{0, NULL},
};
// Initialize the protocol and registered fields
static int proto_ubx_gps_l1;
// Telemetry Word (see IS-GPS-200N, Section 20.3.3.1)
static int hf_ubx_gps_l1_tlm_preamble;
static int hf_ubx_gps_l1_tlm_message;
static int hf_ubx_gps_l1_tlm_integrity;
static int hf_ubx_gps_l1_tlm_reserved;
static int hf_ubx_gps_l1_tlm_parity;
// Handover Word (see IS-GPS-200N, Section 20.3.3.2)
static int hf_ubx_gps_l1_how_tow_count;
static int hf_ubx_gps_l1_how_alert;
static int hf_ubx_gps_l1_how_anti_spoof;
static int hf_ubx_gps_l1_how_subframe_id;
static int hf_ubx_gps_l1_how_parity_sol;
static int hf_ubx_gps_l1_how_parity;
// Subframe 1 (see IS-GPS-200N, Section 20.3.3.3)
static int hf_ubx_gps_l1_sf1;
static int hf_ubx_gps_l1_sf1_week_no;
static int hf_ubx_gps_l1_sf1_l2_channel_code;
static int hf_ubx_gps_l1_sf1_ura_index;
static int hf_ubx_gps_l1_sf1_sv_health_summary;
static int hf_ubx_gps_l1_sf1_sv_health;
static int hf_ubx_gps_l1_sf1_iodc_msbs;
static int hf_ubx_gps_l1_sf1_w3_parity;
static int hf_ubx_gps_l1_sf1_w4_l2_p_data_flag;
static int hf_ubx_gps_l1_sf1_w4_reserved;
static int hf_ubx_gps_l1_sf1_w4_parity;
static int hf_ubx_gps_l1_sf1_w5_reserved;
static int hf_ubx_gps_l1_sf1_w5_parity;
static int hf_ubx_gps_l1_sf1_w6_reserved;
static int hf_ubx_gps_l1_sf1_w6_parity;
static int hf_ubx_gps_l1_sf1_w7_reserved;
static int hf_ubx_gps_l1_sf1_w7_tgd;
static int hf_ubx_gps_l1_sf1_w7_parity;
static int hf_ubx_gps_l1_sf1_w8_iodc_lsbs;
static int hf_ubx_gps_l1_sf1_w8_toc;
static int hf_ubx_gps_l1_sf1_w8_parity;
static int hf_ubx_gps_l1_sf1_w9_af2;
static int hf_ubx_gps_l1_sf1_w9_af1;
static int hf_ubx_gps_l1_sf1_w9_parity;
static int hf_ubx_gps_l1_sf1_w10_af0;
static int hf_ubx_gps_l1_sf1_w10_parity;
static dissector_table_t ubx_gps_l1_sf_dissector_table;
static expert_field ei_ubx_gps_l1_tlm_preamble;
static expert_field ei_ubx_gps_l1_how_tow_count;
static expert_field ei_ubx_gps_l1_how_subframe_id;
static int ett_ubx_gps_l1;
static int ett_ubx_gps_l1_tlm;
static int ett_ubx_gps_l1_how;
static int ett_ubx_gps_l1_sf1_w3;
static int ett_ubx_gps_l1_sf1_w4;
static int ett_ubx_gps_l1_sf1_w5;
static int ett_ubx_gps_l1_sf1_w6;
static int ett_ubx_gps_l1_sf1_w7;
static int ett_ubx_gps_l1_sf1_w8;
static int ett_ubx_gps_l1_sf1_w9;
static int ett_ubx_gps_l1_sf1_w10;
/* Format TOW count */
static void fmt_tow_count(gchar *label, gint32 c) {
guint tow = c << 2;
snprintf(label, ITEM_LABEL_LENGTH, "%d (TOW: %.1fs)", c, tow * 1.5);
}
/* Format Clock Data Reference Time t_OC */
static void fmt_t_oc(gchar *label, gint32 i) {
guint t_oc = i << 4;
snprintf(label, ITEM_LABEL_LENGTH, "%ds", t_oc);
}
/* Dissect GPS L1 C/A LNAV navigation message */
static int dissect_ubx_gps_l1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
guint32 subframe_id = (tvb_get_guint32(tvb, 4, ENC_LITTLE_ENDIAN) & 0x00000700) >> 8;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "GPS L1 LNAV");
col_clear(pinfo->cinfo, COL_INFO);
col_append_fstr(pinfo->cinfo, COL_INFO, "Subframe %i", subframe_id);
proto_tree *gps_l1_tree = proto_tree_add_subtree_format(tree, tvb, 0, 40, ett_ubx_gps_l1, NULL, "GPS L1 LNAV (Subframe %i)", subframe_id);
// send the subframe to the next dissector
if (!dissector_try_uint(ubx_gps_l1_sf_dissector_table, subframe_id, tvb, pinfo, gps_l1_tree)) {
call_data_dissector(tvb, pinfo, tree);
}
return tvb_captured_length(tvb);
}
// Dissect the telemetry (TLM) word
static void dissect_ubx_gps_l1_tlm(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_) {
proto_tree *tlm_tree = proto_tree_add_subtree_format(tree, tvb, 0, 4, ett_ubx_gps_l1_tlm, NULL, "Word 1: Telemetry");
guint32 tlm_preamble;
proto_item* pi_tlm_preamble = proto_tree_add_item_ret_uint(tlm_tree, hf_ubx_gps_l1_tlm_preamble, tvb, 0, 4, ENC_LITTLE_ENDIAN, &tlm_preamble);
if (tlm_preamble != 0x8b) {
expert_add_info_format(pinfo, pi_tlm_preamble, &ei_ubx_gps_l1_tlm_preamble, "Invalid preamble");
}
proto_tree_add_item(tlm_tree, hf_ubx_gps_l1_tlm_message, tvb, 0, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tlm_tree, hf_ubx_gps_l1_tlm_integrity, tvb, 0, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tlm_tree, hf_ubx_gps_l1_tlm_reserved, tvb, 0, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tlm_tree, hf_ubx_gps_l1_tlm_parity, tvb, 0, 4, ENC_LITTLE_ENDIAN);
}
// Dissect the handover word (HOW)
static void dissect_ubx_gps_l1_how(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_) {
guint32 subframe_id;
proto_tree *how_tree = proto_tree_add_subtree_format(tree, tvb, 4, 4, ett_ubx_gps_l1_how, NULL, "Word 2: Handover");
guint32 tow_count;
proto_item* pi_how_tow_count = proto_tree_add_item_ret_uint(how_tree, hf_ubx_gps_l1_how_tow_count, tvb, 4, 4, ENC_LITTLE_ENDIAN, &tow_count);
if (tow_count > 100799) {
expert_add_info_format(pinfo, pi_how_tow_count, &ei_ubx_gps_l1_how_tow_count, "Invalid TOW count");
}
proto_tree_add_item(how_tree, hf_ubx_gps_l1_how_alert, tvb, 4, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(how_tree, hf_ubx_gps_l1_how_anti_spoof, tvb, 4, 4, ENC_LITTLE_ENDIAN);
proto_item* pi_how_sf_id = proto_tree_add_item_ret_uint(how_tree, hf_ubx_gps_l1_how_subframe_id, tvb, 4, 4, ENC_LITTLE_ENDIAN, &subframe_id);
if (subframe_id > 5) {
expert_add_info_format(pinfo, pi_how_sf_id, &ei_ubx_gps_l1_how_subframe_id, "Invalid subframe ID");
}
proto_tree_add_item(how_tree, hf_ubx_gps_l1_how_parity_sol, tvb, 4, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(how_tree, hf_ubx_gps_l1_how_parity, tvb, 4, 4, ENC_LITTLE_ENDIAN);
}
/* Dissect subframe 1 */
static int dissect_ubx_gps_l1_sf1(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_) {
dissect_ubx_gps_l1_tlm(tvb, pinfo, tree, data);
dissect_ubx_gps_l1_how(tvb, pinfo, tree, data);
// subframe 1, word 3
proto_tree *w3_tree = proto_tree_add_subtree_format(tree, tvb, 8, 4, ett_ubx_gps_l1_sf1_w3, NULL, "Word 3");
proto_tree_add_item(w3_tree, hf_ubx_gps_l1_sf1_week_no, tvb, 8, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w3_tree, hf_ubx_gps_l1_sf1_l2_channel_code, tvb, 8, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w3_tree, hf_ubx_gps_l1_sf1_ura_index, tvb, 8, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w3_tree, hf_ubx_gps_l1_sf1_sv_health_summary, tvb, 8, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w3_tree, hf_ubx_gps_l1_sf1_sv_health, tvb, 8, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w3_tree, hf_ubx_gps_l1_sf1_iodc_msbs, tvb, 8, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w3_tree, hf_ubx_gps_l1_sf1_w3_parity, tvb, 8, 4, ENC_LITTLE_ENDIAN);
// subframe 1, word 4
proto_tree *w4_tree = proto_tree_add_subtree_format(tree, tvb, 12, 4, ett_ubx_gps_l1_sf1_w4, NULL, "Word 4");
proto_tree_add_item(w4_tree, hf_ubx_gps_l1_sf1_w4_l2_p_data_flag, tvb, 12, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w4_tree, hf_ubx_gps_l1_sf1_w4_reserved, tvb, 12, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w4_tree, hf_ubx_gps_l1_sf1_w4_parity, tvb, 12, 4, ENC_LITTLE_ENDIAN);
// subframe 1, word 5
proto_tree *w5_tree = proto_tree_add_subtree_format(tree, tvb, 16, 4, ett_ubx_gps_l1_sf1_w5, NULL, "Word 5");
proto_tree_add_item(w5_tree, hf_ubx_gps_l1_sf1_w5_reserved, tvb, 16, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w5_tree, hf_ubx_gps_l1_sf1_w5_parity, tvb, 16, 4, ENC_LITTLE_ENDIAN);
// subframe 1, word 6
proto_tree *w6_tree = proto_tree_add_subtree_format(tree, tvb, 20, 4, ett_ubx_gps_l1_sf1_w6, NULL, "Word 6");
proto_tree_add_item(w6_tree, hf_ubx_gps_l1_sf1_w6_reserved, tvb, 20, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w6_tree, hf_ubx_gps_l1_sf1_w6_parity, tvb, 20, 4, ENC_LITTLE_ENDIAN);
// subframe 1, word 7
proto_tree *w7_tree = proto_tree_add_subtree_format(tree, tvb, 24, 4, ett_ubx_gps_l1_sf1_w7, NULL, "Word 7");
proto_tree_add_item(w7_tree, hf_ubx_gps_l1_sf1_w7_reserved, tvb, 24, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w7_tree, hf_ubx_gps_l1_sf1_w7_tgd, tvb, 24, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w7_tree, hf_ubx_gps_l1_sf1_w7_parity, tvb, 24, 4, ENC_LITTLE_ENDIAN);
// subframe 1, word 8
proto_tree *w8_tree = proto_tree_add_subtree_format(tree, tvb, 28, 4, ett_ubx_gps_l1_sf1_w8, NULL, "Word 8");
proto_tree_add_item(w8_tree, hf_ubx_gps_l1_sf1_w8_iodc_lsbs, tvb, 28, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w8_tree, hf_ubx_gps_l1_sf1_w8_toc, tvb, 28, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w8_tree, hf_ubx_gps_l1_sf1_w8_parity, tvb, 28, 4, ENC_LITTLE_ENDIAN);
// subframe 1, word 9
proto_tree *w9_tree = proto_tree_add_subtree_format(tree, tvb, 32, 4, ett_ubx_gps_l1_sf1_w9, NULL, "Word 9");
proto_tree_add_item(w9_tree, hf_ubx_gps_l1_sf1_w9_af2, tvb, 32, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w9_tree, hf_ubx_gps_l1_sf1_w9_af1, tvb, 32, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w9_tree, hf_ubx_gps_l1_sf1_w9_parity, tvb, 32, 4, ENC_LITTLE_ENDIAN);
// subframe 1, word 10
proto_tree *w10_tree = proto_tree_add_subtree_format(tree, tvb, 36, 4, ett_ubx_gps_l1_sf1_w10, NULL, "Word 10");
proto_tree_add_item(w10_tree, hf_ubx_gps_l1_sf1_w10_af0, tvb, 36, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(w10_tree, hf_ubx_gps_l1_sf1_w10_parity, tvb, 36, 4, ENC_LITTLE_ENDIAN);
return tvb_captured_length(tvb);
}
void proto_register_ubx_gps_l1(void) {
static hf_register_info hf[] = {
// TLM
{&hf_ubx_gps_l1_tlm_preamble, {"Preamble", "gps_l1.tlm.preamble", FT_UINT32, BASE_HEX, NULL, 0x3fc00000, NULL, HFILL}},
{&hf_ubx_gps_l1_tlm_message, {"Message", "gps_l1.tlm.message", FT_UINT32, BASE_HEX, NULL, 0x003fff00, NULL, HFILL}},
{&hf_ubx_gps_l1_tlm_integrity, {"Integrity Status Flag (ISF)", "gps_l1.tlm.integrity", FT_BOOLEAN, 32, NULL, 0x00000080, NULL, HFILL}},
{&hf_ubx_gps_l1_tlm_reserved, {"Reserved", "gps_l1.tlm.reserved", FT_UINT32, BASE_HEX, NULL, 0x00000040, NULL, HFILL}},
{&hf_ubx_gps_l1_tlm_parity, {"Parity", "gps_l1.tlm.parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
// HOW
{&hf_ubx_gps_l1_how_tow_count, {"Time-of-Week (TOW) Count", "gps_l1.how.tow_count", FT_UINT32, BASE_CUSTOM, CF_FUNC(&fmt_tow_count), 0x3fffe000, NULL, HFILL}},
{&hf_ubx_gps_l1_how_alert, {"Alert", "gps_l1.how.alert", FT_BOOLEAN, 32, NULL, 0x00001000, NULL, HFILL}},
{&hf_ubx_gps_l1_how_anti_spoof, {"Anti-Spoof (A-S)", "gps_l1.how.anti_spoof", FT_BOOLEAN, 32, NULL, 0x00000800, NULL, HFILL}},
{&hf_ubx_gps_l1_how_subframe_id, {"Subframe ID", "gps_l1.how.subframe_id", FT_UINT32, BASE_DEC, NULL, 0x00000700, NULL, HFILL}},
{&hf_ubx_gps_l1_how_parity_sol, {"Solved for parity zero bits", "gps_l1.how.parity_sol", FT_UINT32, BASE_HEX, NULL, 0x000000c0, NULL, HFILL}},
{&hf_ubx_gps_l1_how_parity, {"Parity", "gps_l1.how.parity", FT_UINT32, BASE_DEC, NULL, 0x0000003f, NULL, HFILL}},
// SF1
{&hf_ubx_gps_l1_sf1, {"Subframe 1", "gps_l1.sf1", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_week_no, {"Week Number", "gps_l1.sf1.week_number", FT_UINT32, BASE_DEC, NULL, 0x3ff00000, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_l2_channel_code, {"L2 Channel Code", "gps_l1.sf1.l2_channel_code", FT_UINT32, BASE_HEX, VALS(L2_CHANNEL_CODE), 0x000c0000, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_ura_index, {"URA Index", "gps_l1.sf1.ura_index", FT_UINT32, BASE_HEX, VALS(URA_INDEX), 0x0003c000, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_sv_health_summary, {"SV Health Summary", "gps_l1.sf1.sv_health_summary", FT_UINT32, BASE_HEX, VALS(SV_HEALTH_SUMMARY), 0x00002000, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_sv_health, {"SV's Signal Component Health Status", "gps_l1.sf1.sv_health", FT_UINT32, BASE_HEX, VALS(SV_HEALTH_CODE), 0x00001f00, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_iodc_msbs, {"Issue of Data Clock (IODC) MSBs", "gps_l1.sf1.iodc_msbs", FT_UINT32, BASE_HEX, NULL, 0x000000c0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w3_parity, {"Parity", "gps_l1.sf1.w3_parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w4_l2_p_data_flag, {"L2 P Data Flag", "gps_l1.sf1.l2_p_data_flag", FT_BOOLEAN, 32, NULL, 0x20000000, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w4_reserved, {"Reserved", "gps_l1.sf1.w4_reserved", FT_UINT32, BASE_HEX, NULL, 0x1fffffc0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w4_parity, {"Parity", "gps_l1.sf1.w4_parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w5_reserved, {"Reserved", "gps_l1.sf1.w5_reserved", FT_UINT32, BASE_HEX, NULL, 0x3fffffc0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w5_parity, {"Parity", "gps_l1.sf1.w5_parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w6_reserved, {"Reserved", "gps_l1.sf1.w6_reserved", FT_UINT32, BASE_HEX, NULL, 0x3fffffc0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w6_parity, {"Parity", "gps_l1.sf1.w6_parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w7_reserved, {"Reserved", "gps_l1.sf1.w7_reserved", FT_UINT32, BASE_HEX, NULL, 0x3fffc000, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w7_tgd, {"Estimated Group Delay Differential T_GD", "gps_l1.sf1.w7_tgd", FT_INT32, BASE_DEC, NULL, 0x00003fc0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w7_parity, {"Parity", "gps_l1.sf1.w7_parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w8_iodc_lsbs, {"Issue of Data Clock (IODC) LSBs", "gps_l1.sf1.w8_iodc_lsbs", FT_UINT32, BASE_HEX, NULL, 0x3fc00000, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w8_toc, {"Clock Data Reference Time t_OC", "gps_l1.sf1.w8_toc", FT_UINT32, BASE_CUSTOM, CF_FUNC(&fmt_t_oc), 0x003fffc0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w8_parity, {"Parity", "gps_l1.sf1.w8_parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w9_af2, {"Drift Rate Correction Coefficient a_f2", "gps_l1.sf1.w9_af2", FT_INT32, BASE_DEC, NULL, 0x3fc00000, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w9_af1, {"SV Clock Drift Correction Coefficient a_f1", "gps_l1.sf1.w9_af1", FT_INT32, BASE_DEC, NULL, 0x003fffc0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w9_parity, {"Parity", "gps_l1.sf1.w9_parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w10_af0, {"SV Clock Bias Correction Coefficient a_f0", "gps_l1.sf1.w10_af0", FT_INT32, BASE_DEC, NULL, 0x3fffffc0, NULL, HFILL}},
{&hf_ubx_gps_l1_sf1_w10_parity, {"Parity", "gps_l1.sf1.w10_parity", FT_UINT32, BASE_HEX, NULL, 0x0000003f, NULL, HFILL}},
};
expert_module_t *expert_ubx_gps_l1;
static ei_register_info ei[] = {
{&ei_ubx_gps_l1_tlm_preamble, {"gps_l1.tlm.preamble", PI_PROTOCOL, PI_WARN, "Illegal preamble", EXPFILL}},
{&ei_ubx_gps_l1_how_tow_count, {"gps_l1.how.tow_count", PI_PROTOCOL, PI_WARN, "Illegal TOW count", EXPFILL}},
{&ei_ubx_gps_l1_how_subframe_id, {"gps_l1.how.subframe_id", PI_PROTOCOL, PI_WARN, "Illegal subframe ID", EXPFILL}},
};
static gint *ett[] = {
&ett_ubx_gps_l1,
&ett_ubx_gps_l1_tlm,
&ett_ubx_gps_l1_how,
&ett_ubx_gps_l1_sf1_w3,
&ett_ubx_gps_l1_sf1_w4,
&ett_ubx_gps_l1_sf1_w5,
&ett_ubx_gps_l1_sf1_w6,
&ett_ubx_gps_l1_sf1_w7,
&ett_ubx_gps_l1_sf1_w8,
&ett_ubx_gps_l1_sf1_w9,
&ett_ubx_gps_l1_sf1_w10,
};
proto_ubx_gps_l1 = proto_register_protocol("GPS L1 Navigation Message", "GPS L1", "gps_l1");
register_dissector("ubx_gps_l1", dissect_ubx_gps_l1, proto_ubx_gps_l1);
proto_register_field_array(proto_ubx_gps_l1, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_ubx_gps_l1 = expert_register_protocol(proto_ubx_gps_l1);
expert_register_field_array(expert_ubx_gps_l1, ei, array_length(ei));
ubx_gps_l1_sf_dissector_table = register_dissector_table("ubx.rxm.sfrbx.gps_l1.sf",
"GPS L1 LNAV Subframe", proto_ubx_gps_l1, FT_UINT8, BASE_DEC);
}
void proto_reg_handoff_ubx_gps_l1(void) {
dissector_add_uint("ubx.rxm.sfrbx.gnssid", GNSS_ID_GPS,
create_dissector_handle(dissect_ubx_gps_l1, proto_ubx_gps_l1));
dissector_add_uint("ubx.rxm.sfrbx.gps_l1.sf", 1,
create_dissector_handle(dissect_ubx_gps_l1_sf1, proto_ubx_gps_l1));
}