1 /* packet-aprs.c
2  * Routines for Amateur Packet Radio protocol dissection
3  * Copyright 2007,2008,2009,2010,2012 R.W. Stearn <richard@rns-stearn.demon.co.uk>
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11 
12 /*
13  * This dissector is for APRS (Automatic Packet Reporting System)
14  *
15  * Information was drawn from:
16  *    http://www.aprs.org/
17  *    Specification: http://www.aprs.org/doc/APRS101.PDF
18  *
19  * Inspiration on how to build the dissector drawn from
20  *   packet-sdlc.c
21  *   packet-x25.c
22  *   packet-lapb.c
23  *   paket-gprs-llc.c
24  *   xdlc.c
25  * with the base file built from README.developers.
26  */
27 
28 #include "config.h"
29 
30 #include <math.h>
31 
32 #include <epan/packet.h>
33 #include <epan/prefs.h>
34 #include <epan/to_str.h>
35 
36 #define STRLEN	100
37 
38 void proto_register_aprs(void);
39 
40 static int proto_aprs			= -1;
41 
42 /* aprs timestamp items */
43 static int hf_aprs_dhm			= -1;
44 static int hf_aprs_hms			= -1;
45 static int hf_aprs_mdhm			= -1;
46 static int hf_aprs_tz			= -1;
47 
48 /* aprs position items */
49 /* static int hf_aprs_position		= -1; */
50 static int hf_aprs_lat			= -1;
51 static int hf_aprs_long			= -1;
52 
53 /* aprs msg items */
54 static int hf_aprs_msg			= -1;
55 static int hf_aprs_msg_rng		= -1;
56 static int hf_aprs_msg_cse		= -1;
57 static int hf_aprs_msg_spd		= -1;
58 static int hf_aprs_msg_dir		= -1;
59 static int hf_aprs_msg_brg		= -1;
60 static int hf_aprs_msg_nrq		= -1;
61 
62 /* aprs compression type items */
63 static int hf_aprs_compression_type	= -1;
64 static int hf_aprs_ct_gps_fix		= -1;
65 static int hf_aprs_ct_nmea_src		= -1;
66 static int hf_aprs_ct_origin		= -1;
67 
68 /* phg msg items */
69 static int hf_aprs_msg_phg_p		= -1;
70 static int hf_aprs_msg_phg_h		= -1;
71 static int hf_aprs_msg_phg_g		= -1;
72 static int hf_aprs_msg_phg_d		= -1;
73 
74 /* dfs msg items */
75 static int hf_aprs_msg_dfs_s		= -1;
76 static int hf_aprs_msg_dfs_h		= -1;
77 static int hf_aprs_msg_dfs_g		= -1;
78 static int hf_aprs_msg_dfs_d		= -1;
79 
80 /* weather items */
81 static int hf_aprs_weather_dir		= -1;
82 static int hf_aprs_weather_spd		= -1;
83 static int hf_aprs_weather_peak		= -1;
84 static int hf_aprs_weather_temp		= -1;
85 static int hf_aprs_weather_rain_1	= -1;
86 static int hf_aprs_weather_rain_24	= -1;
87 static int hf_aprs_weather_rain		= -1;
88 static int hf_aprs_weather_humidty	= -1;
89 static int hf_aprs_weather_press	= -1;
90 static int hf_aprs_weather_luminosity	= -1;
91 static int hf_aprs_weather_snow		= -1;
92 static int hf_aprs_weather_raw_rain	= -1;
93 static int hf_aprs_weather_software	= -1;
94 static int hf_aprs_weather_unit		= -1;
95 
96 /* aod msg items */
97 static int hf_aprs_msg_aod_t		= -1;
98 static int hf_aprs_msg_aod_c		= -1;
99 
100 /* mic-e msg items */
101 static int hf_aprs_mic_e_dst		= -1;
102 static int hf_aprs_mic_e_long_d		= -1;
103 static int hf_aprs_mic_e_long_m		= -1;
104 static int hf_aprs_mic_e_long_h		= -1;
105 static int hf_aprs_mic_e_spd_sp		= -1;
106 static int hf_aprs_mic_e_spd_dc		= -1;
107 static int hf_aprs_mic_e_spd_se		= -1;
108 static int hf_aprs_mic_e_telemetry	= -1;
109 static int hf_aprs_mic_e_status		= -1;
110 
111 /* Storm items */
112 static int hf_aprs_storm_dir		= -1;
113 static int hf_aprs_storm_spd		= -1;
114 static int hf_aprs_storm_type		= -1;
115 static int hf_aprs_storm_sws		= -1;
116 static int hf_aprs_storm_pwg		= -1;
117 static int hf_aprs_storm_cp		= -1;
118 static int hf_aprs_storm_rhw		= -1;
119 static int hf_aprs_storm_rtsw		= -1;
120 static int hf_aprs_storm_rwg		= -1;
121 
122 /* aprs sundry items */
123 static int hf_aprs_dti			= -1;
124 static int hf_aprs_sym_id		= -1;
125 static int hf_aprs_sym_code		= -1;
126 static int hf_aprs_comment		= -1;
127 static int hf_aprs_storm		= -1;
128 
129 /* aprs main catgories items */
130 static int hf_ultimeter_2000		= -1;
131 static int hf_aprs_status		= -1;
132 static int hf_aprs_object		= -1;
133 static int hf_aprs_item			= -1;
134 static int hf_aprs_query		= -1;
135 static int hf_aprs_telemetry		= -1;
136 static int hf_aprs_raw			= -1;
137 static int hf_aprs_station		= -1;
138 static int hf_aprs_message		= -1;
139 static int hf_aprs_agrelo		= -1;
140 static int hf_aprs_maidenhead		= -1;
141 static int hf_aprs_weather		= -1;
142 static int hf_aprs_invalid_test		= -1;
143 static int hf_aprs_user_defined		= -1;
144 static int hf_aprs_third_party		= -1;
145 static int hf_aprs_mic_e_0_current	= -1;
146 static int hf_aprs_mic_e_0_old		= -1;
147 static int hf_aprs_mic_e_old		= -1;
148 static int hf_aprs_mic_e_current	= -1;
149 static int hf_aprs_peet_1		= -1;
150 static int hf_aprs_peet_2		= -1;
151 static int hf_aprs_map_feature		= -1;
152 static int hf_aprs_shelter_data		= -1;
153 static int hf_aprs_space_weather	= -1;
154 
155 
156 static gboolean gPREF_APRS_LAX = FALSE;
157 
158 static gint ett_aprs		= -1;
159 static gint ett_aprs_msg	= -1;
160 static gint ett_aprs_ct		= -1;
161 static gint ett_aprs_weather	= -1;
162 static gint ett_aprs_storm	= -1;
163 static gint ett_aprs_mic_e	= -1;
164 
165 
166 static const value_string ctype_vals[] = {
167 	{ 0,   "Compressed" },
168 	{ 1,   "TNC BText" },
169 	{ 2,   "Software (DOS/Mac/Win/+SA)" },
170 	{ 3,   "[tbd]" },
171 	{ 4,   "KPC3" },
172 	{ 5,   "Pico" },
173 	{ 6,   "Other tracker [tbd]" },
174 	{ 7,   "Digipeater conversion" },
175 	{ 0,            NULL }
176 };
177 
178 static const value_string nmea_vals[] = {
179 	{ 0,   "other" },
180 	{ 1,   "GLL" },
181 	{ 2,   "GGA" },
182 	{ 3,   "RMC" },
183 	{ 0,            NULL }
184 };
185 
186 static const value_string gps_vals[] = {
187 	{ 0,   "old (last)" },
188 	{ 1,   "current" },
189 	{ 0,            NULL }
190 };
191 
192 /* sorted */
193 static const value_string aprs_description[] = {
194 	{ 0x1c, "Current Mic-E Data (Rev 0 beta)" },
195 	{ 0x1d, "Old Mic-E Data (Rev 0 beta)" },
196 	{ '#',  "Peet Bros U-II Weather Station" },
197 	{ '$',  "Raw GPS data or Ultimeter 2000" },
198 	{ '%',  "Agrelo DFJr / MicroFinder" },
199 	{ '&',  "[Reserved - Map Feature]" },
200 	{ '\'', "Old Mic-E Data (current data for TM-D700)" },
201 	{ ')',  "Item" },
202 	{ '*',  "Peet Bros U-II Weather Station" },
203 	{ '+',  "[Reserved - Shelter data with time]" },
204 	{ ',',  "Invalid data or test data" },
205 	{ '.',  "[Reserved - Space weather]" },
206 	{ '/',  "Position + timestamp" },
207 	{ ':',  "Message" },
208 	{ ';',  "Object" },
209 	{ '<',  "Station Capabilities" },
210 	{ '=',  "Position + APRS data extension" },
211 	{ '>',  "Status" },
212 	{ '?',  "Query" },
213 	{ '@',  "Position + timestamp + APRS data extension" },
214 	{ 'T',  "Telemetry data" },
215 	{ '[',  "Maidenhead grid locator beacon (obsolete)" },
216 	{ '_',  "Weather Report (without position)" },
217 	{ '`',  "Current Mic-E Data (not used in TM-D700)" },
218 	{ '{',  "User-Defined APRS packet format" },
219 	{ '}',  "Third-party traffic" },
220 	{ 0,            NULL }
221 };
222 static value_string_ext aprs_description_ext = VALUE_STRING_EXT_INIT(aprs_description);
223 
224 /* MIC-E destination field code table */
225 typedef struct
226 	{
227 	guint8 key;
228 	char   digit;
229 	int    msg;
230 	char   n_s;
231 	int    long_offset;
232 	char   w_e;
233 	} mic_e_dst_code_table_s;
234 
235 static const mic_e_dst_code_table_s dst_code[] =
236 	{
237 	{ '0' << 1, '0', 0, 'S',   0, 'E' },
238 	{ '1' << 1, '1', 0, 'S',   0, 'E' },
239 	{ '2' << 1, '2', 0, 'S',   0, 'E' },
240 	{ '3' << 1, '3', 0, 'S',   0, 'E' },
241 	{ '4' << 1, '4', 0, 'S',   0, 'E' },
242 	{ '5' << 1, '5', 0, 'S',   0, 'E' },
243 	{ '6' << 1, '6', 0, 'S',   0, 'E' },
244 	{ '7' << 1, '7', 0, 'S',   0, 'E' },
245 	{ '8' << 1, '8', 0, 'S',   0, 'E' },
246 	{ '9' << 1, '9', 0, 'S',   0, 'E' },
247 	{ 'A' << 1, '0', 1, '?',   0, '?' },
248 	{ 'B' << 1, '1', 1, '?',   0, '?' },
249 	{ 'C' << 1, '2', 1, '?',   0, '?' },
250 	{ 'D' << 1, '3', 1, '?',   0, '?' },
251 	{ 'E' << 1, '4', 1, '?',   0, '?' },
252 	{ 'F' << 1, '5', 1, '?',   0, '?' },
253 	{ 'G' << 1, '6', 1, '?',   0, '?' },
254 	{ 'H' << 1, '7', 1, '?',   0, '?' },
255 	{ 'I' << 1, '8', 1, '?',   0, '?' },
256 	{ 'J' << 1, '9', 1, '?',   0, '?' },
257 	{ 'K' << 1, ' ', 1, '?',   0, '?' },
258 	{ 'L' << 1, ' ', 0, 'S',   0, 'E' },
259 	{ 'P' << 1, '0', 1, 'N', 100, 'W' },
260 	{ 'Q' << 1, '1', 1, 'N', 100, 'W' },
261 	{ 'R' << 1, '2', 1, 'N', 100, 'W' },
262 	{ 'S' << 1, '3', 1, 'N', 100, 'W' },
263 	{ 'T' << 1, '4', 1, 'N', 100, 'W' },
264 	{ 'U' << 1, '5', 1, 'N', 100, 'W' },
265 	{ 'V' << 1, '6', 1, 'N', 100, 'W' },
266 	{ 'W' << 1, '7', 1, 'N', 100, 'W' },
267 	{ 'X' << 1, '8', 1, 'N', 100, 'W' },
268 	{ 'Y' << 1, '9', 1, 'N', 100, 'W' },
269 	{ 'Z' << 1, ' ', 1, 'N', 100, 'W' },
270 	{        0, '_', 3, '?',   3, '?' },
271 	};
272 
273 
274 /* MIC-E message table */
275 typedef struct
276 	{
277 	const char *std;
278 	const char *custom;
279 	} mic_e_msg_table_s;
280 
281 static const mic_e_msg_table_s mic_e_msg_table[] =
282 	{
283 	{ "Emergency",  "Emergency" },
284 	{ "Priority",   "Custom 6" },
285 	{ "Special",    "Custom 5" },
286 	{ "Committed",  "Custom 4" },
287 	{ "Returning",  "Custom 3" },
288 	{ "In Service", "Custom 2" },
289 	{ "En Route",   "Custom 1" },
290 	{ "Off Duty",   "Custom 0" }
291 	};
292 
293 /* Code to actually dissect the packets */
294 
295 static int
dissect_aprs_compression_type(tvbuff_t * tvb,int offset,proto_tree * parent_tree)296 dissect_aprs_compression_type(	tvbuff_t	 *tvb,
297 				int		  offset,
298 				proto_tree	 *parent_tree
299 				)
300 {
301 	proto_tree *tc;
302 	proto_tree *compression_tree;
303 	int	    new_offset;
304 	int	    data_len;
305 	guint8	    compression_type;
306 
307 
308 	data_len = 1;
309 	new_offset = offset + data_len;
310 
311 	if ( parent_tree )
312 		{
313 		compression_type = tvb_get_guint8( tvb, offset ) - 33;
314 
315 		tc = proto_tree_add_uint( parent_tree, hf_aprs_compression_type, tvb, offset, data_len,
316 					  compression_type );
317 		compression_tree = proto_item_add_subtree( tc, ett_aprs_ct );
318 
319 		proto_tree_add_item( compression_tree, hf_aprs_ct_gps_fix,  tvb, offset, data_len, ENC_BIG_ENDIAN );
320 		proto_tree_add_item( compression_tree, hf_aprs_ct_nmea_src, tvb, offset, data_len, ENC_BIG_ENDIAN );
321 		proto_tree_add_item( compression_tree, hf_aprs_ct_origin,   tvb, offset, data_len, ENC_BIG_ENDIAN );
322 		}
323 
324 	return new_offset;
325 }
326 
327 static int
dissect_aprs_msg(tvbuff_t * tvb,int offset,proto_tree * parent_tree,int wind,int brg_nrq)328 dissect_aprs_msg(	tvbuff_t	  *tvb,
329 			int		   offset,
330 			proto_tree	  *parent_tree,
331 			int		   wind,
332 			int 		   brg_nrq
333 			)
334 {
335 	proto_tree *msg_tree = NULL;
336 	guint8	    ch;
337 
338 
339 	if ( parent_tree )
340 		{
341 		proto_tree *tc;
342 		tc = proto_tree_add_item( parent_tree, hf_aprs_msg, tvb, offset, 7, ENC_ASCII|ENC_NA );
343 		msg_tree = proto_item_add_subtree( tc, ett_aprs_msg );
344 		}
345 
346 	ch = tvb_get_guint8( tvb, offset );
347 
348 	if ( g_ascii_isdigit( ch ) )
349 		{
350 		if ( wind )
351 			proto_tree_add_item( msg_tree, hf_aprs_msg_dir, tvb, offset, 3, ENC_ASCII|ENC_NA );
352 		else
353 			proto_tree_add_item( msg_tree, hf_aprs_msg_cse, tvb, offset, 3, ENC_ASCII|ENC_NA );
354 		offset += 3;
355 		/* verify the separator */
356 		offset += 1;
357 		proto_tree_add_item( msg_tree, hf_aprs_msg_spd, tvb, offset, 3, ENC_ASCII|ENC_NA );
358 		offset += 3;
359 		}
360 	else
361 		{
362 		switch ( ch )
363 			{
364 			case 'D' :	/* dfs */
365 				offset += 3;
366 				proto_tree_add_item( msg_tree, hf_aprs_msg_dfs_s, tvb, offset, 1, ENC_ASCII|ENC_NA );
367 				offset += 1;
368 				proto_tree_add_item( msg_tree, hf_aprs_msg_dfs_h, tvb, offset, 1, ENC_ASCII|ENC_NA );
369 				offset += 1;
370 				proto_tree_add_item( msg_tree, hf_aprs_msg_dfs_g, tvb, offset, 1, ENC_ASCII|ENC_NA );
371 				offset += 1;
372 				proto_tree_add_item( msg_tree, hf_aprs_msg_dfs_d, tvb, offset, 1, ENC_ASCII|ENC_NA );
373 				break;
374 			case 'P' :	/* phgd */
375 				offset += 3;
376 				proto_tree_add_item( msg_tree, hf_aprs_msg_phg_p, tvb, offset, 1, ENC_ASCII|ENC_NA );
377 				offset += 1;
378 				proto_tree_add_item( msg_tree, hf_aprs_msg_phg_h, tvb, offset, 1, ENC_ASCII|ENC_NA );
379 				offset += 1;
380 				proto_tree_add_item( msg_tree, hf_aprs_msg_phg_g, tvb, offset, 1, ENC_ASCII|ENC_NA );
381 				offset += 1;
382 				proto_tree_add_item( msg_tree, hf_aprs_msg_phg_d, tvb, offset, 1, ENC_ASCII|ENC_NA );
383 				break;
384 			case 'R' :	/* rng */
385 				proto_tree_add_item( msg_tree, hf_aprs_msg_rng, tvb, offset, 7, ENC_ASCII|ENC_NA );
386 				break;
387 			case 'T' :	/* aod */
388 				offset += 1;
389 				proto_tree_add_item( msg_tree, hf_aprs_msg_aod_t, tvb, offset, 2, ENC_ASCII|ENC_NA );
390 				offset += 2;
391 				/* step over the /C */
392 				offset += 2;
393 				proto_tree_add_item( msg_tree, hf_aprs_msg_aod_c, tvb, offset, 2, ENC_ASCII|ENC_NA );
394 				break;
395 			default  :	/* wtf */
396 				break;
397 			}
398 		}
399 	if ( brg_nrq )
400 		{
401 		proto_tree_add_item( msg_tree, hf_aprs_msg_brg, tvb, offset, 3, ENC_ASCII|ENC_NA );
402 		offset += 3;
403 		/* verify the separator */
404 		offset += 1;
405 		proto_tree_add_item( msg_tree, hf_aprs_msg_nrq, tvb, offset, 3, ENC_ASCII|ENC_NA );
406 		offset += 3;
407 		}
408 
409 	return offset;
410 }
411 
412 static int
dissect_aprs_compressed_msg(tvbuff_t * tvb,int offset,proto_tree * parent_tree)413 dissect_aprs_compressed_msg(	tvbuff_t *tvb,
414 				int offset,
415 				proto_tree *parent_tree
416 				)
417 {
418 	proto_tree *tc;
419 	proto_tree *msg_tree;
420 	int	    new_offset;
421 	int	    data_len;
422 	guint8	    ch;
423 	guint8	    course;
424 	double	    speed;
425 	double	    range;
426 	gchar	   *info_buffer;
427 
428 
429 	data_len = 2;
430 	new_offset = offset + data_len;
431 
432 	if ( parent_tree )
433 		{
434 		tc = proto_tree_add_item( parent_tree, hf_aprs_msg, tvb, offset, data_len, ENC_ASCII|ENC_NA );
435 		msg_tree = proto_item_add_subtree( tc, ett_aprs_msg );
436 
437 		ch = tvb_get_guint8( tvb, offset );
438 		if ( ch != ' ' )
439 			{
440 			if ( ch == '{' )
441 				{ /* Pre-Calculated Radio Range */
442 				offset += 1;
443 				ch = tvb_get_guint8( tvb, offset );
444 				range = exp( log( 1.08 ) * (ch - 33) );
445 				info_buffer = wmem_strdup_printf( wmem_packet_scope(), "%7.2f", range );
446 				proto_tree_add_string( msg_tree, hf_aprs_msg_rng, tvb, offset, 1, info_buffer );
447 				}
448 			else
449 				if ( ch >= '!' && ch <= 'z' )
450 					{ /* Course/Speed */
451 					course = (ch - 33) * 4;
452 					info_buffer = wmem_strdup_printf( wmem_packet_scope(), "%d", course );
453 					proto_tree_add_string( msg_tree, hf_aprs_msg_cse,
454 							       tvb, offset, 1, info_buffer );
455 					offset += 1;
456 					ch = tvb_get_guint8( tvb, offset );
457 					speed = exp( log( 1.08 ) * (ch - 33) );
458 					info_buffer = wmem_strdup_printf( wmem_packet_scope(), "%7.2f", speed );
459 					proto_tree_add_string( msg_tree, hf_aprs_msg_spd,
460 							       tvb, offset, 1, info_buffer );
461 					}
462 
463 			}
464 		}
465 
466 	return new_offset;
467 }
468 
469 
470 static const mic_e_dst_code_table_s *
dst_code_lookup(guint8 ch)471 dst_code_lookup( guint8 ch )
472 {
473 	guint indx;
474 
475 	indx = 0;
476 	while ( indx < ( sizeof( dst_code ) / sizeof( mic_e_dst_code_table_s ) )
477 			&& dst_code[ indx ].key != ch
478 			&& dst_code[ indx ].key > 0 )
479 		indx++;
480 	return &( dst_code[ indx ] );
481 }
482 
483 static int
d28_to_deg(guint8 code,int long_offset)484 d28_to_deg( guint8 code, int long_offset )
485 {
486 	int value;
487 
488 	value = code - 28 + long_offset;
489 	if ( value >= 180 && value <= 189 )
490 		value -= 80;
491 	else
492 		if ( value >= 190 && value <= 199 )
493 			value -= 190;
494 	return value;
495 }
496 
497 static int
d28_to_min(guint8 code)498 d28_to_min( guint8 code )
499 {
500 	int value;
501 
502 	value = code - 28;
503 	if ( value >= 60 )
504 		value -= 60;
505 	return value;
506 }
507 
508 static int
dissect_mic_e(tvbuff_t * tvb,int offset,packet_info * pinfo,proto_tree * parent_tree,int hf_mic_e_idx)509 dissect_mic_e(	tvbuff_t    *tvb,
510 		int	     offset,
511 		packet_info *pinfo,
512 		proto_tree  *parent_tree,
513 		int	     hf_mic_e_idx
514 		)
515 {
516 	proto_tree *tc;
517 	proto_tree *mic_e_tree;
518 	int	    new_offset;
519 	int	    data_len;
520 	char    *info_buffer;
521 	char    latitude[7] = { '?', '?', '?', '?', '.', '?', '?' };
522 	int	    msg_a;
523 	int	    msg_b;
524 	int	    msg_c;
525 	char    n_s;
526 	int	    long_offset;
527 	char    w_e;
528 	int	    cse;
529 	int	    spd;
530 	guint8  ssid;
531 	const guint8 *addr;
532 	const mic_e_dst_code_table_s *dst_code_entry;
533 
534 	data_len    = tvb_reported_length_remaining( tvb, offset );
535 	new_offset  = offset + data_len;
536 
537 	info_buffer = (char *)wmem_alloc( wmem_packet_scope(), STRLEN );
538 
539 	msg_a = 0;
540 	msg_b = 0;
541 	msg_c = 0;
542 
543 	n_s = '?';
544 	long_offset = 0;
545 	w_e = '?';
546 	ssid = 0;
547 
548 	if ( pinfo->dst.type == AT_AX25 && pinfo->dst.len == AX25_ADDR_LEN )
549 		{
550 		/* decode the AX.25 destination address */
551 		addr = (const guint8 *)pinfo->dst.data;
552 
553 		dst_code_entry = dst_code_lookup( addr[ 0 ] );
554 		latitude[ 0 ] = dst_code_entry->digit;
555 		msg_a = dst_code_entry->msg & 0x1;
556 
557 		dst_code_entry = dst_code_lookup( addr[ 1 ] );
558 		latitude[ 1 ] = dst_code_entry->digit;
559 		msg_b = dst_code_entry->msg & 0x1;
560 
561 		dst_code_entry = dst_code_lookup( addr[ 2 ] );
562 		latitude[ 2 ] = dst_code_entry->digit;
563 		msg_c = dst_code_entry->msg & 0x1;
564 
565 		dst_code_entry = dst_code_lookup( addr[ 3 ] );
566 		latitude[ 3 ] = dst_code_entry->digit;
567 		n_s = dst_code_entry->n_s;
568 
569 		/* '.' already set */
570 
571 		dst_code_entry = dst_code_lookup( addr[ 4 ] );
572 		latitude[ 5 ] = dst_code_entry->digit;
573 		long_offset = dst_code_entry->long_offset;
574 
575 		dst_code_entry = dst_code_lookup( addr[ 5 ] );
576 		latitude[ 6 ] = dst_code_entry->digit;
577 		w_e = dst_code_entry->w_e;
578 
579 		ssid = (addr[ 6 ] >> 1) & 0x0f;
580 		}
581 
582 	/* decode the mic-e info fields */
583 	spd = ((tvb_get_guint8( tvb, offset + 3 ) - 28) * 10) + ((tvb_get_guint8( tvb, offset + 4 ) - 28) / 10);
584 	if ( spd >= 800 )
585 		spd -= 800;
586 
587 	cse = (((tvb_get_guint8( tvb, offset + 4 ) - 28) % 10) * 100) + ((tvb_get_guint8( tvb, offset + 5 ) - 28) * 10);
588 	if ( cse >= 400 )
589 		cse -= 400;
590 
591 	g_snprintf( info_buffer, STRLEN,
592 				"Lat: %7.7s%c Long: %03d%02d.%02d%c, Cse: %d, Spd: %d, SSID: %d, Msg %s",
593 				latitude,
594 				n_s,
595 				d28_to_deg( tvb_get_guint8( tvb, offset ), long_offset ),
596 				d28_to_min( tvb_get_guint8( tvb, offset + 1 ) ),
597 				tvb_get_guint8( tvb, offset + 2 ) - 28,
598 				w_e,
599 				cse,
600 				spd,
601 				ssid,
602 				mic_e_msg_table[ (msg_a << 2) + (msg_b << 1) + msg_c ].std
603 				);
604 
605 	col_set_str( pinfo->cinfo, COL_INFO, "MIC-E " );
606 	col_append_str( pinfo->cinfo, COL_INFO, info_buffer );
607 
608 	if ( parent_tree )
609 		{
610 		tc = proto_tree_add_string( parent_tree, hf_mic_e_idx, tvb, offset, data_len, info_buffer );
611 		mic_e_tree = proto_item_add_subtree( tc, ett_aprs_mic_e );
612 
613 		g_snprintf( info_buffer, STRLEN,
614 				"Lat %7.7s, Msg A %d, Msg B %d, Msg C %d, N/S %c, Long off %3d, W/E %c, SSID %d",
615 				latitude,
616 				msg_a,
617 				msg_b,
618 				msg_c,
619 				n_s,
620 				long_offset,
621 				w_e,
622 				ssid
623 				);
624 
625 		proto_tree_add_string( mic_e_tree, hf_aprs_mic_e_dst,     tvb,      0, 0, info_buffer ); /* ?? */
626 
627 		proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_long_d,    tvb, offset, 1, ENC_BIG_ENDIAN );
628 		offset += 1;
629 
630 		proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_long_m,    tvb, offset, 1, ENC_BIG_ENDIAN );
631 		offset += 1;
632 
633 		proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_long_h,    tvb, offset, 1, ENC_BIG_ENDIAN );
634 		offset += 1;
635 
636 		proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_spd_sp,    tvb, offset, 1, ENC_BIG_ENDIAN );
637 		offset += 1;
638 
639 		proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_spd_dc,    tvb, offset, 1, ENC_BIG_ENDIAN );
640 		offset += 1;
641 
642 		proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_spd_se,    tvb, offset, 1, ENC_BIG_ENDIAN );
643 		offset += 1;
644 
645 		proto_tree_add_item( mic_e_tree, hf_aprs_sym_code,  tvb, offset, 1, ENC_ASCII|ENC_NA );
646 		offset += 1;
647 
648 		proto_tree_add_item( mic_e_tree, hf_aprs_sym_id,    tvb, offset, 1, ENC_ASCII|ENC_NA );
649 		offset += 1;
650 
651 		if ( offset < new_offset )
652 			{
653 			guint8 c = tvb_get_guint8(tvb, offset);
654 			if ( (c == ',') || (c == 0x1d) )
655 				proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_telemetry,
656 						     tvb, offset, -1, ENC_NA );
657 			else
658 				proto_tree_add_item( mic_e_tree, hf_aprs_mic_e_status,
659 						     tvb, offset, -1, ENC_ASCII|ENC_NA );
660 			}
661 
662 		}
663 
664 	return new_offset;
665 }
666 
667 static int
dissect_aprs_storm(tvbuff_t * tvb,int offset,proto_tree * parent_tree)668 dissect_aprs_storm(	tvbuff_t   *tvb,
669 			int	    offset,
670 			proto_tree *parent_tree
671 			)
672 {
673 	proto_tree  *storm_tree;
674 	proto_tree *tc;
675 
676 	tc = proto_tree_add_item( parent_tree, hf_aprs_storm, tvb, offset, -1, ENC_ASCII|ENC_NA );
677 	storm_tree = proto_item_add_subtree( tc, ett_aprs_storm );
678 
679 	proto_tree_add_item( storm_tree, hf_aprs_storm_dir,  tvb, offset, 3, ENC_ASCII|ENC_NA );
680 	offset += 3;
681 	offset += 1;
682 	proto_tree_add_item( storm_tree, hf_aprs_storm_spd,  tvb, offset, 3, ENC_ASCII|ENC_NA );
683 	offset += 3;
684 	proto_tree_add_item( storm_tree, hf_aprs_storm_type, tvb, offset, 3, ENC_ASCII|ENC_NA );
685 	offset += 3;
686 	proto_tree_add_item( storm_tree, hf_aprs_storm_sws,  tvb, offset, 4, ENC_ASCII|ENC_NA );
687 	offset += 4;
688 	proto_tree_add_item( storm_tree, hf_aprs_storm_pwg,  tvb, offset, 4, ENC_ASCII|ENC_NA );
689 	offset += 4;
690 	proto_tree_add_item( storm_tree, hf_aprs_storm_cp,   tvb, offset, 5, ENC_ASCII|ENC_NA );
691 	offset += 5;
692 	proto_tree_add_item( storm_tree, hf_aprs_storm_rhw,  tvb, offset, 4, ENC_ASCII|ENC_NA );
693 	offset += 4;
694 	proto_tree_add_item( storm_tree, hf_aprs_storm_rtsw, tvb, offset, 4, ENC_ASCII|ENC_NA );
695 	offset += 4;
696 	proto_tree_add_item( storm_tree, hf_aprs_storm_rwg,  tvb, offset, 4, ENC_ASCII|ENC_NA );
697 	offset += 4;
698 
699 	return offset;
700 }
701 
702 static int
dissect_aprs_weather(tvbuff_t * tvb,int offset,proto_tree * parent_tree)703 dissect_aprs_weather(	tvbuff_t   *tvb,
704 			int	    offset,
705 			proto_tree *parent_tree
706 			)
707 {
708 	proto_tree  *tc;
709 	proto_tree  *weather_tree;
710 	int	     new_offset;
711 	int	     data_len;
712 	guint8	     ch;
713 
714 
715 	data_len    = tvb_reported_length_remaining( tvb, offset );
716 	new_offset  = offset + data_len;
717 
718 	tc = proto_tree_add_item( parent_tree, hf_aprs_weather, tvb, offset, data_len, ENC_ASCII|ENC_NA );
719 	weather_tree = proto_item_add_subtree( tc, ett_aprs_weather );
720 
721 	ch = tvb_get_guint8( tvb, offset );
722 	if ( g_ascii_isdigit( ch ) )
723 		{
724 		proto_tree_add_item( weather_tree, hf_aprs_weather_dir, tvb, offset, 3, ENC_ASCII|ENC_NA );
725 		offset += 3;
726 		/* verify the separator */
727 		offset += 1;
728 		proto_tree_add_item( weather_tree, hf_aprs_weather_spd, tvb, offset, 3, ENC_ASCII|ENC_NA );
729 		offset += 3;
730 		}
731 
732 	if ( parent_tree )
733 		{
734 		while ( offset < new_offset )
735 			{
736 			ch = tvb_get_guint8( tvb, offset );
737 			switch ( ch )
738 				{
739 				case 'c' :
740 					proto_tree_add_item( weather_tree, hf_aprs_weather_dir,
741 						tvb, offset, 4, ENC_ASCII|ENC_NA );
742 					offset += 4;
743 					break;
744 				case 's' :
745 					proto_tree_add_item( weather_tree, hf_aprs_weather_spd,
746 						tvb, offset, 4, ENC_ASCII|ENC_NA );
747 					offset += 4;
748 					break;
749 				case 'g' :
750 					proto_tree_add_item( weather_tree, hf_aprs_weather_peak,
751 						tvb, offset, 4, ENC_ASCII|ENC_NA );
752 					offset += 4;
753 					break;
754 				case 't' :
755 					proto_tree_add_item( weather_tree, hf_aprs_weather_temp,
756 						tvb, offset, 4, ENC_ASCII|ENC_NA );
757 					offset += 4;
758 					break;
759 				case 'r' :
760 					proto_tree_add_item( weather_tree, hf_aprs_weather_rain_1,
761 						tvb, offset, 4, ENC_ASCII|ENC_NA );
762 					offset += 4;
763 					break;
764 				case 'P' :
765 					proto_tree_add_item( weather_tree, hf_aprs_weather_rain_24,
766 						tvb, offset, 4, ENC_ASCII|ENC_NA );
767 					offset += 4;
768 					break;
769 				case 'p' :
770 					proto_tree_add_item( weather_tree, hf_aprs_weather_rain,
771 						tvb, offset, 4, ENC_ASCII|ENC_NA );
772 					offset += 4;
773 					break;
774 				case 'h' :
775 					proto_tree_add_item( weather_tree, hf_aprs_weather_humidty,
776 						tvb, offset, 3, ENC_ASCII|ENC_NA );
777 					offset += 3;
778 					break;
779 				case 'b' :
780 					proto_tree_add_item( weather_tree, hf_aprs_weather_press,
781 						tvb, offset, 6, ENC_ASCII|ENC_NA );
782 					offset += 6;
783 					break;
784 				case 'l' :
785 				case 'L' :
786 					proto_tree_add_item( weather_tree, hf_aprs_weather_luminosity,
787 						tvb, offset, 4, ENC_ASCII|ENC_NA );
788 					offset += 4;
789 					break;
790 				case 'S' :
791 					proto_tree_add_item( weather_tree, hf_aprs_weather_snow,
792 						tvb, offset, 4, ENC_ASCII|ENC_NA );
793 					offset += 4;
794 					break;
795 				case '#' :
796 					proto_tree_add_item( weather_tree, hf_aprs_weather_raw_rain,
797 						tvb, offset, 4, ENC_ASCII|ENC_NA );
798 					offset += 4;
799 					break;
800 				default  : {
801 					gint lr;
802 					/* optional: software type/unit: see if present */
803 					lr = new_offset - offset;
804 #if 0 /* fcn'al change: defer */
805 					/*
806 					 * XXX - ASCII or UTF-8?
807 					 * See http://www.aprs.org/aprs12/utf-8.txt
808 					 */
809 					if ( ((lr < 3) || (lr > 5)) ||
810 						( lr != strspn( tvb_get_string_enc( wmem_packet_scope(), tvb, offset, lr, ENC_ASCII|ENC_NA ), "a-zA-Z0-9-_" ) ) )
811 						{
812 						new_offset = offset;  /* Assume rest is a comment: force exit from while */
813 						break;  /* from switch */
814 						}
815 #endif
816 					proto_tree_add_item( weather_tree, hf_aprs_weather_software,
817 						tvb, offset, 1, ENC_ASCII|ENC_NA );
818 					offset += 1;
819 					proto_tree_add_item( weather_tree, hf_aprs_weather_unit,
820 						tvb, offset, lr-1, ENC_ASCII|ENC_NA );
821 					offset = new_offset;
822 					break;
823 					}
824 				} /* switch */
825 			} /* while */
826 		} /* if (parent_tree) */
827 	return new_offset;
828 }
829 
830 static int
aprs_timestamp(proto_tree * aprs_tree,tvbuff_t * tvb,int offset)831 aprs_timestamp( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
832 {
833 	int	data_len;
834 	const char *tzone;
835 	guint8  ch;
836 
837 	data_len = 8;
838 	tzone = "zulu";
839 
840 	ch= tvb_get_guint8( tvb, offset + 6 );
841 	if ( g_ascii_isdigit( ch ) )
842 		{ /* MDHM */
843 		proto_tree_add_item( aprs_tree, hf_aprs_mdhm, tvb, offset, data_len, ENC_ASCII|ENC_NA );
844 		proto_tree_add_string( aprs_tree, hf_aprs_tz, tvb, offset, data_len, tzone );
845 		}
846 	else
847 		{
848 		data_len -= 1;
849 		if ( ch  == 'h' )
850 			{ /* HMS */
851 			proto_tree_add_item( aprs_tree, hf_aprs_hms,  tvb, offset, data_len, ENC_ASCII|ENC_NA );
852 			proto_tree_add_string( aprs_tree, hf_aprs_tz, tvb, offset, data_len, tzone );
853 			}
854 		else
855 			{ /* DHM */
856 			switch ( ch )
857 				{
858 				case 'z' : tzone = "zulu";    break;
859 				case '/' : tzone = "local";   break;
860 				default  : tzone = "unknown"; break;
861 				}
862 			proto_tree_add_item( aprs_tree, hf_aprs_dhm,  tvb, offset, data_len, ENC_ASCII|ENC_NA );
863 			proto_tree_add_string( aprs_tree, hf_aprs_tz, tvb, offset + 6, 1, tzone );
864 			}
865 		}
866 
867 	return offset + data_len;
868 }
869 
870 static int
aprs_latitude_compressed(proto_tree * aprs_tree,tvbuff_t * tvb,int offset)871 aprs_latitude_compressed( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
872 {
873 	if ( aprs_tree )
874 		{
875 		char *info_buffer;
876 		int   temp;
877 
878 		info_buffer = (char *)wmem_alloc( wmem_packet_scope(), STRLEN );
879 
880 		temp = ( tvb_get_guint8( tvb, offset + 0 ) - 33 );
881 		temp = ( tvb_get_guint8( tvb, offset + 1 ) - 33 ) + ( temp * 91 );
882 		temp = ( tvb_get_guint8( tvb, offset + 2 ) - 33 ) + ( temp * 91 );
883 		temp = ( tvb_get_guint8( tvb, offset + 3 ) - 33 ) + ( temp * 91 );
884 
885 		g_snprintf( info_buffer, STRLEN, "%6.2f", 90.0 - (temp / 380926.0) );
886 		proto_tree_add_string( aprs_tree, hf_aprs_lat, tvb, offset, 4, info_buffer );
887 		}
888 	return offset + 4;
889 }
890 
891 static int
aprs_longitude_compressed(proto_tree * aprs_tree,tvbuff_t * tvb,int offset)892 aprs_longitude_compressed( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
893 {
894 	if ( aprs_tree )
895 		{
896 		char *info_buffer;
897 		int   temp;
898 
899 		info_buffer = (char *)wmem_alloc( wmem_packet_scope(), STRLEN );
900 
901 		temp = ( tvb_get_guint8( tvb, offset + 0 ) - 33 );
902 		temp = ( tvb_get_guint8( tvb, offset + 1 ) - 33 ) + ( temp * 91 );
903 		temp = ( tvb_get_guint8( tvb, offset + 2 ) - 33 ) + ( temp * 91 );
904 		temp = ( tvb_get_guint8( tvb, offset + 3 ) - 33 ) + ( temp * 91 );
905 
906 		g_snprintf( info_buffer, STRLEN, "%7.2f", (temp / 190463.0) - 180.0 );
907 		proto_tree_add_string( aprs_tree, hf_aprs_long, tvb, offset, 4, info_buffer );
908 		}
909 	return offset + 4;
910 }
911 
912 static int
aprs_status(proto_tree * aprs_tree,tvbuff_t * tvb,int offset)913 aprs_status( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
914 {
915 	int data_len;
916 
917 	data_len = tvb_reported_length_remaining( tvb, offset );
918 
919 	if ( ( data_len > 7 ) && ( tvb_get_guint8( tvb, offset+6 ) == 'z' ) )
920 		{
921 		proto_tree_add_item( aprs_tree, hf_aprs_dhm, tvb, offset, 6, ENC_ASCII|ENC_NA );
922 		offset	 += 6;
923 		data_len -= 6;
924 		proto_tree_add_string( aprs_tree, hf_aprs_tz, tvb, offset, 1, "zulu" );
925 		offset	 += 1;
926 		data_len -= 1;
927 		}
928 	proto_tree_add_item( aprs_tree, hf_aprs_status, tvb, offset, data_len, ENC_ASCII|ENC_NA );
929 
930 	return offset + data_len;
931 }
932 
933 static int
aprs_item(proto_tree * aprs_tree,tvbuff_t * tvb,int offset)934 aprs_item( proto_tree *aprs_tree, tvbuff_t *tvb, int offset )
935 {
936 	char *info_buffer;
937 	int   data_len;
938 	char *ch_ptr;
939 
940 	data_len    = 10;
941 
942 	/*
943 	 * XXX - ASCII or UTF-8?
944 	 * See http://www.aprs.org/aprs12/utf-8.txt
945 	 */
946 	info_buffer = tvb_get_string_enc( wmem_packet_scope(), tvb, offset, data_len, ENC_ASCII|ENC_NA );
947 
948 	ch_ptr = strchr( info_buffer, '!' );
949 	if ( ch_ptr != NULL )
950 		{
951 		data_len = (int)(ch_ptr - info_buffer + 1);
952 		*ch_ptr = '\0';
953 		}
954 	else
955 		{
956 		ch_ptr = strchr( info_buffer, '!' );
957 		if ( ch_ptr != NULL )
958 			{
959 			data_len = (int)(ch_ptr - info_buffer + 1);
960 			*ch_ptr = '\0';
961 			}
962 		}
963 	proto_tree_add_string( aprs_tree, hf_aprs_item, tvb, offset, data_len, info_buffer );
964 
965 	return offset + data_len;
966 }
967 
968 static int
aprs_3rd_party(proto_tree * aprs_tree,tvbuff_t * tvb,int offset,int data_len)969 aprs_3rd_party( proto_tree *aprs_tree, tvbuff_t *tvb, int offset, int data_len )
970 {
971 	/* If the type of the hf[] entry pointed to by hfindex is FT_BYTES or FT_STRING */
972 	/*  then  data_len == -1 is allowed and means "remainder of the tvbuff"         */
973 	if ( data_len == -1 )
974 		{
975 		data_len = tvb_reported_length_remaining( tvb, offset );
976 #if 0 /* fcn'al change: defer */
977 		if ( data_len <= 0 )
978 			return offset;  /* there's no data */
979 #endif
980 		}
981 	proto_tree_add_item( aprs_tree, hf_aprs_third_party, tvb, offset, data_len, ENC_NA );
982 	/* tnc-2 */
983 	/* aea */
984 	return offset + data_len;
985 }
986 
987 static int
aprs_default_string(proto_tree * aprs_tree,tvbuff_t * tvb,int offset,int data_len,int hfindex)988 aprs_default_string( proto_tree *aprs_tree, tvbuff_t *tvb, int offset, int data_len, int hfindex )
989 {
990 	/* Assumption: hfindex points to an hf[] entry with type FT_STRING; should be validated ? */
991 	/* If the type of the hf[] entry pointed to by hfindex is FT_STRING      */
992 	/*  then  data_len == -1 is allowed and means "remainder of the tvbuff"  */
993 	if ( data_len == -1 )
994 		{
995 		data_len = tvb_reported_length_remaining( tvb, offset );
996 #if 0 /* fcn'al change: defer */
997 		if ( data_len <= 0 )
998 			return offset;  /* there's no data */
999 #endif
1000 		}
1001 	proto_tree_add_item( aprs_tree, hfindex, tvb, offset, data_len, ENC_ASCII|ENC_NA );
1002 	return offset + data_len;
1003 }
1004 
1005 static int
aprs_default_bytes(proto_tree * aprs_tree,tvbuff_t * tvb,int offset,int data_len,int hfindex)1006 aprs_default_bytes( proto_tree *aprs_tree, tvbuff_t *tvb, int offset, int data_len, int hfindex )
1007 {
1008 	/* Assumption: hfindex points to an hf[] entry with type FT_BYTES; should be validated ? */
1009 	/* If the type of the hf[] entry pointed to by hfindex is FT_BYTES      */
1010 	/*  then  data_len == -1 is allowed and means "remainder of the tvbuff" */
1011 	if ( data_len == -1 )
1012 		{
1013 		data_len = tvb_reported_length_remaining( tvb, offset );
1014 #if 0 /* fcn'al change: defer */
1015 		if ( data_len <= 0 )
1016 			return offset;  /* there's no data */
1017 #endif
1018 		}
1019 	proto_tree_add_item( aprs_tree, hfindex, tvb, offset, data_len, ENC_NA );
1020 	return offset + data_len;
1021 }
1022 
1023 static int
aprs_position(proto_tree * aprs_tree,tvbuff_t * tvb,int offset,gboolean with_msg)1024 aprs_position( proto_tree *aprs_tree, tvbuff_t *tvb, int offset, gboolean with_msg )
1025 {
1026 	guint8	 symbol_table_id    = 0;
1027 	guint8	 symbol_code	    = 0;
1028 	gboolean probably_a_msg	    = FALSE;
1029 	gboolean probably_not_a_msg = FALSE;
1030 
1031 	if ( g_ascii_isdigit( tvb_get_guint8( tvb, offset ) ) )
1032 		{
1033 		offset		= aprs_default_string( aprs_tree, tvb, offset, 8, hf_aprs_lat );
1034 		symbol_table_id = tvb_get_guint8( tvb, offset );
1035 		offset		= aprs_default_string( aprs_tree, tvb, offset, 1, hf_aprs_sym_id );
1036 		offset		= aprs_default_string( aprs_tree, tvb, offset, 9, hf_aprs_long );
1037 		symbol_code	= tvb_get_guint8( tvb, offset );
1038 		offset		= aprs_default_string( aprs_tree, tvb, offset, 1, hf_aprs_sym_code );
1039 		if ( gPREF_APRS_LAX )
1040 			{
1041 			switch ( tvb_get_guint8( tvb, offset ) )
1042 				{
1043 				case 'D'	: probably_a_msg = TRUE;     break;
1044 				case 'P'	: probably_a_msg = TRUE;     break;
1045 				case 'R'	: probably_a_msg = TRUE;     break;
1046 				case 'T'	: probably_a_msg = TRUE;     break;
1047 				default		: probably_not_a_msg = TRUE; break;
1048 				}
1049 			}
1050 		if ( with_msg || probably_a_msg || ! probably_not_a_msg )
1051 			offset = dissect_aprs_msg(	tvb,
1052 							offset,
1053 							aprs_tree,
1054 							( symbol_code == '_' ),
1055 							( symbol_table_id == '/' && symbol_code == '\\' )
1056 							);
1057 		}
1058 	else
1059 		{
1060 		symbol_table_id = tvb_get_guint8( tvb, offset );
1061 		offset = aprs_default_string( aprs_tree, tvb, offset, 1, hf_aprs_sym_id );
1062 		offset = aprs_latitude_compressed( aprs_tree, tvb, offset );
1063 		offset = aprs_longitude_compressed( aprs_tree, tvb, offset );
1064 		symbol_code = tvb_get_guint8( tvb, offset );
1065 		offset = aprs_default_string( aprs_tree, tvb, offset, 1, hf_aprs_sym_code );
1066 		offset = dissect_aprs_compressed_msg(	tvb,
1067 							offset,
1068 							aprs_tree
1069 							);
1070 		offset = dissect_aprs_compression_type(	tvb,
1071 							offset,
1072 							aprs_tree
1073 							);
1074 		if ( symbol_table_id == '/' && symbol_code == '\\' )
1075 			offset = aprs_default_string( aprs_tree, tvb, offset, 8, hf_aprs_msg_brg );
1076 		}
1077 
1078 	if ( symbol_code == '_' )
1079 		offset = dissect_aprs_weather(	tvb,
1080 						offset,
1081 						aprs_tree
1082 						);
1083 	if ( ( symbol_table_id == '/' && symbol_code == '@' ) || ( symbol_table_id == '\\' && symbol_code == '@' ) )
1084 		offset = dissect_aprs_storm(	tvb,
1085 						offset,
1086 						aprs_tree
1087 						);
1088 
1089 	return offset;
1090 }
1091 
1092 static int
dissect_aprs(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)1093 dissect_aprs( tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_ )
1094 {
1095 	proto_item    *ti;
1096 	proto_tree    *aprs_tree;
1097 
1098 	int	       offset;
1099 	guint8	       dti;
1100 	wmem_strbuf_t *sb;
1101 
1102 	col_set_str( pinfo->cinfo, COL_PROTOCOL, "APRS" );
1103 	col_clear( pinfo->cinfo, COL_INFO );
1104 
1105 	offset	 = 0;
1106 
1107 	dti	 = tvb_get_guint8( tvb, offset );
1108 
1109 	sb = wmem_strbuf_new_label(wmem_packet_scope());
1110 
1111 	if (dti != '!')
1112 		wmem_strbuf_append(sb, val_to_str_ext_const(dti, &aprs_description_ext, ""));
1113 
1114 	switch ( dti )
1115 		{
1116 		case '!':
1117 			/* Position or Ultimeter 2000 WX Station */
1118 			if ( tvb_get_guint8( tvb, offset + 1 ) == '!' )
1119 				{
1120 				wmem_strbuf_append(sb, "Ultimeter 2000 WX Station");
1121 				}
1122 			else
1123 				{
1124 				/* Position "without APRS messaging" */
1125 				wmem_strbuf_append(sb, "Position (");
1126 				wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1, 8));	/* Lat */
1127 				wmem_strbuf_append(sb, " ");
1128 				wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8 + 1, 9));	/* Long */
1129 				wmem_strbuf_append(sb, " ");
1130 				wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8, 1));	/* Symbol table id */
1131 				wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8 + 1 + 9, 1));	/* Symbol Code */
1132 				}
1133 			break;
1134 
1135 		case '=':
1136 			/* Position "with APRS messaging" + Ext APRS message */
1137 			wmem_strbuf_append(sb, " (");
1138 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1, 8));	/* Lat */
1139 			wmem_strbuf_append(sb, " ");
1140 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8 + 1, 9));	/* Long */
1141 			wmem_strbuf_append(sb, " ");
1142 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8, 1));	/* Symbol table id */
1143 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 8 + 1 + 9, 1));	/* Symbol Code */
1144 			break;
1145 
1146 		case '/':
1147 			/* Position + timestamp "without APRS messaging" */
1148 			wmem_strbuf_append(sb, " (");
1149 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1, 7));	/* Timestamp */
1150 			wmem_strbuf_append(sb, " ");
1151 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 1, 8));    /*??*/	/* Lat */
1152 			wmem_strbuf_append(sb, " ");
1153 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 8 + 1, 9));	/* Long */
1154 			wmem_strbuf_append(sb, " ");
1155 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7, 1));	/* Symbol table id */
1156 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 1 + 9, 1));	/* Symbol Code */
1157 			break;
1158 
1159 		case '@':
1160 			/* Position + timestamp "with APRS messaging" + Ext APRS message */
1161 			wmem_strbuf_append(sb, " (");
1162 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1, 7));	/* Timestamp */
1163 			wmem_strbuf_append(sb, " ");
1164 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 1, 8));    /*??*/	/* Lat */
1165 			wmem_strbuf_append(sb, " ");
1166 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 8 + 1, 9));	/* Long */
1167 			wmem_strbuf_append(sb, " ");
1168 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7, 1));	/* Symbol table id */
1169 			wmem_strbuf_append(sb, tvb_format_text(pinfo->pool, tvb, offset + 1 + 7 + 1 + 9, 1));	/* Symbol Code */
1170 			break;
1171 		}
1172 
1173 	col_add_str( pinfo->cinfo, COL_INFO, wmem_strbuf_get_str(sb) );
1174 
1175 	/* create display subtree for the protocol */
1176 	ti = proto_tree_add_protocol_format( parent_tree , proto_aprs, tvb, 0, -1, "%s", wmem_strbuf_get_str(sb) );
1177 	aprs_tree = proto_item_add_subtree( ti, ett_aprs );
1178 
1179 	proto_tree_add_item( aprs_tree, hf_aprs_dti, tvb, offset, 1, ENC_ASCII|ENC_NA );
1180 	offset += 1;
1181 
1182 	switch ( dti )
1183 		{
1184 		case '<'	: /* Station Capabilities */
1185 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_station );
1186 			break;
1187 		case '>'	: /* Status */
1188 			offset = aprs_status( aprs_tree, tvb, offset );
1189 			break;
1190 		case '?'	: /* Query */
1191 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_query );
1192 			break;
1193 		case '$'	: /* Raw GPS data or Ultimeter 2000 */
1194 			if ( tvb_get_guint8( tvb, offset ) == 'U' )
1195 				offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_ultimeter_2000 );
1196 			else
1197 				offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_raw );
1198 			break;
1199 		case '%'	: /* Agrelo DFJr / MicroFinder */
1200 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_agrelo );
1201 			break;
1202 		case 'T'	: /* Telemetry data */
1203 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_telemetry );
1204 			break;
1205 		case '['	: /* Maidenhead grid locator beacon (obsolete) */
1206 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_maidenhead );
1207 			break;
1208 		case '_'	: /* Weather Report (without position) */
1209 			offset = aprs_timestamp( aprs_tree, tvb, offset );
1210 			offset = dissect_aprs_weather( tvb,
1211 				offset,
1212 				aprs_tree
1213 				);
1214 			break;
1215 		case ','	: /* Invalid data or test data */
1216 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_invalid_test );
1217 			break;
1218 		case '{'	: /* User-Defined APRS packet format */
1219 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_user_defined );
1220 			break;
1221 		case '}'	: /* Third-party traffic */
1222 			offset = aprs_3rd_party( aprs_tree, tvb, offset, -1 );
1223 			break;
1224 		case ':'	: /* Message */
1225 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_message );
1226 			break;
1227 		case 0x1c	: /* Current Mic-E Data (Rev 0 beta) */
1228 			offset = dissect_mic_e(	tvb,
1229 						offset,
1230 						pinfo,
1231 						aprs_tree,
1232 						hf_aprs_mic_e_0_current
1233 						);
1234 			break;
1235 		case 0x1d	: /* Old Mic-E Data (Rev 0 beta) */
1236 			offset = dissect_mic_e(	tvb,
1237 						offset,
1238 						pinfo,
1239 						aprs_tree,
1240 						hf_aprs_mic_e_0_old
1241 						);
1242 			break;
1243 		case '\''	: /* Old Mic-E Data (but Current data for TM-D700) */
1244 			offset = dissect_mic_e(	tvb,
1245 						offset,
1246 						pinfo,
1247 						aprs_tree,
1248 						hf_aprs_mic_e_old
1249 						);
1250 			break;
1251 		case '`'	: /* Current Mic-E Data (not used in TM-D700) */
1252 			offset = dissect_mic_e(	tvb,
1253 						offset,
1254 						pinfo,
1255 						aprs_tree,
1256 						hf_aprs_mic_e_current
1257 						);
1258 			break;
1259 		case '#'	: /* Peet Bros U-II Weather Station */
1260 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_peet_1 );
1261 			break;
1262 		case '*'	: /* Peet Bros U-II Weather Station */
1263 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_peet_2 );
1264 			break;
1265 		case '&'	: /* [Reserved - Map Feature] */
1266 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_map_feature );
1267 			break;
1268 		case '+'	: /* [Reserved - Shelter data with time] */
1269 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_shelter_data );
1270 			break;
1271 		case '.'	: /* [Reserved - Space weather] */
1272 			offset = aprs_default_bytes( aprs_tree, tvb, offset, -1, hf_aprs_space_weather );
1273 			break;
1274 		case ')'	: /* Item */
1275 			offset = aprs_item( aprs_tree, tvb, offset );
1276 			offset = aprs_position( aprs_tree, tvb, offset, TRUE );
1277 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
1278 			break;
1279 		case ';'	: /* Object */
1280 			offset = aprs_default_string( aprs_tree, tvb, offset, 10, hf_aprs_object );
1281 			offset = aprs_timestamp( aprs_tree, tvb, offset );
1282 			offset = aprs_position( aprs_tree, tvb, offset, TRUE );
1283 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
1284 			break;
1285 		case '!'	: /* Position or Ultimeter 2000 WX Station */
1286 			if ( tvb_get_guint8( tvb, offset ) == '!' )
1287 				offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_ultimeter_2000 );
1288 			else
1289 				{
1290 				offset = aprs_position( aprs_tree, tvb, offset, FALSE );
1291 				offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
1292 				}
1293 			break;
1294 		case '='	: /* Position + Ext APRS message */
1295 			offset = aprs_position( aprs_tree, tvb, offset, TRUE );
1296 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
1297 			break;
1298 		case '/'	: /* Position + timestamp */
1299 			offset = aprs_timestamp( aprs_tree, tvb, offset );
1300 			offset = aprs_position( aprs_tree, tvb, offset, FALSE );
1301 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
1302 			break;
1303 		case '@'	: /* Position + timestamp + Ext APRS message */
1304 			offset = aprs_timestamp( aprs_tree, tvb, offset );
1305 			offset = aprs_position( aprs_tree, tvb, offset, TRUE );
1306 			offset = aprs_default_string( aprs_tree, tvb, offset, -1, hf_aprs_comment );
1307 			break;
1308 		default	: break;
1309 		}
1310 	return offset;
1311 }
1312 
1313 void
proto_register_aprs(void)1314 proto_register_aprs( void )
1315 {
1316 	module_t *aprs_module;
1317 
1318 	static hf_register_info hf[] = {
1319 		{ &hf_aprs_dti,
1320 			{ "Data Type Indicator",		"aprs.dti",
1321 			FT_STRING, BASE_NONE, NULL, 0x0,
1322 			NULL, HFILL }
1323 		},
1324 		{ &hf_aprs_sym_code,
1325 			{ "Symbol code",		"aprs.sym_code",
1326 			FT_STRING, BASE_NONE, NULL, 0x0,
1327 			NULL, HFILL }
1328 		},
1329 		{ &hf_aprs_sym_id,
1330 			{ "Symbol table ID",		"aprs.sym_id",
1331 			FT_STRING, BASE_NONE, NULL, 0x0,
1332 			NULL, HFILL }
1333 		},
1334 
1335 /* Position */
1336 #if 0
1337 		{ &hf_aprs_position,
1338 			{ "Position",		"aprs.position",
1339 			FT_STRING, BASE_NONE, NULL, 0x0,
1340 			NULL, HFILL }
1341 		},
1342 #endif
1343 		{ &hf_aprs_lat,
1344 			{ "Latitude",		"aprs.position.lat",
1345 			FT_STRING, BASE_NONE, NULL, 0x0,
1346 			NULL, HFILL }
1347 		},
1348 		{ &hf_aprs_long,
1349 			{ "Longitude",		"aprs.position.long",
1350 			FT_STRING, BASE_NONE, NULL, 0x0,
1351 			NULL, HFILL }
1352 		},
1353 
1354 /* APRS Messages */
1355 		{ &hf_aprs_comment,
1356 			{ "Comment",			"aprs.comment",
1357 			FT_STRING, BASE_NONE, NULL, 0x0,
1358 			NULL, HFILL }
1359 		},
1360 		{ &hf_ultimeter_2000,
1361 			{ "Ultimeter 2000",		"aprs.ultimeter_2000",
1362 			FT_STRING, BASE_NONE, NULL, 0x0,
1363 			NULL, HFILL }
1364 		},
1365 		{ &hf_aprs_status,
1366 			{ "Status",			"aprs.status",
1367 			FT_STRING, BASE_NONE, NULL, 0x0,
1368 			NULL, HFILL }
1369 		},
1370 		{ &hf_aprs_object,
1371 			{ "Object",			"aprs.object",
1372 			FT_STRING, BASE_NONE, NULL, 0x0,
1373 			NULL, HFILL }
1374 		},
1375 		{ &hf_aprs_item,
1376 			{ "Item",			"aprs.item",
1377 			FT_STRING, BASE_NONE, NULL, 0x0,
1378 			NULL, HFILL }
1379 		},
1380 		{ &hf_aprs_query,
1381 			{ "Query",			"aprs.query",
1382 			FT_STRING, BASE_NONE, NULL, 0x0,
1383 			NULL, HFILL }
1384 		},
1385 		{ &hf_aprs_telemetry,
1386 			{ "Telemetry",			"aprs.telemetry",
1387 			FT_STRING, BASE_NONE, NULL, 0x0,
1388 			NULL, HFILL }
1389 		},
1390 		{ &hf_aprs_raw,
1391 			{ "Raw",			"aprs.raw",
1392 			FT_STRING, BASE_NONE, NULL, 0x0,
1393 			"Raw NMEA position report format", HFILL }
1394 		},
1395 		{ &hf_aprs_station,
1396 			{ "Station",			"aprs.station",
1397 			FT_STRING, BASE_NONE, NULL, 0x0,
1398 			"Station capabilities", HFILL }
1399 		},
1400 		{ &hf_aprs_message,
1401 			{ "Message",			"aprs.message",
1402 			FT_STRING, BASE_NONE, NULL, 0x0,
1403 			NULL, HFILL }
1404 		},
1405 		{ &hf_aprs_agrelo,
1406 			{ "Agrelo",			"aprs.agrelo",
1407 			FT_BYTES, BASE_NONE, NULL, 0x0,
1408 			"Agrelo DFJr / MicroFinder", HFILL }
1409 		},
1410 		{ &hf_aprs_maidenhead,
1411 			{ "Maidenhead",			"aprs.maidenhead",
1412 			FT_BYTES, BASE_NONE, NULL, 0x0,
1413 			"Maidenhead grid locator beacon (obsolete)", HFILL }
1414 		},
1415 		{ &hf_aprs_invalid_test,
1416 			{ "Invalid or test",		"aprs.invalid_test",
1417 			FT_BYTES, BASE_NONE, NULL, 0x0,
1418 			"Invalid data or test data", HFILL }
1419 		},
1420 		{ &hf_aprs_user_defined,
1421 			{ "User-Defined",		"aprs.user_defined",
1422 			FT_BYTES, BASE_NONE, NULL, 0x0,
1423 			"User-Defined APRS packet format", HFILL }
1424 		},
1425 		{ &hf_aprs_third_party,
1426 			{ "Third-party",		"aprs.third_party",
1427 			FT_BYTES, BASE_NONE, NULL, 0x0,
1428 			"Third-party traffic", HFILL }
1429 		},
1430 		{ &hf_aprs_peet_1,
1431 			{ "Peet U-II (1)",		"aprs.peet_1",
1432 			FT_BYTES, BASE_NONE, NULL, 0x0,
1433 			"Peet Bros U-II Weather Station", HFILL }
1434 		},
1435 		{ &hf_aprs_peet_2,
1436 			{ "Peet U-II (2)",		"aprs.peet_2",
1437 			FT_BYTES, BASE_NONE, NULL, 0x0,
1438 			"Peet Bros U-II Weather Station", HFILL }
1439 		},
1440 		{ &hf_aprs_map_feature,
1441 			{ "Map Feature",		"aprs.map_feature",
1442 			FT_BYTES, BASE_NONE, NULL, 0x0,
1443 			"[Reserved - Map Feature", HFILL }
1444 		},
1445 		{ &hf_aprs_shelter_data,
1446 			{ "Shelter data",		"aprs.shelter_data",
1447 			FT_BYTES, BASE_NONE, NULL, 0x0,
1448 			"[Reserved - Shelter data with time]", HFILL }
1449 		},
1450 		{ &hf_aprs_space_weather,
1451 			{ "Space weather",		"aprs.space_weather",
1452 			FT_BYTES, BASE_NONE, NULL, 0x0,
1453 			"[Reserved - Space weather]", HFILL }
1454 		},
1455 		{ &hf_aprs_storm,
1456 			{ "Storm",			"aprs.storm",
1457 			FT_STRING, BASE_NONE, NULL, 0x0,
1458 			NULL, HFILL }
1459 		},
1460 
1461 /* Time stamp */
1462 		{ &hf_aprs_dhm,
1463 			{ "Day/Hour/Minute",		"aprs.dhm",
1464 			FT_STRING, BASE_NONE, NULL, 0x0,
1465 			NULL, HFILL }
1466 		},
1467 		{ &hf_aprs_hms,
1468 			{ "Hour/Minute/Second",		"aprs.hms",
1469 			FT_STRING, BASE_NONE, NULL, 0x0,
1470 			NULL, HFILL }
1471 		},
1472 		{ &hf_aprs_mdhm,
1473 			{ "Month/Day/Hour/Minute",		"aprs.mdhm",
1474 			FT_STRING, BASE_NONE, NULL, 0x0,
1475 			NULL, HFILL }
1476 		},
1477 		{ &hf_aprs_tz,
1478 			{ "Time Zone",		"aprs.tz",
1479 			FT_STRING, BASE_NONE, NULL, 0x0,
1480 			NULL, HFILL }
1481 		},
1482 
1483 /* Compressed Msg */
1484 		{ &hf_aprs_compression_type,
1485 			{ "Compression type",		"aprs.ct",
1486 			FT_UINT8, BASE_HEX, NULL, 0x0,
1487 			NULL, HFILL }
1488 		},
1489 		{ &hf_aprs_ct_gps_fix,
1490 			{ "GPS fix type",		"aprs.ct.gps_fix",
1491 			FT_UINT8, BASE_HEX, VALS(gps_vals), 0x20,
1492 			NULL, HFILL }
1493 		},
1494 		{ &hf_aprs_ct_nmea_src,
1495 			{ "NMEA source",		"aprs.ct.nmea_src",
1496 			FT_UINT8, BASE_HEX, VALS(nmea_vals), 0x18,
1497 			NULL, HFILL }
1498 		},
1499 		{ &hf_aprs_ct_origin,
1500 			{ "Compression origin",		"aprs.ct.origin",
1501 			FT_UINT8, BASE_HEX, VALS(ctype_vals), 0x07,
1502 			NULL, HFILL }
1503 		},
1504 
1505 /* Ext Msg */
1506 		{ &hf_aprs_msg,
1507 			{ "Extended message",			"aprs.msg",
1508 			FT_STRING, BASE_NONE, NULL, 0x0,
1509 			NULL, HFILL }
1510 		},
1511 		{ &hf_aprs_msg_rng,
1512 			{ "Range",			"aprs.msg.rng",
1513 			FT_STRING, BASE_NONE, NULL, 0x0,
1514 			"Pre-calculated radio range", HFILL }
1515 		},
1516 		{ &hf_aprs_msg_cse,
1517 			{ "Course",			"aprs.msg.cse",
1518 			FT_STRING, BASE_NONE, NULL, 0x0,
1519 			NULL, HFILL }
1520 		},
1521 		{ &hf_aprs_msg_spd,
1522 			{ "Speed",			"aprs.msg.spd",
1523 			FT_STRING, BASE_NONE, NULL, 0x0,
1524 			NULL, HFILL }
1525 		},
1526 		{ &hf_aprs_msg_dir,
1527 			{ "Wind direction",		"aprs.msg.dir",
1528 			FT_STRING, BASE_NONE, NULL, 0x0,
1529 			NULL, HFILL }
1530 		},
1531 		{ &hf_aprs_msg_brg,
1532 			{ "Bearing",			"aprs.msg.brg",
1533 			FT_STRING, BASE_NONE, NULL, 0x0,
1534 			NULL, HFILL }
1535 		},
1536 		{ &hf_aprs_msg_nrq,
1537 			{ "Number/Range/Quality",			"aprs.msg.nrq",
1538 			FT_STRING, BASE_NONE, NULL, 0x0,
1539 			NULL, HFILL }
1540 		},
1541 
1542 /* Msg PHGD */
1543 		{ &hf_aprs_msg_phg_p,
1544 			{ "Power",		"aprs.msg.phg.p",
1545 			FT_STRING, BASE_NONE, NULL, 0x0,
1546 			NULL, HFILL }
1547 		},
1548 		{ &hf_aprs_msg_phg_h,
1549 			{ "Height",		"aprs.msg.phg.h",
1550 			FT_STRING, BASE_NONE, NULL, 0x0,
1551 			NULL, HFILL }
1552 		},
1553 		{ &hf_aprs_msg_phg_g,
1554 			{ "Gain",		"aprs.msg.phg.g",
1555 			FT_STRING, BASE_NONE, NULL, 0x0,
1556 			NULL, HFILL }
1557 		},
1558 		{ &hf_aprs_msg_phg_d,
1559 			{ "Directivity",		"aprs.msg.phg.d",
1560 			FT_STRING, BASE_NONE, NULL, 0x0,
1561 			NULL, HFILL }
1562 		},
1563 
1564 /* Msg DFS */
1565 		{ &hf_aprs_msg_dfs_s,
1566 			{ "Strength",			"aprs.msg.dfs.s",
1567 			FT_STRING, BASE_NONE, NULL, 0x0,
1568 			NULL, HFILL }
1569 		},
1570 		{ &hf_aprs_msg_dfs_h,
1571 			{ "Height",			"aprs.msg.dfs.h",
1572 			FT_STRING, BASE_NONE, NULL, 0x0,
1573 			NULL, HFILL }
1574 		},
1575 		{ &hf_aprs_msg_dfs_g,
1576 			{ "Gain",			"aprs.msg.dfs.g",
1577 			FT_STRING, BASE_NONE, NULL, 0x0,
1578 			NULL, HFILL }
1579 		},
1580 		{ &hf_aprs_msg_dfs_d,
1581 			{ "Directivity",		"aprs.msg.dfs.d",
1582 			FT_STRING, BASE_NONE, NULL, 0x0,
1583 			NULL, HFILL }
1584 		},
1585 
1586 /* Msg AOD */
1587 		{ &hf_aprs_msg_aod_t,
1588 			{ "Type",			"aprs.msg.aod.t",
1589 			FT_STRING, BASE_NONE, NULL, 0x0,
1590 			NULL, HFILL }
1591 		},
1592 		{ &hf_aprs_msg_aod_c,
1593 			{ "Colour",			"aprs.msg.aod.c",
1594 			FT_STRING, BASE_NONE, NULL, 0x0,
1595 			NULL, HFILL }
1596 		},
1597 
1598 /* Weather */
1599 		{ &hf_aprs_weather,
1600 			{ "Weather report",			"aprs.weather",
1601 			FT_STRING, BASE_NONE, NULL, 0x0,
1602 			NULL, HFILL }
1603 		},
1604 		{ &hf_aprs_weather_dir,
1605 			{ "Wind direction",		"aprs.weather.dir",
1606 			FT_STRING, BASE_NONE, NULL, 0x0,
1607 			NULL, HFILL }
1608 		},
1609 		{ &hf_aprs_weather_spd,
1610 			{ "Wind speed",			"aprs.weather.speed",
1611 			FT_STRING, BASE_NONE, NULL, 0x0,
1612 			"Wind speed (1 minute)", HFILL }
1613 		},
1614 		{ &hf_aprs_weather_peak,
1615 			{ "Peak wind speed",		"aprs.weather.peak",
1616 			FT_STRING, BASE_NONE, NULL, 0x0,
1617 			NULL, HFILL }
1618 		},
1619 		{ &hf_aprs_weather_temp,
1620 			{ "Temperature (F)",		"aprs.weather.temp",
1621 			FT_STRING, BASE_NONE, NULL, 0x0,
1622 			NULL, HFILL }
1623 		},
1624 		{ &hf_aprs_weather_rain_1,
1625 			{ "Rain (last 1 hour)",			"aprs.weather.1_hour",
1626 			FT_STRING, BASE_NONE, NULL, 0x0,
1627 			NULL, HFILL }
1628 		},
1629 		{ &hf_aprs_weather_rain_24,
1630 			{ "Rain (last 24 hours)",		"aprs.weather.24_hour",
1631 			FT_STRING, BASE_NONE, NULL, 0x0,
1632 			NULL, HFILL }
1633 		},
1634 		{ &hf_aprs_weather_rain,
1635 			{ "Rain",			"aprs.weather.rain",
1636 			FT_STRING, BASE_NONE, NULL, 0x0,
1637 			NULL, HFILL }
1638 		},
1639 		{ &hf_aprs_weather_humidty,
1640 			{ "Humidity",			"aprs.weather.humidity",
1641 			FT_STRING, BASE_NONE, NULL, 0x0,
1642 			NULL, HFILL }
1643 		},
1644 		{ &hf_aprs_weather_press,
1645 			{ "Pressure",			"aprs.weather.pressure",
1646 			FT_STRING, BASE_NONE, NULL, 0x0,
1647 			NULL, HFILL }
1648 		},
1649 		{ &hf_aprs_weather_luminosity,
1650 			{ "Luminosity",			"aprs.weather.luminosity",
1651 			FT_STRING, BASE_NONE, NULL, 0x0,
1652 			NULL, HFILL }
1653 		},
1654 		{ &hf_aprs_weather_snow,
1655 			{ "Snow",			"aprs.weather.snow",
1656 			FT_STRING, BASE_NONE, NULL, 0x0,
1657 			NULL, HFILL }
1658 		},
1659 		{ &hf_aprs_weather_raw_rain,
1660 			{ "Raw rain",			"aprs.weather.raw_rain",
1661 			FT_STRING, BASE_NONE, NULL, 0x0,
1662 			NULL, HFILL }
1663 		},
1664 		{ &hf_aprs_weather_software,
1665 			{ "Software",			"aprs.weather.software",
1666 			FT_STRING, BASE_NONE, NULL, 0x0,
1667 			NULL, HFILL }
1668 		},
1669 		{ &hf_aprs_weather_unit,
1670 			{ "Unit",			"aprs.weather.unit",
1671 			FT_STRING, BASE_NONE, NULL, 0x0,
1672 			NULL, HFILL }
1673 		},
1674 
1675 /* MIC-E */
1676 		{ &hf_aprs_mic_e_0_current,
1677 			{ "Current Mic-E (Rev 0)",	"aprs.mic_e_0_current",
1678 			FT_STRING, BASE_NONE, NULL, 0x0,
1679 			NULL, HFILL }
1680 		},
1681 		{ &hf_aprs_mic_e_0_old,
1682 			{ "Old Mic-E (Rev 0)",		"aprs.mic_e_0_old",
1683 			FT_STRING, BASE_NONE, NULL, 0x0,
1684 			NULL, HFILL }
1685 		},
1686 		{ &hf_aprs_mic_e_old,
1687 			{ "Old Mic-E",			"aprs.mic_e_old",
1688 			FT_STRING, BASE_NONE, NULL, 0x0,
1689 			"Old Mic-E Data (but Current data for TM-D700)", HFILL }
1690 		},
1691 		{ &hf_aprs_mic_e_current,
1692 			{ "Current Mic-E",		"aprs.mic_e_current",
1693 			FT_STRING, BASE_NONE, NULL, 0x0,
1694 			"Current Mic-E Data (not used in TM-D700)", HFILL }
1695 		},
1696 		{ &hf_aprs_mic_e_dst,
1697 			{ "Destination Address",		"aprs.mic_e.dst",
1698 			FT_STRING, BASE_NONE, NULL, 0x0,
1699 			NULL, HFILL }
1700 		},
1701 		{ &hf_aprs_mic_e_long_d,
1702 			{ "Longitude degrees",		"aprs.mic_e.long_d",
1703 			FT_UINT8, BASE_HEX, NULL, 0x0,
1704 			NULL, HFILL }
1705 		},
1706 		{ &hf_aprs_mic_e_long_m  ,
1707 			{ "Longitude minutes",		"aprs.mic_e.long_m",
1708 			FT_UINT8, BASE_HEX, NULL, 0x0,
1709 			NULL, HFILL }
1710 		},
1711 		{ &hf_aprs_mic_e_long_h,
1712 			{ "Longitude hundredths of minutes",	"aprs.mic_e.long_h",
1713 			FT_UINT8, BASE_HEX, NULL, 0x0,
1714 			NULL, HFILL }
1715 		},
1716 		{ &hf_aprs_mic_e_spd_sp,
1717 			{ "Speed (hundreds & tens)",		"aprs.mic_e.speed_sp",
1718 			FT_UINT8, BASE_HEX, NULL, 0x0,
1719 			NULL, HFILL }
1720 		},
1721 		{ &hf_aprs_mic_e_spd_dc,
1722 			{ "Speed (tens), Course (hundreds)",		"aprs.mic_e.speed_dc",
1723 			FT_UINT8, BASE_HEX, NULL, 0x0,
1724 			NULL, HFILL }
1725 		},
1726 		{ &hf_aprs_mic_e_spd_se,
1727 			{ "Course (tens & units)",	"aprs.mic_e.speed_se",
1728 			FT_UINT8, BASE_HEX, NULL, 0x0,
1729 			NULL, HFILL }
1730 		},
1731 		{ &hf_aprs_mic_e_telemetry,
1732 			{ "Telemetry",	"aprs.mic_e.telemetry",
1733 			FT_BYTES, BASE_NONE, NULL, 0x0,
1734 			NULL, HFILL }
1735 		},
1736 		{ &hf_aprs_mic_e_status,
1737 			{ "Status",	"aprs.mic_e.status",
1738 			FT_STRING, BASE_NONE, NULL, 0x0,
1739 			NULL, HFILL }
1740 		},
1741 		{ &hf_aprs_storm_dir,
1742 			{ "Direction",	"aprs.storm.direction",
1743 			FT_STRING, BASE_NONE, NULL, 0x0,
1744 			NULL, HFILL }
1745 		},
1746 		{ &hf_aprs_storm_spd,
1747 			{ "Speed (knots)",	"aprs.storm.speed",
1748 			FT_STRING, BASE_NONE, NULL, 0x0,
1749 			NULL, HFILL }
1750 		},
1751 		{ &hf_aprs_storm_type,
1752 			{ "Type",	"aprs.storm.type",
1753 			FT_STRING, BASE_NONE, NULL, 0x0,
1754 			NULL, HFILL }
1755 		},
1756 		{ &hf_aprs_storm_sws,
1757 			{ "Sustained wind speed (knots)",	"aprs.storm.sws",
1758 			FT_STRING, BASE_NONE, NULL, 0x0,
1759 			NULL, HFILL }
1760 		},
1761 		{ &hf_aprs_storm_pwg,
1762 			{ "Peak wind gusts (knots)",	"aprs.storm.pwg",
1763 			FT_STRING, BASE_NONE, NULL, 0x0,
1764 			NULL, HFILL }
1765 		},
1766 		{ &hf_aprs_storm_cp,
1767 			{ "Central pressure (millibars/hPascal)",	"aprs.storm.central_pressure",
1768 			FT_STRING, BASE_NONE, NULL, 0x0,
1769 			NULL, HFILL }
1770 		},
1771 		{ &hf_aprs_storm_rhw,
1772 			{ "Radius Hurricane Winds (nautical miles)",	"aprs.storm.radius_hurricane_winds",
1773 			FT_STRING, BASE_NONE, NULL, 0x0,
1774 			NULL, HFILL }
1775 		},
1776 		{ &hf_aprs_storm_rtsw,
1777 			{ "Radius Tropical Storm Winds (nautical miles)",	"aprs.storm.radius_tropical_storms_winds",
1778 			FT_STRING, BASE_NONE, NULL, 0x0,
1779 			NULL, HFILL }
1780 		},
1781 		{ &hf_aprs_storm_rwg,
1782 			{ "Radius Whole Gale (nautical miles)",	"aprs.storm.radius_whole_gale",
1783 			FT_STRING, BASE_NONE, NULL, 0x0,
1784 			NULL, HFILL }
1785 		}
1786 	};
1787 
1788 	static gint *ett[] = {
1789 		&ett_aprs,
1790 		&ett_aprs_msg,
1791 		&ett_aprs_ct,
1792 		&ett_aprs_weather,
1793 		&ett_aprs_storm,
1794 		&ett_aprs_mic_e,
1795 	};
1796 
1797 	proto_aprs = proto_register_protocol("Automatic Position Reporting System", "APRS", "aprs");
1798 
1799 	register_dissector( "aprs", dissect_aprs, proto_aprs);
1800 
1801 	proto_register_field_array( proto_aprs, hf, array_length(hf ) );
1802 	proto_register_subtree_array( ett, array_length( ett ) );
1803 
1804 	aprs_module = prefs_register_protocol( proto_aprs, NULL);
1805 
1806 	prefs_register_bool_preference(aprs_module, "showaprslax",
1807 		"Allow APRS violations.",
1808 		"Attempt to display common APRS protocol violations correctly",
1809 		&gPREF_APRS_LAX );
1810 
1811 }
1812 
1813 /*
1814  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1815  *
1816  * Local variables:
1817  * c-basic-offset: 8
1818  * tab-width: 8
1819  * indent-tabs-mode: t
1820  * End:
1821  *
1822  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1823  * :indentSize=8:tabSize=8:noTabs=false:
1824  */
1825