wireshark/epan/dissectors/packet-aprs.c

1825 lines
51 KiB
C

/* packet-aprs.c
* Routines for Amateur Packet Radio protocol dissection
* Copyright 2007,2008,2009,2010,2012 R.W. Stearn <richard@rns-stearn.demon.co.uk>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/*
* This dissector is for APRS (Automatic Packet Reporting System)
*
* Information was drawn from:
* http://www.aprs.org/
* Specification: http://www.aprs.org/doc/APRS101.PDF
*
* Inspiration on how to build the dissector drawn from
* packet-sdlc.c
* packet-x25.c
* packet-lapb.c
* paket-gprs-llc.c
* xdlc.c
* with the base file built from README.developers.
*/
#include "config.h"
#include <math.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/to_str.h>
#define STRLEN 100
void proto_register_aprs(void);
static int proto_aprs = -1;
/* aprs timestamp items */
static int hf_aprs_dhm = -1;
static int hf_aprs_hms = -1;
static int hf_aprs_mdhm = -1;
static int hf_aprs_tz = -1;
/* aprs position items */
/* static int hf_aprs_position = -1; */
static int hf_aprs_lat = -1;
static int hf_aprs_long = -1;
/* aprs msg items */
static int hf_aprs_msg = -1;
static int hf_aprs_msg_rng = -1;
static int hf_aprs_msg_cse = -1;
static int hf_aprs_msg_spd = -1;
static int hf_aprs_msg_dir = -1;
static int hf_aprs_msg_brg = -1;
static int hf_aprs_msg_nrq = -1;
/* aprs compression type items */
static int hf_aprs_compression_type = -1;
static int hf_aprs_ct_gps_fix = -1;
static int hf_aprs_ct_nmea_src = -1;
static int hf_aprs_ct_origin = -1;
/* phg msg items */
static int hf_aprs_msg_phg_p = -1;
static int hf_aprs_msg_phg_h = -1;
static int hf_aprs_msg_phg_g = -1;
static int hf_aprs_msg_phg_d = -1;
/* dfs msg items */
static int hf_aprs_msg_dfs_s = -1;
static int hf_aprs_msg_dfs_h = -1;
static int hf_aprs_msg_dfs_g = -1;
static int hf_aprs_msg_dfs_d = -1;
/* weather items */
static int hf_aprs_weather_dir = -1;
static int hf_aprs_weather_spd = -1;
static int hf_aprs_weather_peak = -1;
static int hf_aprs_weather_temp = -1;
static int hf_aprs_weather_rain_1 = -1;
static int hf_aprs_weather_rain_24 = -1;
static int hf_aprs_weather_rain = -1;
static int hf_aprs_weather_humidty = -1;
static int hf_aprs_weather_press = -1;
static int hf_aprs_weather_luminosity = -1;
static int hf_aprs_weather_snow = -1;
static int hf_aprs_weather_raw_rain = -1;
static int hf_aprs_weather_software = -1;
static int hf_aprs_weather_unit = -1;
/* aod msg items */
static int hf_aprs_msg_aod_t = -1;
static int hf_aprs_msg_aod_c = -1;
/* mic-e msg items */
static int hf_aprs_mic_e_dst = -1;
static int hf_aprs_mic_e_long_d = -1;
static int hf_aprs_mic_e_long_m = -1;
static int hf_aprs_mic_e_long_h = -1;
static int hf_aprs_mic_e_spd_sp = -1;
static int hf_aprs_mic_e_spd_dc = -1;
static int hf_aprs_mic_e_spd_se = -1;
static int hf_aprs_mic_e_telemetry = -1;
static int hf_aprs_mic_e_status = -1;
/* Storm items */
static int hf_aprs_storm_dir = -1;
static int hf_aprs_storm_spd = -1;
static int hf_aprs_storm_type = -1;
static int hf_aprs_storm_sws = -1;
static int hf_aprs_storm_pwg = -1;
static int hf_aprs_storm_cp = -1;
static int hf_aprs_storm_rhw = -1;
static int hf_aprs_storm_rtsw = -1;
static int hf_aprs_storm_rwg = -1;
/* aprs sundry items */
static int hf_aprs_dti = -1;
static int hf_aprs_sym_id = -1;
static int hf_aprs_sym_code = -1;
static int hf_aprs_comment = -1;
static int hf_aprs_storm = -1;
/* aprs main catgories items */
static int hf_ultimeter_2000 = -1;
static int hf_aprs_status = -1;
static int hf_aprs_object = -1;
static int hf_aprs_item = -1;
static int hf_aprs_query = -1;
static int hf_aprs_telemetry = -1;
static int hf_aprs_raw = -1;
static int hf_aprs_station = -1;
static int hf_aprs_message = -1;
static int hf_aprs_agrelo = -1;
static int hf_aprs_maidenhead = -1;
static int hf_aprs_weather = -1;
static int hf_aprs_invalid_test = -1;
static int hf_aprs_user_defined = -1;
static int hf_aprs_third_party = -1;
static int hf_aprs_mic_e_0_current = -1;
static int hf_aprs_mic_e_0_old = -1;
static int hf_aprs_mic_e_old = -1;
static int hf_aprs_mic_e_current = -1;
static int hf_aprs_peet_1 = -1;
static int hf_aprs_peet_2 = -1;
static int hf_aprs_map_feature = -1;
static int hf_aprs_shelter_data = -1;
static int hf_aprs_space_weather = -1;
static gboolean gPREF_APRS_LAX = FALSE;
static gint ett_aprs = -1;
static gint ett_aprs_msg = -1;
static gint ett_aprs_ct = -1;
static gint ett_aprs_weather = -1;
static gint ett_aprs_storm = -1;
static gint ett_aprs_mic_e = -1;
static const value_string ctype_vals[] = {
{ 0, "Compressed" },
{ 1, "TNC BText" },
{ 2, "Software (DOS/Mac/Win/+SA)" },
{ 3, "[tbd]" },
{ 4, "KPC3" },
{ 5, "Pico" },
{ 6, "Other tracker [tbd]" },
{ 7, "Digipeater conversion" },
{ 0, NULL }
};
static const value_string nmea_vals[] = {
{ 0, "other" },
{ 1, "GLL" },
{ 2, "GGA" },
{ 3, "RMC" },
{ 0, NULL }
};
static const value_string gps_vals[] = {
{ 0, "old (last)" },
{ 1, "current" },
{ 0, NULL }
};
/* sorted */
static const value_string aprs_description[] = {
{ 0x1c, "Current Mic-E Data (Rev 0 beta)" },
{ 0x1d, "Old Mic-E Data (Rev 0 beta)" },
{ '#', "Peet Bros U-II Weather Station" },
{ '$', "Raw GPS data or Ultimeter 2000" },
{ '%', "Agrelo DFJr / MicroFinder" },
{ '&', "[Reserved - Map Feature]" },
{ '\'', "Old Mic-E Data (current data for TM-D700)" },
{ ')', "Item" },
{ '*', "Peet Bros U-II Weather Station" },
{ '+', "[Reserved - Shelter data with time]" },
{ ',', "Invalid data or test data" },
{ '.', "[Reserved - Space weather]" },
{ '/', "Position + timestamp" },
{ ':', "Message" },
{ ';', "Object" },
{ '<', "Station Capabilities" },
{ '=', "Position + APRS data extension" },
{ '>', "Status" },
{ '?', "Query" },
{ '@', "Position + timestamp + APRS data extension" },
{ 'T', "Telemetry data" },
{ '[', "Maidenhead grid locator beacon (obsolete)" },
{ '_', "Weather Report (without position)" },
{ '`', "Current Mic-E Data (not used in TM-D700)" },
{ '{', "User-Defined APRS packet format" },
{ '}', "Third-party traffic" },
{ 0, NULL }
};
static value_string_ext aprs_description_ext = VALUE_STRING_EXT_INIT(aprs_description);
/* MIC-E destination field code table */
typedef struct
{
guint8 key;
char digit;
int msg;
char n_s;
int long_offset;
char w_e;
} mic_e_dst_code_table_s;
static const mic_e_dst_code_table_s dst_code[] =
{
{ '0' << 1, '0', 0, 'S', 0, 'E' },
{ '1' << 1, '1', 0, 'S', 0, 'E' },
{ '2' << 1, '2', 0, 'S', 0, 'E' },
{ '3' << 1, '3', 0, 'S', 0, 'E' },
{ '4' << 1, '4', 0, 'S', 0, 'E' },
{ '5' << 1, '5', 0, 'S', 0, 'E' },
{ '6' << 1, '6', 0, 'S', 0, 'E' },
{ '7' << 1, '7', 0, 'S', 0, 'E' },
{ '8' << 1, '8', 0, 'S', 0, 'E' },
{ '9' << 1, '9', 0, 'S', 0, 'E' },
{ 'A' << 1, '0', 1, '?', 0, '?' },
{ 'B' << 1, '1', 1, '?', 0, '?' },
{ 'C' << 1, '2', 1, '?', 0, '?' },
{ 'D' << 1, '3', 1, '?', 0, '?' },
{ 'E' << 1, '4', 1, '?', 0, '?' },
{ 'F' << 1, '5', 1, '?', 0, '?' },
{ 'G' << 1, '6', 1, '?', 0, '?' },
{ 'H' << 1, '7', 1, '?', 0, '?' },
{ 'I' << 1, '8', 1, '?', 0, '?' },
{ 'J' << 1, '9', 1, '?', 0, '?' },
{ 'K' << 1, ' ', 1, '?', 0, '?' },
{ 'L' << 1, ' ', 0, 'S', 0, 'E' },
{ 'P' << 1, '0', 1, 'N', 100, 'W' },
{ 'Q' << 1, '1', 1, 'N', 100, 'W' },
{ 'R' << 1, '2', 1, 'N', 100, 'W' },
{ 'S' << 1, '3', 1, 'N', 100, 'W' },
{ 'T' << 1, '4', 1, 'N', 100, 'W' },
{ 'U' << 1, '5', 1, 'N', 100, 'W' },
{ 'V' << 1, '6', 1, 'N', 100, 'W' },
{ 'W' << 1, '7', 1, 'N', 100, 'W' },
{ 'X' << 1, '8', 1, 'N', 100, 'W' },
{ 'Y' << 1, '9', 1, 'N', 100, 'W' },
{ 'Z' << 1, ' ', 1, 'N', 100, 'W' },
{ 0, '_', 3, '?', 3, '?' },
};
/* MIC-E message table */
typedef struct
{
const char *std;
const char *custom;
} mic_e_msg_table_s;
static const mic_e_msg_table_s mic_e_msg_table[] =
{
{ "Emergency", "Emergency" },
{ "Priority", "Custom 6" },
{ "Special", "Custom 5" },
{ "Committed", "Custom 4" },
{ "Returning", "Custom 3" },
{ "In Service", "Custom 2" },
{ "En Route", "Custom 1" },
{ "Off Duty", "Custom 0" }
};
/* Code to actually dissect the packets */
static int
dissect_aprs_compression_type( tvbuff_t *tvb,
int offset,
proto_tree *parent_tree
)
{
proto_tree *tc;
proto_tree *compression_tree;
int new_offset;
int data_len;
guint8 compression_type;
data_len = 1;
new_offset = offset + data_len;
if ( parent_tree )
{
compression_type = tvb_get_guint8( tvb, offset ) - 33;
tc = proto_tree_add_uint( parent_tree, hf_aprs_compression_type, tvb, offset, data_len,
compression_type );
compression_tree = proto_item_add_subtree( tc, ett_aprs_ct );
proto_tree_add_item( compression_tree, hf_aprs_ct_gps_fix, tvb, offset, data_len, ENC_BIG_ENDIAN );
proto_tree_add_item( compression_tree, hf_aprs_ct_nmea_src, tvb, offset, data_len, ENC_BIG_ENDIAN );
proto_tree_add_item( compression_tree, hf_aprs_ct_origin, tvb, offset, data_len, ENC_BIG_ENDIAN );
}
return new_offset;
}
static int
dissect_aprs_msg( tvbuff_t *tvb,
int offset,
proto_tree *parent_tree,
int wind,
int brg_nrq
)
{
proto_tree *msg_tree = NULL;
guint8 ch;
if ( parent_tree )
{
proto_tree *tc;
tc = proto_tree_add_item( parent_tree, hf_aprs_msg, tvb, offset, 7, ENC_ASCII );
msg_tree = proto_item_add_subtree( tc, ett_aprs_msg );
}
ch = tvb_get_guint8( tvb, offset );
if ( g_ascii_isdigit( ch ) )
{
if ( wind )
proto_tree_add_item( msg_tree, hf_aprs_msg_dir, tvb, offset, 3, ENC_ASCII );
else
proto_tree_add_item( msg_tree, hf_aprs_msg_cse, tvb, offset, 3, ENC_ASCII );
offset += 3;
/* verify the separator */
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_spd, tvb, offset, 3, ENC_ASCII );
offset += 3;
}
else
{
switch ( ch )
{
case 'D' : /* dfs */
offset += 3;
proto_tree_add_item( msg_tree, hf_aprs_msg_dfs_s, tvb, offset, 1, ENC_ASCII );
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_dfs_h, tvb, offset, 1, ENC_ASCII );
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_dfs_g, tvb, offset, 1, ENC_ASCII );
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_dfs_d, tvb, offset, 1, ENC_ASCII );
break;
case 'P' : /* phgd */
offset += 3;
proto_tree_add_item( msg_tree, hf_aprs_msg_phg_p, tvb, offset, 1, ENC_ASCII );
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_phg_h, tvb, offset, 1, ENC_ASCII );
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_phg_g, tvb, offset, 1, ENC_ASCII );
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_phg_d, tvb, offset, 1, ENC_ASCII );
break;
case 'R' : /* rng */
proto_tree_add_item( msg_tree, hf_aprs_msg_rng, tvb, offset, 7, ENC_ASCII );
break;
case 'T' : /* aod */
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_aod_t, tvb, offset, 2, ENC_ASCII );
offset += 2;
/* step over the /C */
offset += 2;
proto_tree_add_item( msg_tree, hf_aprs_msg_aod_c, tvb, offset, 2, ENC_ASCII );
break;
default : /* wtf */
break;
}
}
if ( brg_nrq )
{
proto_tree_add_item( msg_tree, hf_aprs_msg_brg, tvb, offset, 3, ENC_ASCII );
offset += 3;
/* verify the separator */
offset += 1;
proto_tree_add_item( msg_tree, hf_aprs_msg_nrq, tvb, offset, 3, ENC_ASCII );
offset += 3;
}
return offset;
}
static int
dissect_aprs_compressed_msg( tvbuff_t *tvb,
int offset,
proto_tree *parent_tree
)
{
proto_tree *tc;
proto_tree *msg_tree;
int new_offset;
int data_len;
guint8 ch;
guint8 course;
double speed;
double range;
gchar *info_buffer;
data_len = 2;
new_offset = offset + data_len;
if ( parent_tree )
{
tc = proto_tree_add_item( parent_tree, hf_aprs_msg, tvb, offset, data_len, ENC_ASCII );
msg_tree = proto_item_add_subtree( tc, ett_aprs_msg );
ch = tvb_get_guint8( tvb, offset );
if ( ch != ' ' )
{
if ( ch == '{' )
{ /* Pre-Calculated Radio Range */
offset += 1;
ch = tvb_get_guint8( tvb, offset );
range = exp( log( 1.08 ) * (ch - 33) );
info_buffer = wmem_strdup_printf( wmem_packet_scope(), "%7.2f", range );
proto_tree_add_string( msg_tree, hf_aprs_msg_rng, tvb, offset, 1, info_buffer );
}
else
if ( ch >= '!' && ch <= 'z' )
{ /* Course/Speed */
course = (ch - 33) * 4;
info_buffer = wmem_strdup_printf( wmem_packet_scope(), "%d", course );
proto_tree_add_string( msg_tree, hf_aprs_msg_cse,
tvb, offset, 1, info_buffer );
offset += 1;
ch = tvb_get_guint8( tvb, offset );
speed = exp( log( 1.08 ) * (ch - 33) );
info_buffer = wmem_strdup_printf( wmem_packet_scope(), "%7.2f", speed );
proto_tree_add_string( msg_tree, hf_aprs_msg_spd,
tvb, offset, 1, info_buffer );
}
}
}
return new_offset;
}
static const mic_e_dst_code_table_s *
dst_code_lookup( guint8 ch )
{
guint indx;
indx = 0;
while ( indx < ( sizeof( dst_code ) / sizeof( mic_e_dst_code_table_s ) )
&& dst_code[ indx ].key != ch
&& dst_code[ indx ].key > 0 )
indx++;
return &( dst_code[ indx ] );
}
static int
d28_to_deg( guint8 code, int long_offset )
{
int value;
value = code - 28 + long_offset;
if ( value >= 180 && value <= 189 )
value -= 80;
else
if ( value >= 190 && value <= 199 )
value -= 190;
return value;
}
static int
d28_to_min( guint8 code )
{
int value;
value = code - 28;
if ( value >= 60 )
value -= 60;
return value;
}
static int
dissect_mic_e( tvbuff_t *tvb,
int offset,
packet_info *pinfo,
proto_tree *parent_tree,
int hf_mic_e_idx
)
{
proto_tree *tc;
proto_tree *mic_e_tree;
int new_offset;
int data_len;
char *info_buffer;
char latitude[8] = { '?', '?', '?', '?', '.', '?', '?', 0x0 };
int msg_a;
int msg_b;
int msg_c;
char n_s;
int long_offset;
char w_e;
int cse;
int spd;
guint8 ssid;
const guint8 *addr;
const mic_e_dst_code_table_s *dst_code_entry;
data_len = tvb_reported_length_remaining( tvb, offset );
new_offset = offset + data_len;
info_buffer = (char *)wmem_alloc( wmem_packet_scope(), STRLEN );
msg_a = 0;
msg_b = 0;
msg_c = 0;
n_s = '?';
long_offset = 0;
w_e = '?';
ssid = 0;
if ( pinfo->dst.type == AT_AX25 && pinfo->dst.len == AX25_ADDR_LEN )
{
/* decode the AX.25 destination address */
addr = (const guint8 *)pinfo->dst.data;
dst_code_entry = dst_code_lookup( addr[ 0 ] );
latitude[ 0 ] = dst_code_entry->digit;
msg_a = dst_code_entry->msg & 0x1;
dst_code_entry = dst_code_lookup( addr[ 1 ] );
latitude[ 1 ] = dst_code_entry->digit;
msg_b = dst_code_entry->msg & 0x1;
dst_code_entry = dst_code_lookup( addr[ 2 ] );
latitude[ 2 ] = dst_code_entry->digit;
msg_c = dst_code_entry->msg & 0x1;
dst_code_entry = dst_code_lookup( addr[ 3 ] );
latitude[ 3 ] = dst_code_entry->digit;
n_s = dst_code_entry->n_s;
/* '.' already set */
dst_code_entry = dst_code_lookup( addr[ 4 ] );
latitude[ 5 ] = dst_code_entry->digit;
long_offset = dst_code_entry->long_offset;
dst_code_entry = dst_code_lookup( addr[ 5 ] );
latitude[ 6 ] = dst_code_entry->digit;
w_e = dst_code_entry->w_e;
ssid = (addr[ 6 ] >> 1) & 0x0f;
}
/* decode the mic-e info fields */
spd = ((tvb_get_guint8( tvb, offset + 3 ) - 28) * 10) + ((tvb_get_guint8( tvb, offset + 4 ) - 28) / 10);
if ( spd >= 800 )
spd -= 800;
cse = (((tvb_get_guint8( tvb, offset + 4 ) - 28) % 10) * 100) + ((tvb_get_guint8( tvb, offset + 5 ) - 28) * 10);
if ( cse >= 400 )
cse -= 400;
snprintf( info_buffer, STRLEN,
"Lat: %7.7s%c Long: %03d%02d.%02d%c, Cse: %d, Spd: %d, SSID: %d, Msg %s",
latitude,
n_s,
d28_to_deg( tvb_get_guint8( tvb, offset ), long_offset ),
d28_to_min( tvb_get_guint8( tvb, offset + 1 ) ),
tvb_get_guint8( tvb, offset + 2 ) - 28,
w_e,
cse,
spd,
ssid,
mic_e_msg_table[ (msg_a << 2) + (msg_b << 1) + msg_c ].std
);
col_set_str( pinfo->cinfo, COL_INFO, "MIC-E " );
col_append_str( pinfo->cinfo, COL_INFO, info_buffer );
if ( parent_tree )
{
tc = proto_tree_add_string( parent_tree, hf_mic_e_idx, tvb, offset, data_len, info_buffer );
mic_e_tree = proto_item_add_subtree( tc, ett_aprs_mic_e );
snprintf( info_buffer, STRLEN,
"Lat %7.7s, Msg A %d, Msg B %d, Msg C %d, N/S %c, Long off %3d, W/E %c, SSID %d",
latitude,
msg_a,
msg_b,
msg_c,
n_s,
long_offset,
w_e,
ssid
);
proto_tree_add_string( mic_e_tree, hf_aprs_mic_e_dst, tvb, 0, 0, info_buffer ); /* ?? */
proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_long_d, tvb, offset, 1, ENC_BIG_ENDIAN );
offset += 1;
proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_long_m, tvb, offset, 1, ENC_BIG_ENDIAN );
offset += 1;
proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_long_h, tvb, offset, 1, ENC_BIG_ENDIAN );
offset += 1;
proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_spd_sp, tvb, offset, 1, ENC_BIG_ENDIAN );
offset += 1;
proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_spd_dc, tvb, offset, 1, ENC_BIG_ENDIAN );
offset += 1;
proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_spd_se, tvb, offset, 1, ENC_BIG_ENDIAN );
offset += 1;
proto_tree_add_item( mic_e_tree, hf_aprs_sym_code, tvb, offset, 1, ENC_ASCII );
offset += 1;
proto_tree_add_item( mic_e_tree, hf_aprs_sym_id, tvb, offset, 1, ENC_ASCII );
offset += 1;
if ( offset < new_offset )
{
guint8 c = tvb_get_guint8(tvb, offset);
if ( (c == ',') || (c == 0x1d) )
proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_telemetry,
tvb, offset, -1, ENC_NA );
else
proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_status,
tvb, offset, -1, ENC_ASCII );
}
}
return new_offset;
}
static int
dissect_aprs_storm( tvbuff_t *tvb,
int offset,
proto_tree *parent_tree
)
{
proto_tree *storm_tree;
proto_tree *tc;
tc = proto_tree_add_item( parent_tree, hf_aprs_storm, tvb, offset, -1, ENC_ASCII );
storm_tree = proto_item_add_subtree( tc, ett_aprs_storm );
proto_tree_add_item( storm_tree, hf_aprs_storm_dir, tvb, offset, 3, ENC_ASCII );
offset += 3;
offset += 1;
proto_tree_add_item( storm_tree, hf_aprs_storm_spd, tvb, offset, 3, ENC_ASCII );
offset += 3;
proto_tree_add_item( storm_tree, hf_aprs_storm_type, tvb, offset, 3, ENC_ASCII );
offset += 3;
proto_tree_add_item( storm_tree, hf_aprs_storm_sws, tvb, offset, 4, ENC_ASCII );
offset += 4;
proto_tree_add_item( storm_tree, hf_aprs_storm_pwg, tvb, offset, 4, ENC_ASCII );
offset += 4;
proto_tree_add_item( storm_tree, hf_aprs_storm_cp, tvb, offset, 5, ENC_ASCII );
offset += 5;
proto_tree_add_item( storm_tree, hf_aprs_storm_rhw, tvb, offset, 4, ENC_ASCII );
offset += 4;
proto_tree_add_item( storm_tree, hf_aprs_storm_rtsw, tvb, offset, 4, ENC_ASCII );
offset += 4;
proto_tree_add_item( storm_tree, hf_aprs_storm_rwg, tvb, offset, 4, ENC_ASCII );
offset += 4;
return offset;
}
static int
dissect_aprs_weather( tvbuff_t *tvb,
int offset,
proto_tree *parent_tree
)
{
proto_tree *tc;
proto_tree *weather_tree;
int new_offset;
int data_len;
guint8 ch;
data_len = tvb_reported_length_remaining( tvb, offset );
new_offset = offset + data_len;
tc = proto_tree_add_item( parent_tree, hf_aprs_weather, tvb, offset, data_len, ENC_ASCII );
weather_tree = proto_item_add_subtree( tc, ett_aprs_weather );
ch = tvb_get_guint8( tvb, offset );
if ( g_ascii_isdigit( ch ) )
{
proto_tree_add_item( weather_tree, hf_aprs_weather_dir, tvb, offset, 3, ENC_ASCII );
offset += 3;
/* verify the separator */
offset += 1;
proto_tree_add_item( weather_tree, hf_aprs_weather_spd, tvb, offset, 3, ENC_ASCII );
offset += 3;
}
if ( parent_tree )
{
while ( offset < new_offset )
{
ch = tvb_get_guint8( tvb, offset );
switch ( ch )
{
case 'c' :
proto_tree_add_item( weather_tree, hf_aprs_weather_dir,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case 's' :
proto_tree_add_item( weather_tree, hf_aprs_weather_spd,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case 'g' :
proto_tree_add_item( weather_tree, hf_aprs_weather_peak,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case 't' :
proto_tree_add_item( weather_tree, hf_aprs_weather_temp,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case 'r' :
proto_tree_add_item( weather_tree, hf_aprs_weather_rain_1,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case 'P' :
proto_tree_add_item( weather_tree, hf_aprs_weather_rain_24,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case 'p' :
proto_tree_add_item( weather_tree, hf_aprs_weather_rain,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case 'h' :
proto_tree_add_item( weather_tree, hf_aprs_weather_humidty,
tvb, offset, 3, ENC_ASCII );
offset += 3;
break;
case 'b' :
proto_tree_add_item( weather_tree, hf_aprs_weather_press,
tvb, offset, 6, ENC_ASCII );
offset += 6;
break;
case 'l' :
case 'L' :
proto_tree_add_item( weather_tree, hf_aprs_weather_luminosity,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case 'S' :
proto_tree_add_item( weather_tree, hf_aprs_weather_snow,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
case '#' :
proto_tree_add_item( weather_tree, hf_aprs_weather_raw_rain,
tvb, offset, 4, ENC_ASCII );
offset += 4;
break;
default : {
gint lr;
/* optional: software type/unit: see if present */
lr = new_offset - offset;
#if 0 /* fcn'al change: defer */
/*
* XXX - ASCII or UTF-8?
* See http://www.aprs.org/aprs12/utf-8.txt
*/
if ( ((lr < 3) || (lr > 5)) ||
( lr != strspn( tvb_get_string_enc( wmem_packet_scope(), tvb, offset, lr, ENC_ASCII|ENC_NA ), "a-zA-Z0-9-_" ) ) )
{
new_offset = offset; /* Assume rest is a comment: force exit from while */
break; /* from switch */
}
#endif
proto_tree_add_item( weather_tree, hf_aprs_weather_software,
tvb, offset, 1, ENC_ASCII );
offset += 1;
proto_tree_add_item( weather_tree, hf_aprs_weather_unit,
tvb, offset, lr-1, ENC_ASCII );
offset = new_offset;
break;
}
} /* switch */
} /* while */
} /* if (parent_tree) */
return new_offset;
}
static int
aprs_timestamp( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
{
int data_len;
const char *tzone;
guint8 ch;
data_len = 8;
tzone = "zulu";
ch= tvb_get_guint8( tvb, offset + 6 );
if ( g_ascii_isdigit( ch ) )
{ /* MDHM */
proto_tree_add_item( aprs_tree, hf_aprs_mdhm, tvb, offset, data_len, ENC_ASCII );
proto_tree_add_string( aprs_tree, hf_aprs_tz, tvb, offset, data_len, tzone );
}
else
{
data_len -= 1;
if ( ch == 'h' )
{ /* HMS */
proto_tree_add_item( aprs_tree, hf_aprs_hms, tvb, offset, data_len, ENC_ASCII );
proto_tree_add_string( aprs_tree, hf_aprs_tz, tvb, offset, data_len, tzone );
}
else
{ /* DHM */
switch ( ch )
{
case 'z' : tzone = "zulu"; break;
case '/' : tzone = "local"; break;
default : tzone = "unknown"; break;
}
proto_tree_add_item( aprs_tree, hf_aprs_dhm, tvb, offset, data_len, ENC_ASCII );
proto_tree_add_string( aprs_tree, hf_aprs_tz, tvb, offset + 6, 1, tzone );
}
}
return offset + data_len;
}
static int
aprs_latitude_compressed( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
{
if ( aprs_tree )
{
char *info_buffer;
int temp;
info_buffer = (char *)wmem_alloc( wmem_packet_scope(), STRLEN );
temp = ( tvb_get_guint8( tvb, offset + 0 ) - 33 );
temp = ( tvb_get_guint8( tvb, offset + 1 ) - 33 ) + ( temp * 91 );
temp = ( tvb_get_guint8( tvb, offset + 2 ) - 33 ) + ( temp * 91 );
temp = ( tvb_get_guint8( tvb, offset + 3 ) - 33 ) + ( temp * 91 );
snprintf( info_buffer, STRLEN, "%6.2f", 90.0 - (temp / 380926.0) );
proto_tree_add_string( aprs_tree, hf_aprs_lat, tvb, offset, 4, info_buffer );
}
return offset + 4;
}
static int
aprs_longitude_compressed( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
{
if ( aprs_tree )
{
char *info_buffer;
int temp;
info_buffer = (char *)wmem_alloc( wmem_packet_scope(), STRLEN );
temp = ( tvb_get_guint8( tvb, offset + 0 ) - 33 );
temp = ( tvb_get_guint8( tvb, offset + 1 ) - 33 ) + ( temp * 91 );
temp = ( tvb_get_guint8( tvb, offset + 2 ) - 33 ) + ( temp * 91 );
temp = ( tvb_get_guint8( tvb, offset + 3 ) - 33 ) + ( temp * 91 );
snprintf( info_buffer, STRLEN, "%7.2f", (temp / 190463.0) - 180.0 );
proto_tree_add_string( aprs_tree, hf_aprs_long, tvb, offset, 4, info_buffer );
}
return offset + 4;
}
static int
aprs_status( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
{
int data_len;
data_len = tvb_reported_length_remaining( tvb, offset );
if ( ( data_len > 7 ) && ( tvb_get_guint8( tvb, offset+6 ) == 'z' ) )
{
proto_tree_add_item( aprs_tree, hf_aprs_dhm, tvb, offset, 6, ENC_ASCII );
offset += 6;
data_len -= 6;
proto_tree_add_string( aprs_tree, hf_aprs_tz, tvb, offset, 1, "zulu" );
offset += 1;
data_len -= 1;
}
proto_tree_add_item( aprs_tree, hf_aprs_status, tvb, offset, data_len, ENC_ASCII );
return offset + data_len;
}
static int
aprs_item( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
{
char *info_buffer;
int data_len;
char *ch_ptr;
data_len = 10;
/*
* XXX - ASCII or UTF-8?
* See http://www.aprs.org/aprs12/utf-8.txt
*/
info_buffer = tvb_get_string_enc( wmem_packet_scope(), tvb, offset, data_len, ENC_ASCII|ENC_NA );
ch_ptr = strchr( info_buffer, '!' );
if ( ch_ptr != NULL )
{
data_len = (int)(ch_ptr - info_buffer + 1);
*ch_ptr = '\0';
}
else
{
ch_ptr = strchr( info_buffer, '!' );
if ( ch_ptr != NULL )
{
data_len = (int)(ch_ptr - info_buffer + 1);
*ch_ptr = '\0';
}
}
proto_tree_add_string( aprs_tree, hf_aprs_item, tvb, offset, data_len, info_buffer );
return offset + data_len;
}
static int
aprs_3rd_party( proto_tree *aprs_tree, tvbuff_t *tvb, int offset, int data_len )
{
/* If the type of the hf[] entry pointed to by hfindex is FT_BYTES or FT_STRING */
/* then data_len == -1 is allowed and means "remainder of the tvbuff" */
if ( data_len == -1 )
{
data_len = tvb_reported_length_remaining( tvb, offset );
#if 0 /* fcn'al change: defer */
if ( data_len <= 0 )
return offset; /* there's no data */
#endif
}
proto_tree_add_item( aprs_tree, hf_aprs_third_party, tvb, offset, data_len, ENC_NA );
/* tnc-2 */
/* aea */
return offset + data_len;
}
static int
aprs_default_string( proto_tree *aprs_tree, tvbuff_t *tvb, int offset, int data_len, int hfindex )
{
/* Assumption: hfindex points to an hf[] entry with type FT_STRING; should be validated ? */
/* If the type of the hf[] entry pointed to by hfindex is FT_STRING */
/* then data_len == -1 is allowed and means "remainder of the tvbuff" */
if ( data_len == -1 )
{
data_len = tvb_reported_length_remaining( tvb, offset );
#if 0 /* fcn'al change: defer */
if ( data_len <= 0 )
return offset; /* there's no data */
#endif
}
proto_tree_add_item( aprs_tree, hfindex, tvb, offset, data_len, ENC_ASCII|ENC_NA );
return offset + data_len;
}
static int
aprs_default_bytes( proto_tree *aprs_tree, tvbuff_t *tvb, int offset, int data_len, int hfindex )
{
/* Assumption: hfindex points to an hf[] entry with type FT_BYTES; should be validated ? */
/* If the type of the hf[] entry pointed to by hfindex is FT_BYTES */
/* then data_len == -1 is allowed and means "remainder of the tvbuff" */
if ( data_len == -1 )
{
data_len = tvb_reported_length_remaining( tvb, offset );
#if 0 /* fcn'al change: defer */
if ( data_len <= 0 )
return offset; /* there's no data */
#endif
}
proto_tree_add_item( aprs_tree, hfindex, tvb, offset, data_len, ENC_NA );
return offset + data_len;
}
static int
aprs_position( proto_tree *aprs_tree, tvbuff_t *tvb, int offset, gboolean with_msg )
{
guint8 symbol_table_id = 0;
guint8 symbol_code = 0;
gboolean probably_a_msg = FALSE;
gboolean probably_not_a_msg = FALSE;
if ( g_ascii_isdigit( tvb_get_guint8( tvb, offset ) ) )
{
offset = aprs_default_string( aprs_tree, tvb, offset, 8, hf_aprs_lat );
symbol_table_id = tvb_get_guint8( tvb, offset );
offset = aprs_default_string( aprs_tree, tvb, offset, 1, hf_aprs_sym_id );
offset = aprs_default_string( aprs_tree, tvb, offset, 9, hf_aprs_long );
symbol_code = tvb_get_guint8( tvb, offset );
offset = aprs_default_string( aprs_tree, tvb, offset, 1, hf_aprs_sym_code );
if ( gPREF_APRS_LAX )
{
switch ( tvb_get_guint8( tvb, offset ) )
{
case 'D' : probably_a_msg = TRUE; break;
case 'P' : probably_a_msg = TRUE; break;
case 'R' : probably_a_msg = TRUE; break;
case 'T' : probably_a_msg = TRUE; break;
default : probably_not_a_msg = TRUE; break;
}
}
if ( with_msg || probably_a_msg || ! probably_not_a_msg )
offset = dissect_aprs_msg( tvb,
offset,
aprs_tree,
( symbol_code == '_' ),
( symbol_table_id == '/' && symbol_code == '\\' )
);
}
else
{
symbol_table_id = tvb_get_guint8( tvb, offset );
offset = aprs_default_string( aprs_tree, tvb, offset, 1, hf_aprs_sym_id );
offset = aprs_latitude_compressed( aprs_tree, tvb, offset );
offset = aprs_longitude_compressed( aprs_tree, tvb, offset );
symbol_code = tvb_get_guint8( tvb, offset );
offset = aprs_default_string( aprs_tree, tvb, offset, 1, hf_aprs_sym_code );
offset = dissect_aprs_compressed_msg( tvb,
offset,
aprs_tree
);
offset = dissect_aprs_compression_type( tvb,
offset,
aprs_tree
);
if ( symbol_table_id == '/' && symbol_code == '\\' )
offset = aprs_default_string( aprs_tree, tvb, offset, 8, hf_aprs_msg_brg );
}
if ( symbol_code == '_' )
offset = dissect_aprs_weather( tvb,
offset,
aprs_tree
);
if ( ( symbol_table_id == '/' && symbol_code == '@' ) || ( symbol_table_id == '\\' && symbol_code == '@' ) )
offset = dissect_aprs_storm( tvb,
offset,
aprs_tree
);
return offset;
}
static int
dissect_aprs( tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_ )
{
proto_item *ti;
proto_tree *aprs_tree;
int offset;
guint8 dti;
wmem_strbuf_t *sb;
col_set_str( pinfo->cinfo, COL_PROTOCOL, "APRS" );
col_clear( pinfo->cinfo, COL_INFO );
offset = 0;
dti = tvb_get_guint8( tvb, offset );
sb = wmem_strbuf_create(wmem_packet_scope());
if (dti != '!')
wmem_strbuf_append(sb, val_to_str_ext_const(dti, &aprs_description_ext, ""));
switch ( dti )
{
case '!':
/* Position or Ultimeter 2000 WX Station */
if ( tvb_get_guint8( tvb, offset + 1 ) == '!' )
{
wmem_strbuf_append(sb, "Ultimeter 2000 WX Station");
}
else
{
/* Position "without APRS messaging" */
wmem_strbuf_append(sb, "Position (");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1, 8)); /* Lat */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8 + 1, 9)); /* Long */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8, 1)); /* Symbol table id */
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8 + 1 + 9, 1)); /* Symbol Code */
}
break;
case '=':
/* Position "with APRS messaging" + Ext APRS message */
wmem_strbuf_append(sb, " (");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1, 8)); /* Lat */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8 + 1, 9)); /* Long */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8, 1)); /* Symbol table id */
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8 + 1 + 9, 1)); /* Symbol Code */
break;
case '/':
/* Position + timestamp "without APRS messaging" */
wmem_strbuf_append(sb, " (");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1, 7)); /* Timestamp */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 1, 8)); /*??*/ /* Lat */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 8 + 1, 9)); /* Long */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7, 1)); /* Symbol table id */
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 1 + 9, 1)); /* Symbol Code */
break;
case '@':
/* Position + timestamp "with APRS messaging" + Ext APRS message */
wmem_strbuf_append(sb, " (");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1, 7)); /* Timestamp */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 1, 8)); /*??*/ /* Lat */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 8 + 1, 9)); /* Long */
wmem_strbuf_append(sb, " ");
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7, 1)); /* Symbol table id */
wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 1 + 9, 1)); /* Symbol Code */
break;
}
col_add_str( pinfo->cinfo, COL_INFO, wmem_strbuf_get_str(sb) );
/* create display subtree for the protocol */
ti = proto_tree_add_protocol_format( parent_tree , proto_aprs, tvb, 0, -1, "%s", wmem_strbuf_get_str(sb) );
aprs_tree = proto_item_add_subtree( ti, ett_aprs );
proto_tree_add_item( aprs_tree, hf_aprs_dti, tvb, offset, 1, ENC_ASCII );
offset += 1;
switch ( dti )
{
case '<' : /* Station Capabilities */
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_station );
break;
case '>' : /* Status */
offset = aprs_status( aprs_tree, tvb, offset );
break;
case '?' : /* Query */
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_query );
break;
case '$' : /* Raw GPS data or Ultimeter 2000 */
if ( tvb_get_guint8( tvb, offset ) == 'U' )
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_ultimeter_2000 );
else
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_raw );
break;
case '%' : /* Agrelo DFJr / MicroFinder */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_agrelo );
break;
case 'T' : /* Telemetry data */
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_telemetry );
break;
case '[' : /* Maidenhead grid locator beacon (obsolete) */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_maidenhead );
break;
case '_' : /* Weather Report (without position) */
offset = aprs_timestamp( aprs_tree, tvb, offset );
offset = dissect_aprs_weather( tvb,
offset,
aprs_tree
);
break;
case ',' : /* Invalid data or test data */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_invalid_test );
break;
case '{' : /* User-Defined APRS packet format */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_user_defined );
break;
case '}' : /* Third-party traffic */
offset = aprs_3rd_party( aprs_tree, tvb, offset, -1 );
break;
case ':' : /* Message */
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_message );
break;
case 0x1c : /* Current Mic-E Data (Rev 0 beta) */
offset = dissect_mic_e( tvb,
offset,
pinfo,
aprs_tree,
hf_aprs_mic_e_0_current
);
break;
case 0x1d : /* Old Mic-E Data (Rev 0 beta) */
offset = dissect_mic_e( tvb,
offset,
pinfo,
aprs_tree,
hf_aprs_mic_e_0_old
);
break;
case '\'' : /* Old Mic-E Data (but Current data for TM-D700) */
offset = dissect_mic_e( tvb,
offset,
pinfo,
aprs_tree,
hf_aprs_mic_e_old
);
break;
case '`' : /* Current Mic-E Data (not used in TM-D700) */
offset = dissect_mic_e( tvb,
offset,
pinfo,
aprs_tree,
hf_aprs_mic_e_current
);
break;
case '#' : /* Peet Bros U-II Weather Station */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_peet_1 );
break;
case '*' : /* Peet Bros U-II Weather Station */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_peet_2 );
break;
case '&' : /* [Reserved - Map Feature] */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_map_feature );
break;
case '+' : /* [Reserved - Shelter data with time] */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_shelter_data );
break;
case '.' : /* [Reserved - Space weather] */
offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_space_weather );
break;
case ')' : /* Item */
offset = aprs_item( aprs_tree, tvb, offset );
offset = aprs_position( aprs_tree, tvb, offset, TRUE );
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
break;
case ';' : /* Object */
offset = aprs_default_string( aprs_tree, tvb, offset, 10, hf_aprs_object );
offset = aprs_timestamp( aprs_tree, tvb, offset );
offset = aprs_position( aprs_tree, tvb, offset, TRUE );
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
break;
case '!' : /* Position or Ultimeter 2000 WX Station */
if ( tvb_get_guint8( tvb, offset ) == '!' )
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_ultimeter_2000 );
else
{
offset = aprs_position( aprs_tree, tvb, offset, FALSE );
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
}
break;
case '=' : /* Position + Ext APRS message */
offset = aprs_position( aprs_tree, tvb, offset, TRUE );
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
break;
case '/' : /* Position + timestamp */
offset = aprs_timestamp( aprs_tree, tvb, offset );
offset = aprs_position( aprs_tree, tvb, offset, FALSE );
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
break;
case '@' : /* Position + timestamp + Ext APRS message */
offset = aprs_timestamp( aprs_tree, tvb, offset );
offset = aprs_position( aprs_tree, tvb, offset, TRUE );
offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
break;
default : break;
}
return offset;
}
void
proto_register_aprs( void )
{
module_t *aprs_module;
static hf_register_info hf[] = {
{ &hf_aprs_dti,
{ "Data Type Indicator", "aprs.dti",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_sym_code,
{ "Symbol code", "aprs.sym_code",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_sym_id,
{ "Symbol table ID", "aprs.sym_id",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Position */
#if 0
{ &hf_aprs_position,
{ "Position", "aprs.position",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
#endif
{ &hf_aprs_lat,
{ "Latitude", "aprs.position.lat",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_long,
{ "Longitude", "aprs.position.long",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* APRS Messages */
{ &hf_aprs_comment,
{ "Comment", "aprs.comment",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_ultimeter_2000,
{ "Ultimeter 2000", "aprs.ultimeter_2000",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_status,
{ "Status", "aprs.status",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_object,
{ "Object", "aprs.object",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_item,
{ "Item", "aprs.item",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_query,
{ "Query", "aprs.query",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_telemetry,
{ "Telemetry", "aprs.telemetry",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_raw,
{ "Raw", "aprs.raw",
FT_STRING, BASE_NONE, NULL, 0x0,
"Raw NMEA position report format", HFILL }
},
{ &hf_aprs_station,
{ "Station", "aprs.station",
FT_STRING, BASE_NONE, NULL, 0x0,
"Station capabilities", HFILL }
},
{ &hf_aprs_message,
{ "Message", "aprs.message",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_agrelo,
{ "Agrelo", "aprs.agrelo",
FT_BYTES, BASE_NONE, NULL, 0x0,
"Agrelo DFJr / MicroFinder", HFILL }
},
{ &hf_aprs_maidenhead,
{ "Maidenhead", "aprs.maidenhead",
FT_BYTES, BASE_NONE, NULL, 0x0,
"Maidenhead grid locator beacon (obsolete)", HFILL }
},
{ &hf_aprs_invalid_test,
{ "Invalid or test", "aprs.invalid_test",
FT_BYTES, BASE_NONE, NULL, 0x0,
"Invalid data or test data", HFILL }
},
{ &hf_aprs_user_defined,
{ "User-Defined", "aprs.user_defined",
FT_BYTES, BASE_NONE, NULL, 0x0,
"User-Defined APRS packet format", HFILL }
},
{ &hf_aprs_third_party,
{ "Third-party", "aprs.third_party",
FT_BYTES, BASE_NONE, NULL, 0x0,
"Third-party traffic", HFILL }
},
{ &hf_aprs_peet_1,
{ "Peet U-II (1)", "aprs.peet_1",
FT_BYTES, BASE_NONE, NULL, 0x0,
"Peet Bros U-II Weather Station", HFILL }
},
{ &hf_aprs_peet_2,
{ "Peet U-II (2)", "aprs.peet_2",
FT_BYTES, BASE_NONE, NULL, 0x0,
"Peet Bros U-II Weather Station", HFILL }
},
{ &hf_aprs_map_feature,
{ "Map Feature", "aprs.map_feature",
FT_BYTES, BASE_NONE, NULL, 0x0,
"[Reserved - Map Feature", HFILL }
},
{ &hf_aprs_shelter_data,
{ "Shelter data", "aprs.shelter_data",
FT_BYTES, BASE_NONE, NULL, 0x0,
"[Reserved - Shelter data with time]", HFILL }
},
{ &hf_aprs_space_weather,
{ "Space weather", "aprs.space_weather",
FT_BYTES, BASE_NONE, NULL, 0x0,
"[Reserved - Space weather]", HFILL }
},
{ &hf_aprs_storm,
{ "Storm", "aprs.storm",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Time stamp */
{ &hf_aprs_dhm,
{ "Day/Hour/Minute", "aprs.dhm",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_hms,
{ "Hour/Minute/Second", "aprs.hms",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mdhm,
{ "Month/Day/Hour/Minute", "aprs.mdhm",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_tz,
{ "Time Zone", "aprs.tz",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Compressed Msg */
{ &hf_aprs_compression_type,
{ "Compression type", "aprs.ct",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_ct_gps_fix,
{ "GPS fix type", "aprs.ct.gps_fix",
FT_UINT8, BASE_HEX, VALS(gps_vals), 0x20,
NULL, HFILL }
},
{ &hf_aprs_ct_nmea_src,
{ "NMEA source", "aprs.ct.nmea_src",
FT_UINT8, BASE_HEX, VALS(nmea_vals), 0x18,
NULL, HFILL }
},
{ &hf_aprs_ct_origin,
{ "Compression origin", "aprs.ct.origin",
FT_UINT8, BASE_HEX, VALS(ctype_vals), 0x07,
NULL, HFILL }
},
/* Ext Msg */
{ &hf_aprs_msg,
{ "Extended message", "aprs.msg",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_rng,
{ "Range", "aprs.msg.rng",
FT_STRING, BASE_NONE, NULL, 0x0,
"Pre-calculated radio range", HFILL }
},
{ &hf_aprs_msg_cse,
{ "Course", "aprs.msg.cse",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_spd,
{ "Speed", "aprs.msg.spd",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_dir,
{ "Wind direction", "aprs.msg.dir",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_brg,
{ "Bearing", "aprs.msg.brg",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_nrq,
{ "Number/Range/Quality", "aprs.msg.nrq",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Msg PHGD */
{ &hf_aprs_msg_phg_p,
{ "Power", "aprs.msg.phg.p",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_phg_h,
{ "Height", "aprs.msg.phg.h",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_phg_g,
{ "Gain", "aprs.msg.phg.g",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_phg_d,
{ "Directivity", "aprs.msg.phg.d",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Msg DFS */
{ &hf_aprs_msg_dfs_s,
{ "Strength", "aprs.msg.dfs.s",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_dfs_h,
{ "Height", "aprs.msg.dfs.h",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_dfs_g,
{ "Gain", "aprs.msg.dfs.g",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_dfs_d,
{ "Directivity", "aprs.msg.dfs.d",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Msg AOD */
{ &hf_aprs_msg_aod_t,
{ "Type", "aprs.msg.aod.t",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_msg_aod_c,
{ "Colour", "aprs.msg.aod.c",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Weather */
{ &hf_aprs_weather,
{ "Weather report", "aprs.weather",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_dir,
{ "Wind direction", "aprs.weather.dir",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_spd,
{ "Wind speed", "aprs.weather.speed",
FT_STRING, BASE_NONE, NULL, 0x0,
"Wind speed (1 minute)", HFILL }
},
{ &hf_aprs_weather_peak,
{ "Peak wind speed", "aprs.weather.peak",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_temp,
{ "Temperature (F)", "aprs.weather.temp",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_rain_1,
{ "Rain (last 1 hour)", "aprs.weather.1_hour",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_rain_24,
{ "Rain (last 24 hours)", "aprs.weather.24_hour",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_rain,
{ "Rain", "aprs.weather.rain",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_humidty,
{ "Humidity", "aprs.weather.humidity",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_press,
{ "Pressure", "aprs.weather.pressure",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_luminosity,
{ "Luminosity", "aprs.weather.luminosity",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_snow,
{ "Snow", "aprs.weather.snow",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_raw_rain,
{ "Raw rain", "aprs.weather.raw_rain",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_software,
{ "Software", "aprs.weather.software",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_weather_unit,
{ "Unit", "aprs.weather.unit",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* MIC-E */
{ &hf_aprs_mic_e_0_current,
{ "Current Mic-E (Rev 0)", "aprs.mic_e_0_current",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_0_old,
{ "Old Mic-E (Rev 0)", "aprs.mic_e_0_old",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_old,
{ "Old Mic-E", "aprs.mic_e_old",
FT_STRING, BASE_NONE, NULL, 0x0,
"Old Mic-E Data (but Current data for TM-D700)", HFILL }
},
{ &hf_aprs_mic_e_current,
{ "Current Mic-E", "aprs.mic_e_current",
FT_STRING, BASE_NONE, NULL, 0x0,
"Current Mic-E Data (not used in TM-D700)", HFILL }
},
{ &hf_aprs_mic_e_dst,
{ "Destination Address", "aprs.mic_e.dst",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_long_d,
{ "Longitude degrees", "aprs.mic_e.long_d",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_long_m ,
{ "Longitude minutes", "aprs.mic_e.long_m",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_long_h,
{ "Longitude hundredths of minutes", "aprs.mic_e.long_h",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_spd_sp,
{ "Speed (hundreds & tens)", "aprs.mic_e.speed_sp",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_spd_dc,
{ "Speed (tens), Course (hundreds)", "aprs.mic_e.speed_dc",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_spd_se,
{ "Course (tens & units)", "aprs.mic_e.speed_se",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_telemetry,
{ "Telemetry", "aprs.mic_e.telemetry",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_mic_e_status,
{ "Status", "aprs.mic_e.status",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_dir,
{ "Direction", "aprs.storm.direction",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_spd,
{ "Speed (knots)", "aprs.storm.speed",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_type,
{ "Type", "aprs.storm.type",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_sws,
{ "Sustained wind speed (knots)", "aprs.storm.sws",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_pwg,
{ "Peak wind gusts (knots)", "aprs.storm.pwg",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_cp,
{ "Central pressure (millibars/hPascal)", "aprs.storm.central_pressure",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_rhw,
{ "Radius Hurricane Winds (nautical miles)", "aprs.storm.radius_hurricane_winds",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_rtsw,
{ "Radius Tropical Storm Winds (nautical miles)", "aprs.storm.radius_tropical_storms_winds",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_aprs_storm_rwg,
{ "Radius Whole Gale (nautical miles)", "aprs.storm.radius_whole_gale",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
}
};
static gint *ett[] = {
&ett_aprs,
&ett_aprs_msg,
&ett_aprs_ct,
&ett_aprs_weather,
&ett_aprs_storm,
&ett_aprs_mic_e,
};
proto_aprs = proto_register_protocol("Automatic Position Reporting System", "APRS", "aprs");
register_dissector( "aprs", dissect_aprs, proto_aprs);
proto_register_field_array( proto_aprs, hf, array_length(hf ) );
proto_register_subtree_array( ett, array_length( ett ) );
aprs_module = prefs_register_protocol( proto_aprs, NULL);
prefs_register_bool_preference(aprs_module, "showaprslax",
"Allow APRS violations.",
"Attempt to display common APRS protocol violations correctly",
&gPREF_APRS_LAX );
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: t
* End:
*
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
* :indentSize=8:tabSize=8:noTabs=false:
*/