Fix various typos and spelling errors.
[obnox/wireshark/wip.git] / epan / dissectors / packet-synphasor.c
1 /* packet-synphasor.c
2  * Dissector for IEEE C37.118 synchrophasor frames.
3  *
4  * Copyright 2008, Jens Steinhauser <jens.steinhauser@omicron.at>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #       include "config.h"
29 #endif
30
31 #include <glib.h>
32 #include <epan/conversation.h>
33 #include <epan/crc16.h>
34 #include <epan/dissectors/packet-tcp.h>
35 #include <epan/packet.h>
36 #include <epan/prefs.h>
37
38 #include <math.h>
39
40 #define PROTOCOL_NAME       "IEEE C37.118 Synchrophasor Protocol"
41 #define PROTOCOL_SHORT_NAME "SYNCHROPHASOR"
42 #define PROTOCOL_ABBREV     "synphasor"
43
44 /* forward references */
45 void proto_reg_handoff_synphasor(void);
46
47 /* global variables */
48
49 static int     proto_synphasor   = -1;
50 static GSList *config_frame_list = NULL;
51
52 #if ! GLIB_CHECK_VERSION(2,10,0)
53 static GMemChunk *frame_chunks   = NULL;
54 #endif
55
56 /* user preferences */
57 static guint global_pref_tcp_port = 4712;
58 static guint global_pref_udp_port = 4713;
59
60 /* the ett... variables hold the state (open/close) of the treeview in the GUI */
61 static gint ett_synphasor          = -1; /* root element for this protocol */
62   /* used in the common header */
63   static gint ett_frtype           = -1;
64   static gint ett_timequal         = -1;
65   /* used for config frames */
66   static gint ett_conf             = -1;
67     static gint ett_conf_station   = -1;
68       static gint ett_conf_format  = -1;
69       static gint ett_conf_phnam   = -1;
70       static gint ett_conf_annam   = -1;
71       static gint ett_conf_dgnam   = -1;
72       static gint ett_conf_phconv  = -1;
73       static gint ett_conf_anconv  = -1;
74       static gint ett_conf_dgmask  = -1;
75   /* used for data frames */
76   static gint ett_data             = -1;
77     static gint ett_data_block     = -1;
78       static gint ett_data_stat    = -1;
79       static gint ett_data_phasors = -1;
80       static gint ett_data_analog  = -1;
81       static gint ett_data_digital = -1;
82   /* used for command frames */
83   static gint ett_command          = -1;
84
85 /* handles to the header fields hf[] in proto_register_synphasor() */
86 static int hf_sync                  = -1;
87 static int hf_sync_frtype           = -1;
88 static int hf_sync_version          = -1;
89 static int hf_idcode                = -1;
90 static int hf_frsize                = -1;
91 static int hf_soc                   = -1;
92 static int hf_timeqal_lsdir         = -1;
93 static int hf_timeqal_lsocc         = -1;
94 static int hf_timeqal_lspend        = -1;
95 static int hf_timeqal_timequalindic = -1;
96 static int hf_fracsec               = -1;
97 static int hf_conf_timebase         = -1;
98 static int hf_conf_numpmu           = -1;
99 static int hf_conf_formatb3         = -1;
100 static int hf_conf_formatb2         = -1;
101 static int hf_conf_formatb1         = -1;
102 static int hf_conf_formatb0         = -1;
103 static int hf_conf_fnom             = -1;
104 static int hf_conf_cfgcnt           = -1;
105 static int hf_data_statb15          = -1;
106 static int hf_data_statb14          = -1;
107 static int hf_data_statb13          = -1;
108 static int hf_data_statb12          = -1;
109 static int hf_data_statb11          = -1;
110 static int hf_data_statb10          = -1;
111 static int hf_data_statb05to04      = -1;
112 static int hf_data_statb03to00      = -1;
113 static int hf_command               = -1;
114
115 /* the five different frame types for this protocol */
116 enum FrameType {
117         DATA = 0,
118         HEADER,
119         CFG1,
120         CFG2,
121         CMD
122 };
123
124 /* the channel names in the protocol are all 16 bytes
125  * long (and don't have to be NULL terminated) */
126 #define CHNAM_LEN 16
127
128 /* Structures to save CFG frame content. */
129
130 /* type to indicate the format for (D)FREQ/PHASORS/ANALOG in data frame  */
131 typedef enum { integer,         /* 16 bit signed integer */
132                floating_point   /* single precision floating point */
133 } data_format;
134
135 typedef enum { rect, polar } phasor_notation;
136
137 /* holds the information required to dissect a single phasor */
138 typedef struct {
139         char          name[CHNAM_LEN + 1];
140         enum { V, A } unit;
141         guint32       conv; /* conversation factor in 10^-5 scale */
142 } phasor_info;
143
144 /* holds the information for an analog value */
145 typedef struct {
146         char    name[CHNAM_LEN + 1];
147         guint32 conv; /* conversation factor, user defined scaling (so its pretty useless) */
148 } analog_info;
149
150 /* holds information required to dissect a single PMU block in a data frame */
151 typedef struct {
152         guint16         id;                  /* identifies source of block     */
153         char            name[CHNAM_LEN + 1]; /* holds STN                      */
154         data_format     format_fr;           /* data format of FREQ and DFREQ  */
155         data_format     format_ph;           /* data format of PHASORS         */
156         data_format     format_an;           /* data format of ANALOG          */
157         phasor_notation phasor_notation;     /* format of the phasors          */
158         guint           fnom;                /* nominal line frequency         */
159         guint           num_dg;              /* number of digital status words */
160         GArray         *phasors;             /* array of phasor_infos          */
161         GArray         *analogs;             /* array of analog_infos          */
162 } config_block;
163
164 /* holds the id the configuration comes from an and
165  * an array of config_block members */
166 typedef struct {
167         guint32  fnum;          /* frame number */
168
169         guint16  id;
170         GArray  *config_blocks; /* Contains a config_block struct for
171                                  * every PMU included in the config frame */
172 } config_frame;
173
174 /* strings for type bits in SYNC */
175 static const value_string typenames[] = {
176         { 0, "Data Frame"            },
177         { 1, "Header Frame"          },
178         { 2, "Configuration Frame 1" },
179         { 3, "Configuration Frame 2" },
180         { 4, "Command Frame"         },
181         { 0, NULL                    }
182 };
183
184 /* strings for version bits in SYNC */
185 static const value_string versionnames[] = {
186         { 1, "IEEE C37.118-2005 initial publication" },
187         { 0, NULL                                    }
188 };
189
190 /* strings for the time quality flags in FRACSEC */
191 static const value_string timequalcodes[] = {
192         { 0xF, "Clock failure, time not reliable"    },
193         { 0xB, "Clock unlocked, time within 10 s"    },
194         { 0xA, "Clock unlocked, time within 1 s"     },
195         { 0x9, "Clock unlocked, time within 10^-1 s" },
196         { 0x8, "Clock unlocked, time within 10^-2 s" },
197         { 0x7, "Clock unlocked, time within 10^-3 s" },
198         { 0x6, "Clock unlocked, time within 10^-4 s" },
199         { 0x5, "Clock unlocked, time within 10^-5 s" },
200         { 0x4, "Clock unlocked, time within 10^-6 s" },
201         { 0x3, "Clock unlocked, time within 10^-7 s" },
202         { 0x2, "Clock unlocked, time within 10^-8 s" },
203         { 0x1, "Clock unlocked, time within 10^-9 s" },
204         { 0x0, "Normal operation, clock locked"      },
205         {  0 , NULL                                  }
206 };
207
208 /* strings for flags in the FORMAT word of a configuration frame */
209 static const true_false_string conf_formatb123names = {
210         "floating point",
211         "16-bit integer"
212 };
213 static const true_false_string conf_formatb0names = {
214         "polar",
215         "rectangular"
216 };
217
218 /* strings to decode ANUNIT in configuration frame */
219 static const range_string conf_anconvnames[] = {
220         {  0,   0, "single point-on-wave" },
221         {  1,   1, "rms of analog input"  },
222         {  2,   2, "peak of input"        },
223         {  3,   4, "undefined"            },
224         {  5,  64, "reserved"             },
225         { 65, 255, "user defined"         },
226         {  0,   0, NULL                   }
227 };
228
229 /* strings for the FNOM field */
230 static const true_false_string conf_fnomnames = {
231         "50Hz",
232         "60Hz"
233 };
234
235 /* strings for flags in the STAT word of a data frame */
236 static const true_false_string data_statb15names = {
237         "Data is invalid",
238         "Data is valid"
239 };
240 static const true_false_string data_statb14names = {
241         "Error",
242         "No error"
243 };
244 static const true_false_string data_statb13names = {
245         "Synchronization lost",
246         "Clock is synchronized"
247 };
248 static const true_false_string data_statb12names = {
249         "By arrival",
250         "By timestamp"
251 };
252 static const true_false_string data_statb11names = {
253         "Trigger detected",
254         "No trigger"
255 };
256 static const true_false_string data_statb10names = {
257         "Within 1 minute",
258         "No"
259 };
260 static const value_string      data_statb05to04names[] = {
261         { 0, "Time locked, best quality" },
262         { 1, "Unlocked for 10s"          },
263         { 2, "Unlocked for 100s"         },
264         { 3, "Unlocked for over 1000s"   },
265         { 0, NULL                        }
266 };
267 static const value_string      data_statb03to00names[] = {
268         { 0x0, "Manual"             },
269         { 0x1, "Magnitude low"      },
270         { 0x2, "Magnitude high"     },
271         { 0x3, "Phase-angel diff"   },
272         { 0x4, "Frequency high/low" },
273         { 0x5, "df/dt high"         },
274         { 0x6, "Reserved"           },
275         { 0x7, "Digital"            },
276         { 0x8, "User defined"       },
277         { 0x9, "User defined"       },
278         { 0xA, "User defined"       },
279         { 0xB, "User defined"       },
280         { 0xC, "User defined"       },
281         { 0xD, "User defined"       },
282         { 0xE, "User defined"       },
283         { 0xF, "User defined"       },
284         {  0 , NULL                 }
285 };
286
287 /* strings to decode the commands */
288 static const value_string command_names[] = {
289         {  0, "unknown command"       },
290         {  1, "data transmission off" },
291         {  2, "data transmission on"  },
292         {  3, "send HDR frame"        },
293         {  4, "send CFG-1 frame"      },
294         {  5, "send CFG-2 frame"      },
295         {  6, "unknown command"       },
296         {  7, "unknown command"       },
297         {  8, "extended frame"        },
298         {  9, "unknown command"       },
299         { 10, "unknown command"       },
300         { 11, "unknown command"       },
301         { 12, "unknown command"       },
302         { 13, "unknown command"       },
303         { 14, "unknown command"       },
304         { 15, "unknown command"       },
305         {  0, NULL                    }
306 };
307
308 /* Dissects a configuration frame (only the most important stuff, tries
309  * to be fast, does no GUI stuff) and returns a pointer to a config_frame
310  * struct that contains all the information from the frame needed to
311  * dissect a DATA frame.
312  *
313  * use 'config_frame_free()' to free the config_frame again
314  */
315 static config_frame* config_frame_fast(tvbuff_t *tvb)
316 {
317         guint16       idcode, num_pmu;
318         gint          offset;
319         config_frame *frame;
320
321         /* get a new frame and initialize it */
322 #if GLIB_CHECK_VERSION(2,10,0)
323         frame = g_slice_new(config_frame);
324 #else
325         frame = g_mem_chunk_alloc(frame_chunks);
326 #endif
327
328         frame->config_blocks = g_array_new(FALSE, TRUE, sizeof(config_block));
329
330         idcode = tvb_get_ntohs(tvb, 4);
331         frame->id       = idcode;
332
333         num_pmu = tvb_get_ntohs(tvb, 18);
334         offset = 20; /* start of repeating blocks */
335
336         while (num_pmu) {
337                 guint16      format_flags;
338                 gint         num_ph,
339                              num_an,
340                              num_dg;
341                 gint         i,
342                              phunit,
343                              anunit,
344                              fnom;
345                 config_block block;
346
347                 /* initialize the block */
348                 block.phasors = g_array_new(FALSE, TRUE, sizeof(phasor_info));
349                 block.analogs = g_array_new(FALSE, TRUE, sizeof(analog_info));
350                 /* copy the station name from the tvb to block, and add NULL byte */
351                 tvb_memcpy(tvb, block.name, offset, CHNAM_LEN); offset += CHNAM_LEN;
352                 block.name[CHNAM_LEN] = '\0';
353
354                 block.id = tvb_get_ntohs(tvb, offset); offset += 2;
355
356                 format_flags          = tvb_get_ntohs(tvb, offset); offset += 2;
357                 block.format_fr       = (format_flags & 0x0008) ? floating_point : integer;
358                 block.format_an       = (format_flags & 0x0004) ? floating_point : integer;
359                 block.format_ph       = (format_flags & 0x0002) ? floating_point : integer;
360                 block.phasor_notation = (format_flags & 0x0001) ? polar          : rect;
361
362                 num_ph = tvb_get_ntohs(tvb, offset); offset += 2;
363                 num_an = tvb_get_ntohs(tvb, offset); offset += 2;
364                 num_dg = tvb_get_ntohs(tvb, offset); offset += 2;
365                 block.num_dg = num_dg;
366
367                 /* the offset of the PHUNIT, ANUNIT, and FNOM blocks */
368                 phunit = offset + (num_ph + num_an + num_dg * CHNAM_LEN) * CHNAM_LEN;
369                 anunit = phunit + num_ph * 4;
370                 fnom   = anunit + num_an * 4 + num_dg * 4;
371
372                 /* read num_ph phasor names and conversation factors */
373                 for (i = 0; i != num_ph; i++) {
374                         phasor_info  pi;
375                         guint32      conv;
376
377                         /* copy the phasor name from the tvb, and add NULL byte */
378                         tvb_memcpy(tvb, pi.name, offset, CHNAM_LEN); offset += CHNAM_LEN;
379                         pi.name[CHNAM_LEN] = '\0';
380
381                         conv = tvb_get_ntohl(tvb, phunit + 4 * i);
382                         pi.unit = conv & 0xFF000000 ? A : V;
383                         pi.conv = conv & 0x00FFFFFF;
384
385                         g_array_append_val(block.phasors, pi);
386                 }
387
388                 /* read num_an analog value names and conversation factors */
389                 for (i = 0; i != num_an; i++) {
390                         analog_info ai;
391                         guint32     conv;
392
393                         /* copy the phasor name from the tvb, and add NULL byte */
394                         tvb_memcpy(tvb, ai.name, offset, CHNAM_LEN); offset += CHNAM_LEN;
395                         ai.name[CHNAM_LEN] = '\0';
396
397                         conv = tvb_get_ntohl(tvb, anunit + 4 * i);
398                         ai.conv = conv;
399
400                         g_array_append_val(block.analogs, ai);
401                 }
402
403                 /* the names for the bits in the digital status words aren't saved,
404                    there is no space to display them in the GUI anyway */
405
406                 /* save FNOM */
407                 block.fnom = tvb_get_ntohs(tvb, fnom) & 0x0001 ? 50 : 60;
408                 offset = fnom + 2;
409
410                 /* skip CFGCNT */
411                 offset += 2;
412
413                 g_array_append_val(frame->config_blocks, block);
414                 num_pmu--;
415         }
416
417         return frame;
418 }
419
420 /* Frees the memory pointed to by 'frame' and all the contained
421  * config_blocks and the data in their GArrays.
422  */
423 static void config_frame_free(config_frame *frame)
424 {
425         int i = frame->config_blocks->len;
426
427         /* free all the config_blocks this frame contains */
428         while (i--) {
429                 config_block *block;
430
431                 block = &g_array_index(frame->config_blocks, config_block, i);
432                 g_array_free(block->phasors, TRUE);
433                 g_array_free(block->analogs, TRUE);
434         }
435
436         /* free the array of config blocks itself */
437         g_array_free(frame->config_blocks, TRUE);
438
439         /* and the config_frame */
440 #if GLIB_CHECK_VERSION(2,10,0)
441         g_slice_free1(sizeof(config_frame), frame);
442 #else
443         g_mem_chunk_free(frame_chunks, frame);
444 #endif
445 }
446
447 /* called every time the user loads a capture file or starts to capture */
448 static void synphasor_init(void)
449 {
450         /* free stuff in the list from a previous run */
451         if (config_frame_list) {
452                 g_slist_foreach(config_frame_list, (GFunc) config_frame_free, NULL);
453
454                 g_slist_free(config_frame_list);
455
456                 config_frame_list = NULL;
457         }
458
459 #if ! GLIB_CHECK_VERSION(2,10,0)
460         if (frame_chunks != NULL) {
461             g_mem_chunk_destroy(frame_chunks);
462         }
463         frame_chunks = g_mem_chunk_new("Frame_Chunks",
464                                        sizeof(config_frame),
465                                        10*(sizeof(config_frame)),
466                                        G_ALLOC_AND_FREE);
467 #endif
468 }
469
470 /* the main dissection routine */
471 static void dissect_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
472
473 /* called for synchrophasors over UDP */
474 static void dissect_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
475 {
476         dissect_common(tvb, pinfo, tree);
477 }
478
479 /* callback for 'tcp_dissect_pdus()' to give it the length of the frame */
480 static guint get_pdu_length(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
481 {
482         return tvb_get_ntohs(tvb, offset + 2);
483 }
484
485 static void dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
486 {
487         tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 4, get_pdu_length, dissect_common);
488 }
489
490 /* Checks the CRC of a synchrophasor frame, 'tvb' has to include the whole
491  * frame, including CRC, the calculated CRC is returned in '*computedcrc'.
492  */
493 static gboolean check_crc(tvbuff_t *tvb, guint16 *computedcrc)
494 {
495         guint16 crc;
496         guint   len = tvb_get_ntohs(tvb, 2);
497
498         crc = tvb_get_ntohs(tvb, len - 2);
499         *computedcrc = crc16_x25_ccitt_tvb(tvb, len - 2);
500
501         if (crc == *computedcrc)
502                 return TRUE;
503
504         return FALSE;
505 }
506
507 /* forward declarations of the subdissectors for the data
508  * in the frame that is not common to all types of frames
509  */
510 static int dissect_config_frame (tvbuff_t *, proto_item *);
511 static int dissect_data_frame   (tvbuff_t *, proto_item *, packet_info *);
512 static int dissect_command_frame(tvbuff_t *, proto_item *, packet_info *);
513 /* to keep 'dissect_common()' shorter */
514 static gint dissect_header(tvbuff_t *, proto_tree *);
515
516 /* Dissects the header (common to all types of frames) and then calls
517  * one of the subdissectors (declared above) for the rest of the frame.
518  */
519 static void dissect_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
520 {
521         guint8  frame_type;
522         guint16 crc;
523         guint   tvbsize = tvb_length(tvb);
524
525         /* some heuristics */
526         if (tvbsize < 17                    /* 17 bytes = header frame with only a
527                                                NULL character, useless but valid */
528          || tvb_get_guint8(tvb, 0) != 0xAA) /* every synchrophasor frame starts with 0xAA */
529                 return;
530
531         /* write the protocol name to the info column */
532         if (check_col(pinfo->cinfo, COL_PROTOCOL))
533                 col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTOCOL_SHORT_NAME);
534
535         frame_type = tvb_get_guint8(tvb, 1) >> 4;
536
537         if (check_col(pinfo->cinfo, COL_INFO)) {
538                 col_clear(pinfo->cinfo, COL_INFO); /* clear out stuff in the info column */
539                 col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(frame_type, typenames, "invalid packet type"));
540         }
541
542         /* CFG-2 and DATA frames need special treatment during the first run:
543          * For CFG-2 frames, a 'config_frame' struct is created to hold the
544          * information necessary to decode DATA frames. A pointer to this
545          * struct is saved in the conversation and is copied to the
546          * per-packet information if a DATA frame is dissected.
547          */
548         if (!pinfo->fd->flags.visited) {
549                 if (CFG2 == frame_type &&
550                     check_crc(tvb, &crc)) {
551                         conversation_t *conversation;
552
553                         /* fill the config_frame */
554                         config_frame *frame = config_frame_fast(tvb);
555                         frame->fnum = pinfo->fd->num;
556                         /* so we can cleanup later */
557                         config_frame_list = g_slist_append(config_frame_list, frame);
558
559                         /* find a conversation, create a new if no one exists */
560                         conversation = find_or_create_conversation(pinfo);
561
562                         /* remove data from a previous CFG-2 frame, only
563                          * the most recent configuration frame is relevant */
564                         if (conversation_get_proto_data(conversation, proto_synphasor))
565                                 conversation_delete_proto_data(conversation, proto_synphasor);
566
567                         conversation_add_proto_data(conversation, proto_synphasor, frame);
568                 }
569                 else if (DATA == frame_type) {
570                         conversation_t *conversation = find_conversation(pinfo->fd->num,
571                                                                          &pinfo->src, &pinfo->dst,
572                                                                          pinfo->ptype,
573                                                                          pinfo->srcport, pinfo->destport,
574                                                                          0);
575
576                         if (conversation) {
577                                 config_frame *conf = conversation_get_proto_data(conversation, proto_synphasor);
578                                 /* no problem if 'conf' is NULL, the DATA frame dissector checks this again */
579                                 p_add_proto_data(pinfo->fd, proto_synphasor, conf);
580                         }
581                 }
582         } /* if (!visited) */
583
584         if (tree) { /* we are being asked for details */
585                 proto_tree *synphasor_tree = NULL;
586                 proto_item *temp_item      = NULL;
587                 proto_item *sub_item       = NULL;
588
589                 gint     offset;
590                 guint16  framesize;
591                 tvbuff_t *sub_tvb;
592
593                 temp_item = proto_tree_add_item(tree, proto_synphasor, tvb, 0, -1, FALSE);
594                 proto_item_append_text(temp_item, ", %s", val_to_str(frame_type, typenames,
595                                                                      ", invalid packet type"));
596
597                 /* synphasor_tree is where from now on all new elements for this protocol get added */
598                 synphasor_tree = proto_item_add_subtree(temp_item, ett_synphasor);
599
600                 framesize = dissect_header(tvb, synphasor_tree);
601                 offset = 14; /* header is 14 bytes long */
602
603                 /* check CRC, call appropriate subdissector for the rest of the frame if CRC is correct*/
604                 sub_item  = proto_tree_add_text(synphasor_tree, tvb, offset     , tvbsize - 16, "Data"     );
605                 temp_item = proto_tree_add_text(synphasor_tree, tvb, tvbsize - 2, 2           , "Checksum:");
606                 if (!check_crc(tvb, &crc)) {
607                         proto_item_append_text(sub_item,  ", not dissected because of wrong checksum");
608                         proto_item_append_text(temp_item, " 0x%04x [incorrect]", crc);
609                 }
610                 else {
611                         /* create a new tvb to pass to the subdissector
612                            '-16': length of header + 2 CRC bytes */
613                         sub_tvb = tvb_new_subset(tvb, offset, tvbsize - 16, framesize - 16);
614
615                         /* call subdissector */
616                         switch (frame_type) {
617                                 case DATA:
618                                         offset += dissect_data_frame(sub_tvb, sub_item, pinfo);
619                                         break;
620                                 case HEADER: /* no further dissection is done/needed */
621                                         proto_item_append_text(sub_item, "Header Frame");
622                                         offset += tvb_length(sub_tvb);
623                                         break;
624                                 case CFG1:
625                                 case CFG2:
626                                         offset += dissect_config_frame(sub_tvb, sub_item);
627                                         break;
628                                 case CMD:
629                                         offset += dissect_command_frame(sub_tvb, sub_item, pinfo);
630                                         break;
631
632                                 default:
633                                         proto_item_append_text(sub_item, " of unknown type");
634                         }
635                         proto_item_append_text(temp_item, " 0x%04x [correct]", crc);
636                 }
637
638                 offset += 2; /* CRC */
639         } /* if (tree) */
640 } /* dissect_synphasor() */
641
642 /* Dissects the common header of frames.
643  *
644  * Returns the framesize, in contrast to most
645  * other helper functions that return the offset.
646  */
647 static gint dissect_header(tvbuff_t *tvb, proto_tree *tree)
648 {
649         proto_tree *temp_tree;
650         proto_item *temp_item;
651
652         gint    offset = 0;
653         guint16 framesize, idcode;
654
655         /* SYNC and flags */
656         temp_item = proto_tree_add_item(tree, hf_sync, tvb, offset, 2, FALSE);
657         temp_tree = proto_item_add_subtree(temp_item, ett_frtype);
658                 proto_tree_add_item(temp_tree, hf_sync_frtype,  tvb, offset, 2, FALSE);
659                 proto_tree_add_item(temp_tree, hf_sync_version, tvb, offset, 2, FALSE);
660         offset += 2;
661
662         /* FRAMESIZE */
663         proto_tree_add_item(tree, hf_frsize, tvb, offset, 2, FALSE);
664         framesize = tvb_get_ntohs(tvb, offset); offset += 2;
665
666         /* IDCODE */
667         proto_tree_add_item(tree, hf_idcode, tvb, offset, 2, FALSE);
668         idcode = tvb_get_ntohs(tvb, offset); offset += 2;
669
670         /* SOC */
671         {
672                 /* can't use 'proto_tree_add_time()' because we need UTC */
673                 char   buf[20];
674                 struct tm* t;
675                 time_t soc = tvb_get_ntohl(tvb, offset);
676                 t = gmtime(&soc);
677                 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", t);
678                 proto_tree_add_string(tree, hf_soc, tvb, offset, 4, buf);
679                 offset += 4;
680         }
681
682         /* FRACSEC */
683         /* time quality flags */
684         temp_item = proto_tree_add_text(tree, tvb, offset, 1, "Time quality flags");
685         temp_tree = proto_item_add_subtree(temp_item, ett_timequal);
686                 proto_tree_add_item(temp_tree, hf_timeqal_lsdir,         tvb, offset, 1, FALSE);
687                 proto_tree_add_item(temp_tree, hf_timeqal_lsocc,         tvb, offset, 1, FALSE);
688                 proto_tree_add_item(temp_tree, hf_timeqal_lspend,        tvb, offset, 1, FALSE);
689                 proto_tree_add_item(temp_tree, hf_timeqal_timequalindic, tvb, offset, 1, FALSE);
690         offset += 1;
691
692         proto_tree_add_item(tree, hf_fracsec,  tvb, offset, 3, FALSE); offset += 3;
693
694         return framesize;
695 }
696
697 /* forward declarations of helper functions for 'dissect_config_frame()' */
698 static gint dissect_CHNAM  (tvbuff_t *tvb, proto_tree *tree, gint offset, gint cnt, char *prefix);
699 static gint dissect_PHUNIT (tvbuff_t *tvb, proto_tree *tree, gint offset, gint cnt);
700 static gint dissect_ANUNIT (tvbuff_t *tvb, proto_tree *tree, gint offset, gint cnt);
701 static gint dissect_DIGUNIT(tvbuff_t *tvb, proto_tree *tree, gint offset, gint cnt);
702
703 /* dissects a configuration frame (type 1 and 2) and adds fields to 'config_item' */
704 static int dissect_config_frame(tvbuff_t *tvb, proto_item *config_item)
705 {
706         proto_tree *config_tree = NULL;
707         proto_item *temp_item   = NULL;
708         proto_tree *temp_tree   = NULL;
709         gint     offset = 0, j;
710         guint16  num_pmu;
711
712         proto_item_set_text   (config_item, "Configuration data");
713         config_tree = proto_item_add_subtree(config_item, ett_conf);
714
715         /* TIME_BASE and NUM_PMU */
716         offset += 1; /* skip the reserved byte */
717         proto_tree_add_item(config_tree, hf_conf_timebase, tvb, offset, 3, FALSE); offset += 3;
718         proto_tree_add_item(config_tree, hf_conf_numpmu,   tvb, offset, 2, FALSE);
719         /* add number of included PMUs to the text in the list view  */
720         num_pmu = tvb_get_ntohs(tvb, offset); offset += 2;
721         proto_item_append_text(config_item, ", %"G_GUINT16_FORMAT" PMU(s) included", num_pmu);
722
723         /* dissect the repeating PMU blocks */
724         for (j = 0; j < num_pmu; j++) {
725                 guint16  num_ph, num_an, num_dg;
726                 proto_item *station_item = NULL;
727                 proto_tree *station_tree = NULL;
728                 char *str;
729
730                 gint oldoffset = offset; /* to calculate the length of the whole PMU block later */
731
732                 /* STN with new tree to add the rest of the PMU block */
733                 str = tvb_get_ephemeral_string(tvb, offset, CHNAM_LEN);
734                 station_item = proto_tree_add_text(config_tree, tvb, offset, CHNAM_LEN, "Station #%i: \"%s\"", j + 1, str);
735                 station_tree = proto_item_add_subtree(station_item, ett_conf_station);
736                 offset += CHNAM_LEN;
737
738                 /* IDCODE */
739                 proto_tree_add_item(station_tree, hf_idcode, tvb, offset, 2, FALSE); offset += 2;
740
741                 /* FORMAT */
742                 temp_item = proto_tree_add_text(station_tree, tvb, offset, 2, "Data format in data frame");
743                 temp_tree = proto_item_add_subtree(temp_item, ett_conf_format);
744                         proto_tree_add_item(temp_tree, hf_conf_formatb3, tvb, offset, 2, FALSE);
745                         proto_tree_add_item(temp_tree, hf_conf_formatb2, tvb, offset, 2, FALSE);
746                         proto_tree_add_item(temp_tree, hf_conf_formatb1, tvb, offset, 2, FALSE);
747                         proto_tree_add_item(temp_tree, hf_conf_formatb0, tvb, offset, 2, FALSE);
748                 offset += 2;
749
750                 /* PHNMR, ANNMR, DGNMR */
751                 num_ph = tvb_get_ntohs(tvb, offset    );
752                 num_an = tvb_get_ntohs(tvb, offset + 2);
753                 num_dg = tvb_get_ntohs(tvb, offset + 4);
754                 proto_tree_add_text(station_tree, tvb, offset    , 2, "Number of phasors: %u",              num_ph);
755                 proto_tree_add_text(station_tree, tvb, offset + 2, 2, "Number of analog values: %u",        num_an);
756                 proto_tree_add_text(station_tree, tvb, offset + 4, 2, "Number of digital status words: %u", num_dg);
757                 offset += 6;
758
759                 /* CHNAM, the channel names */
760                 offset = dissect_CHNAM(tvb, station_tree, offset, num_ph     , "Phasor name"         );
761                 offset = dissect_CHNAM(tvb, station_tree, offset, num_an     , "Analog value"        );
762                 offset = dissect_CHNAM(tvb, station_tree, offset, num_dg * 16, "Digital status label");
763
764                 /* PHUNIT, ANUINT and DIGUNIT */
765                 offset = dissect_PHUNIT (tvb, station_tree, offset, num_ph);
766                 offset = dissect_ANUNIT (tvb, station_tree, offset, num_an);
767                 offset = dissect_DIGUNIT(tvb, station_tree, offset, num_dg);
768
769                 /* FNOM and CFGCNT */
770                 proto_tree_add_item(station_tree, hf_conf_fnom,   tvb, offset, 2, FALSE); offset += 2;
771                 proto_tree_add_item(station_tree, hf_conf_cfgcnt, tvb, offset, 2, FALSE); offset += 2;
772
773                 /* set the correct length for the "Station :" item */
774                 proto_item_set_len(station_item, offset - oldoffset);
775         } /* for() PMU blocks */
776
777         /* DATA_RATE */
778         {
779                 gint16 tmp = tvb_get_ntohs(tvb, offset);
780                 temp_item  = proto_tree_add_text(config_tree, tvb, offset, 2, "Rate of transmission: "); offset += 2;
781                 if (tmp > 0)
782                         proto_item_append_text(temp_item, "%"G_GINT16_FORMAT" frame(s) per second", tmp);
783                 else
784                         proto_item_append_text(temp_item, "1 frame per %"G_GINT16_FORMAT" second(s)", (gint16)-tmp);
785         }
786
787         return offset;
788 } /* dissect_config_frame() */
789
790 /* forward declarations of helper functions for 'dissect_data_frame()' */
791 static gint dissect_PHASORS(tvbuff_t *tvb, proto_tree *tree, config_block *block, gint offset);
792 static gint dissect_DFREQ  (tvbuff_t *tvb, proto_tree *tree, config_block *block, gint offset);
793 static gint dissect_ANALOG (tvbuff_t *tvb, proto_tree *tree, config_block *block, gint offset);
794 static gint dissect_DIGITAL(tvbuff_t *tvb, proto_tree *tree, config_block *block, gint offset);
795 /* calculates the size (in bytes) of a data frame that the config_block describes */
796 #define BLOCKSIZE(x) (2                                                    /* STAT    */ \
797                    + (x).phasors->len * (integer == (x).format_ph ? 4 : 8) /* PHASORS */ \
798                    +                    (integer == (x).format_fr ? 4 : 8) /* (D)FREQ */ \
799                    + (x).analogs->len * (integer == (x).format_an ? 2 : 4) /* ANALOG  */ \
800                    + (x).num_dg * 2)                                       /* DIGITAL */
801
802 /* Dissects a data frame */
803 static int dissect_data_frame(tvbuff_t    *tvb,
804                               proto_item  *data_item, /* all items are placed beneath this item   */
805                               packet_info *pinfo)     /* used to find the data from a CFG-2 frame */
806 {
807         proto_tree   *data_tree  = NULL;
808         gint          offset     = 0;
809         guint         i;
810         config_frame *conf;
811
812         proto_item_set_text(data_item, "Measurement data");
813         data_tree = proto_item_add_subtree(data_item, ett_data);
814
815         /* search for configuration information to dissect the frame */
816         {
817                 gboolean config_found = FALSE;
818                 conf = p_get_proto_data(pinfo->fd, proto_synphasor);
819
820                 if (conf) {
821                         /* check if the size of the current frame is the
822                            size of the frame the config_frame describes */
823                         size_t reported_size = 0;
824                         for (i = 0; i < conf->config_blocks->len; i++) {
825                                 config_block *block = &g_array_index(conf->config_blocks, config_block, i);
826                                 reported_size += BLOCKSIZE(*block);
827                         }
828
829                         if (tvb_length(tvb) == reported_size) {
830                                 proto_item_append_text(data_item, ", using frame number %"G_GUINT32_FORMAT" as configuration frame",
831                                                        conf->fnum);
832                                 config_found = TRUE;
833                         }
834                 }
835
836                 if (!config_found) {
837                         proto_item_append_text(data_item, ", no configuration frame found");
838                         return 0;
839                 }
840         }
841
842         /* dissect a PMU block for every config_block in the frame */
843         for (i = 0; i < conf->config_blocks->len; i++) {
844                 config_block *block = &g_array_index(conf->config_blocks, config_block, i);
845
846                 proto_item *block_item = proto_tree_add_text(data_tree, tvb, offset, BLOCKSIZE(*block),
847                                                  "Station: \"%s\"", block->name);
848                 proto_tree *block_tree = proto_item_add_subtree(block_item, ett_data_block);
849
850                 /* STAT */
851                 proto_item *temp_item = proto_tree_add_text(block_tree, tvb, offset, 2, "Flags");
852                 proto_tree *temp_tree = proto_item_add_subtree(temp_item, ett_data_stat);
853                         proto_tree_add_item(temp_tree, hf_data_statb15,     tvb, offset, 2, FALSE);
854                         proto_tree_add_item(temp_tree, hf_data_statb14,     tvb, offset, 2, FALSE);
855                         proto_tree_add_item(temp_tree, hf_data_statb13,     tvb, offset, 2, FALSE);
856                         proto_tree_add_item(temp_tree, hf_data_statb12,     tvb, offset, 2, FALSE);
857                         proto_tree_add_item(temp_tree, hf_data_statb11,     tvb, offset, 2, FALSE);
858                         proto_tree_add_item(temp_tree, hf_data_statb10,     tvb, offset, 2, FALSE);
859                         proto_tree_add_item(temp_tree, hf_data_statb05to04, tvb, offset, 2, FALSE);
860                         proto_tree_add_item(temp_tree, hf_data_statb03to00, tvb, offset, 2, FALSE);
861                 offset += 2;
862
863                 /* PHASORS, (D)FREQ, ANALOG, and DIGITAL */
864                 offset = dissect_PHASORS(tvb, block_item, block, offset);
865                 offset = dissect_DFREQ  (tvb, block_item, block, offset);
866                 offset = dissect_ANALOG (tvb, block_item, block, offset);
867                 offset = dissect_DIGITAL(tvb, block_item, block, offset);
868         }
869         return offset;
870 } /* dissect_data_frame() */
871
872 /* Dissects a command frame and adds fields to config_item.
873  *
874  * 'pinfo' is used to add the type of command
875  * to the INFO column in the packet list.
876  */
877 static int dissect_command_frame(tvbuff_t    *tvb,
878                                  proto_item  *command_item,
879                                  packet_info *pinfo)
880 {
881         proto_tree *command_tree  = NULL;
882         guint       tvbsize       = tvb_length(tvb);
883
884         proto_item_set_text(command_item, "Command data");
885         command_tree = proto_item_add_subtree(command_item, ett_command);
886
887         /* CMD */
888         proto_tree_add_item(command_tree, hf_command, tvb, 0, 2, FALSE);
889         if (check_col(pinfo->cinfo, COL_INFO)) {
890                 const char *s = val_to_str(tvb_get_ntohs(tvb, 0), command_names, "invalid command");
891                 col_append_str(pinfo->cinfo, COL_INFO, ", ");
892                 col_append_str(pinfo->cinfo, COL_INFO, s);
893         }
894
895         if (tvbsize > 2) {
896                 if (tvb_get_ntohs(tvb, 0) == 0x0008) {
897                         /* Command: Extended Frame, the extra data is ok */
898                                 proto_item* i = proto_tree_add_text(command_tree, tvb, 2, tvbsize - 2, "Extended frame data");
899                                 if (tvbsize % 2)
900                                         proto_item_append_text(i, ", but size not multiple of 16-bit word");
901                 }
902                 else
903                         proto_tree_add_text(command_tree, tvb, 2, tvbsize - 2, "Unknown data");
904         }
905
906         return tvbsize;
907 } /* dissect_command_frame() */
908
909 /****************************************************************/
910 /* after this line: helper functions for 'dissect_data_frame()' */
911 /****************************************************************/
912
913 /* Dissects a single phasor for 'dissect_PHASORS()' */
914 static int dissect_single_phasor(tvbuff_t *tvb, int offset,
915                                         double* mag, double* phase, /* returns the resulting values here */
916                                         data_format     format,     /* information needed to... */
917                                         phasor_notation notation)   /*   ...dissect the phasor  */
918 {
919         if (floating_point == format) {
920                 if (polar == notation) {
921                         /* float, polar */
922                         *mag   = tvb_get_ntohieee_float(tvb, offset    );
923                         *phase = tvb_get_ntohieee_float(tvb, offset + 4);
924                 }
925                 else {
926                         /* float, rect */
927                         gfloat real, imag;
928                         real = tvb_get_ntohieee_float(tvb, offset    );
929                         imag = tvb_get_ntohieee_float(tvb, offset + 4);
930
931                         *mag   = sqrt(pow(real, 2) + pow(imag, 2));
932                         *phase = atan2(imag, real);
933                 }
934         }
935         else {
936                 if (polar == notation) {
937                         /* int, polar */
938                         *mag    = (guint16)tvb_get_ntohs(tvb, offset    );
939                         *phase  = (gint16) tvb_get_ntohs(tvb, offset + 2);
940                         *phase /= 10000.0; /* angle is in radians*10^4 */
941                 }
942                 else {
943                         /* int, rect */
944                         gint16 real, imag;
945                         real = tvb_get_ntohs(tvb, offset    );
946                         imag = tvb_get_ntohs(tvb, offset + 2);
947
948                         *mag   = sqrt(pow(real, 2) + pow(imag, 2));
949                         *phase = atan2(imag, real);
950                 }
951         }
952
953         return floating_point == format ? 8 : 4;
954 }
955
956 /* used by 'dissect_data_frame()' to dissect the PHASORS field */
957 static gint dissect_PHASORS(tvbuff_t *tvb, proto_tree *tree, config_block *block, gint offset)
958 {
959         proto_item *temp_item   = NULL;
960         proto_tree *phasor_tree = NULL;
961         guint       length;
962         gint        j,
963                     cnt = block->phasors->len; /* number of phasors to dissect */
964
965         if (0 == cnt)
966                 return offset;
967
968         length      = block->phasors->len * (floating_point == block->format_ph ? 8 : 4);
969         temp_item   = proto_tree_add_text(tree, tvb, offset, length, "Phasors (%u)", cnt);
970         phasor_tree = proto_item_add_subtree(temp_item, ett_data_phasors);
971
972         /* dissect a phasor for every phasor_info saved in the config_block */
973         for (j = 0; j < cnt; j++) {
974                 double       mag, phase;
975                 phasor_info *pi;
976
977                 pi = &g_array_index(block->phasors, phasor_info, j);
978                 temp_item = proto_tree_add_text(phasor_tree, tvb, offset,
979                                                 floating_point == block->format_ph ? 8 : 4,
980                                                 "Phasor #%u: \"%s\"", j + 1, pi->name);
981
982                 offset += dissect_single_phasor(tvb, offset,
983                                                 &mag, &phase,
984                                                 block->format_ph,
985                                                 block->phasor_notation);
986
987                 /* for values in integer format, apply conversation factor */
988                 if (integer == block->format_ph)
989                         mag = (mag * pi->conv) * 0.00001;
990
991                 #define ANGLE  "/_"
992                 #define DEGREE "\xC2\xB0" /* DEGREE signs in UTF-8 */
993
994                 proto_item_append_text(temp_item, ", %10.2f%c" ANGLE "%7.2f" DEGREE,
995                                                   mag,
996                                                   V == pi->unit ? 'V' : 'A',
997                                                   phase *180.0/G_PI);
998                 #undef ANGLE
999                 #undef DEGREE
1000         }
1001         return offset;
1002 }
1003
1004 /* used by 'dissect_data_frame()' to dissect the FREQ and DFREQ fields */
1005 static gint dissect_DFREQ(tvbuff_t *tvb, proto_tree *tree, config_block *block, gint offset)
1006 {
1007         if (floating_point == block->format_fr) {
1008                 gfloat tmp;
1009
1010                 tmp = tvb_get_ntohieee_float(tvb, offset);
1011                 proto_tree_add_text(tree, tvb, offset, 4, "Actual frequency value: %fHz", tmp); offset += 4;
1012
1013                 /* The standard doesn't clearly say how to interpret this value, but
1014                  * http://www.pes-psrc.org/h/C37_118_H11_FAQ_Jan2008.pdf provides further information.
1015                  * --> no scaling factor is applied to DFREQ
1016                  */
1017                 tmp = tvb_get_ntohieee_float(tvb, offset);
1018                 proto_tree_add_text(tree, tvb, offset, 4, "Rate of change of frequency: %fHz/s", tmp); offset += 4;
1019         }
1020         else {
1021                 gint16 tmp;
1022
1023                 tmp = tvb_get_ntohs(tvb, offset);
1024                 proto_tree_add_text(tree, tvb, offset, 2,
1025                                     "Frequency deviation from nominal: %" G_GINT16_FORMAT "mHz (actual frequency: %.3fHz)",
1026                                     tmp, block->fnom + (tmp / 1000.0));
1027                 offset += 2;
1028
1029                 tmp = tvb_get_ntohs(tvb, offset);
1030                 proto_tree_add_text(tree, tvb, offset, 2, "Rate of change of frequency: %.3fHz/s", tmp / 100.0); offset += 2;
1031         }
1032         return offset;
1033 }
1034
1035 /* used by 'dissect_data_frame()' to dissect the ANALOG field */
1036 static gint dissect_ANALOG(tvbuff_t *tvb, proto_tree *tree, config_block *block, gint offset)
1037 {
1038         proto_tree *analog_tree = NULL;
1039         proto_item *temp_item   = NULL;
1040         guint       length;
1041         gint        j,
1042                     cnt = block->analogs->len; /* number of analog values to dissect */
1043
1044         if (0 == cnt)
1045                 return offset;
1046
1047         length      = block->analogs->len * (floating_point == block->format_an ? 4 : 2);
1048         temp_item   = proto_tree_add_text(tree, tvb, offset, length, "Analog values (%u)", cnt);
1049
1050         analog_tree = proto_item_add_subtree(temp_item, ett_data_analog);
1051
1052         for (j = 0; j < cnt; j++) {
1053                 analog_info *ai = &g_array_index(block->analogs, analog_info, j);
1054
1055                 temp_item = proto_tree_add_text(analog_tree, tvb, offset,
1056                                                 floating_point == block->format_an ? 4 : 2,
1057                                                 "Analog value #%u: \"%s\"", j + 1, ai->name);
1058
1059                 if (floating_point == block->format_an) {
1060                         gfloat tmp = tvb_get_ntohieee_float(tvb, offset); offset += 4;
1061                         proto_item_append_text(temp_item, ", %.3f", tmp);
1062                 }
1063                 else {
1064                         /* the "standard" doesn't say if this is signed or unsigned,
1065                          * so I just use gint16, the scaling of the conversation factor
1066                          * is also "user defined", so I just write it after the analog value */
1067                         gint16 tmp = tvb_get_ntohs(tvb, offset); offset += 2;
1068                         proto_item_append_text(temp_item, ", %" G_GINT16_FORMAT " (conversation factor: %#06x)",
1069                                                tmp, ai->conv);
1070                 }
1071         }
1072         return offset;
1073 }
1074
1075 /* used by 'dissect_data_frame()' to dissect the DIGITAL field */
1076 static gint dissect_DIGITAL(tvbuff_t *tvb, proto_tree *tree, config_block *block, gint offset)
1077 {
1078         proto_item *digital_item = NULL;
1079         gint        j,
1080                     cnt = block->num_dg; /* number of digital status words to dissect */
1081
1082         if (0 == cnt)
1083                 return offset;
1084
1085         digital_item = proto_tree_add_text(tree, tvb, offset, cnt * 2, "Digital status words (%u)", cnt);
1086         tree         = proto_item_add_subtree(digital_item, ett_data_digital);
1087
1088         for (j = 0; j < cnt; j++) {
1089                 guint16 tmp = tvb_get_ntohs(tvb, offset);
1090                 proto_tree_add_text(tree, tvb, offset, 2, "Digital status word #%u: 0x%04x", j + 1, tmp);
1091                 offset += 2;
1092         }
1093         return offset;
1094 }
1095
1096 /*******************************************************************/
1097 /* after this line:  helper functions for 'dissect_config_frame()' */
1098 /*******************************************************************/
1099
1100 /* used by 'dissect_config_frame()' to dissect the PHUNIT field */
1101 static gint dissect_PHUNIT(tvbuff_t *tvb, proto_tree *tree, gint offset, gint cnt)
1102 {
1103         proto_item *temp_item = NULL;
1104         proto_tree *temp_tree = NULL;
1105         int i;
1106
1107         if (0 == cnt)
1108                 return offset;
1109
1110         temp_item = proto_tree_add_text(tree, tvb, offset, 4 * cnt, "Phasor conversation factors (%u)", cnt);
1111         temp_tree = proto_item_add_subtree(temp_item, ett_conf_phconv);
1112
1113         /* Conversion factor for phasor channels. Four bytes for each phasor.
1114          * MSB:           0 = voltage, 1 = current
1115          * Lower 3 Bytes: unsigned 24-bit word in 10^-5 V or A per bit to scale the phasor value
1116          */
1117         for (i = 0; i < cnt; i++) {
1118                 guint32 tmp = tvb_get_ntohl(tvb, offset);
1119                 proto_tree_add_text(temp_tree, tvb, offset, 4,
1120                                     "#%u factor: %u * 10^-5, unit: %s",
1121                                     i + 1,
1122                                     tmp & 0x00FFFFFF,
1123                                     tmp & 0xFF000000 ? "Ampere" : "Volt");
1124                 offset += 4;
1125         }
1126
1127         return offset;
1128 }
1129
1130 /* used by 'dissect_config_frame()' to dissect the ANUNIT field */
1131 static gint dissect_ANUNIT(tvbuff_t *tvb, proto_tree *tree, gint offset, gint cnt)
1132 {
1133         proto_item *temp_item = NULL;
1134         proto_tree *temp_tree = NULL;
1135         int i;
1136
1137         if (0 == cnt)
1138                 return offset;
1139
1140         temp_item = proto_tree_add_text(tree, tvb, offset, 4 * cnt, "Analog values conversation factors (%u)", cnt);
1141         temp_tree = proto_item_add_subtree(temp_item, ett_conf_anconv);
1142
1143         /* Conversation factor for analog channels. Four bytes for each analog value.
1144          * MSB: see 'synphasor_conf_anconvnames' in 'synphasor_strings.c'
1145          * Lower 3 Bytes: signed 24-bit word, user-defined scaling
1146          */
1147         for (i = 0; i < cnt; i++) {
1148                 gint32 tmp = tvb_get_ntohl(tvb, offset);
1149                 temp_item = proto_tree_add_text(temp_tree, tvb, offset, 4,
1150                                                 "Factor for analog value #%i: %s",
1151                                                 i + 1,
1152                                                 match_strrval((tmp >> 24) & 0x000000FF, conf_anconvnames));
1153
1154                         tmp &= 0x00FFFFFF;
1155                 if (    tmp &  0x00800000) /* sign bit set */
1156                         tmp |= 0xFF000000;
1157
1158                 proto_item_append_text(temp_item, ", value: %" G_GINT32_FORMAT, tmp);
1159
1160                 offset += 4;
1161         }
1162
1163         return offset;
1164 }
1165
1166 /* used by 'dissect_config_frame()' to dissect the DIGUNIT field */
1167 static gint dissect_DIGUNIT(tvbuff_t *tvb, proto_tree *tree, gint offset, gint cnt)
1168 {
1169         proto_item *temp_item = NULL;
1170         proto_tree *temp_tree = NULL;
1171         int i;
1172
1173         if (0 == cnt)
1174                 return offset;
1175
1176         temp_item = proto_tree_add_text(tree, tvb, offset, 4 * cnt, "Masks for digital status words (%u)", cnt);
1177         temp_tree = proto_item_add_subtree(temp_item, ett_conf_dgmask);
1178
1179         /* Mask words for digital status words. Two 16-bit words for each digital word. The first
1180          * inidcates the normal status of the inputs, the second indicated the valid bits in
1181          * the status word
1182          */
1183         for (i = 0; i < cnt; i++) {
1184                 guint32 tmp = tvb_get_ntohl(tvb, offset);
1185
1186                 temp_item = proto_tree_add_text(temp_tree, tvb, offset, 4, "Mask for status word #%u: ", i + 1);
1187                 proto_item_append_text(temp_item, "normal state: 0x%04"G_GINT16_MODIFIER"x", (guint16)((tmp & 0xFFFF0000) >> 16));
1188                 proto_item_append_text(temp_item, ", valid bits: 0x%04"G_GINT16_MODIFIER"x", (guint16)( tmp & 0x0000FFFF));
1189
1190                 offset += 4;
1191         }
1192
1193         return offset;
1194 }
1195
1196 /* used by 'dissect_config_frame()' to dissect the "channel name"-fields */
1197 static gint dissect_CHNAM(tvbuff_t *tvb, proto_tree *tree, gint offset, gint cnt, char *prefix)
1198 {
1199         proto_item *temp_item = NULL;
1200         proto_tree *temp_tree = NULL;
1201         int i;
1202
1203         if (0 == cnt)
1204                 return offset;
1205
1206         temp_item = proto_tree_add_text(tree, tvb, offset, CHNAM_LEN * cnt, "%ss (%u)", prefix, cnt);
1207         temp_tree = proto_item_add_subtree(temp_item, ett_conf_phnam);
1208
1209         /* dissect the 'cnt' channel names */
1210         for (i = 0; i < cnt; i++) {
1211                 char *str;
1212                 str = tvb_get_ephemeral_string(tvb, offset, CHNAM_LEN);
1213                 proto_tree_add_text(temp_tree, tvb, offset, CHNAM_LEN,
1214                                     "%s #%i: \"%s\"", prefix, i+1, str);
1215                 offset += CHNAM_LEN;
1216         }
1217
1218         return offset;
1219 }
1220
1221 void proto_register_synphasor(void)
1222 {
1223         static hf_register_info hf[] = {
1224                 /* Sync word */
1225                 { &hf_sync,
1226                 { "Synchronization word", PROTOCOL_ABBREV ".sync", FT_UINT16, BASE_HEX,
1227                   NULL, 0x0, NULL, HFILL }},
1228                         /* Flags in the Sync word */
1229                         { &hf_sync_frtype,
1230                         { "Frame Type", PROTOCOL_ABBREV ".frtype", FT_UINT16, BASE_HEX,
1231                           VALS(typenames), 0x0070, NULL, HFILL }},
1232
1233                         { &hf_sync_version,
1234                         { "Version",    PROTOCOL_ABBREV ".version", FT_UINT16, BASE_DEC,
1235                           VALS(versionnames), 0x000F, NULL, HFILL }},
1236
1237                 { &hf_frsize,
1238                 { "Framesize", PROTOCOL_ABBREV ".frsize", FT_UINT16, BASE_DEC,
1239                   NULL, 0x0, NULL, HFILL }},
1240
1241                 { &hf_idcode,
1242                 { "PMU/DC ID number", PROTOCOL_ABBREV ".idcode", FT_UINT16, BASE_DEC,
1243                   NULL, 0x0, NULL, HFILL }},
1244
1245                 { &hf_soc,
1246                 { "SOC time stamp (UTC)", PROTOCOL_ABBREV ".soc", FT_STRINGZ, BASE_NONE,
1247                   NULL, 0x0, NULL, HFILL }},
1248
1249                 /* Time quality flags in fracsec */
1250                 { &hf_timeqal_lsdir,
1251                 { "Leap second direction", PROTOCOL_ABBREV ".timeqal.lsdir", FT_BOOLEAN, 8,
1252                   NULL, 0x40, NULL, HFILL }},
1253
1254                 { &hf_timeqal_lsocc,
1255                 { "Leap second occurred", PROTOCOL_ABBREV ".timeqal.lsocc", FT_BOOLEAN, 8,
1256                   NULL, 0x20, NULL, HFILL }},
1257
1258                 { &hf_timeqal_lspend,
1259                 { "Leap second pending", PROTOCOL_ABBREV ".timeqal.lspend", FT_BOOLEAN, 8,
1260                   NULL, 0x10, NULL, HFILL }},
1261
1262                 { &hf_timeqal_timequalindic,
1263                 { "Time Quality indicator code", PROTOCOL_ABBREV ".timeqal.timequalindic", FT_UINT8, BASE_HEX,
1264                   VALS(timequalcodes), 0x0F, NULL, HFILL }},
1265
1266                 /* Fraction of second */
1267                 { &hf_fracsec,
1268                 { "Fraction of second (raw)", PROTOCOL_ABBREV ".fracsec", FT_UINT24, BASE_DEC,
1269                   NULL, 0x0, NULL, HFILL }},
1270
1271         /* Data types for configuration frames */
1272                 { &hf_conf_timebase,
1273                 { "Resolution of fractional second time stamp", PROTOCOL_ABBREV ".conf.timebase", FT_UINT24, BASE_DEC,
1274                   NULL, 0x0, NULL, HFILL }},
1275
1276                 { &hf_conf_numpmu,
1277                 { "Number of PMU blocks included in the frame", PROTOCOL_ABBREV ".conf.numpmu", FT_UINT16, BASE_DEC,
1278                   NULL, 0x0, NULL, HFILL }},
1279
1280                 /* Bits in the FORMAT word */
1281                 { &hf_conf_formatb3,
1282                 { "FREQ/DFREQ format", PROTOCOL_ABBREV ".conf.dfreq_format", FT_BOOLEAN, 16,
1283                   TFS(&conf_formatb123names), 0x8, NULL, HFILL }},
1284
1285                 { &hf_conf_formatb2,
1286                 { "Analog values format", PROTOCOL_ABBREV ".conf.analog_format", FT_BOOLEAN, 16,
1287                   TFS(&conf_formatb123names), 0x4, NULL, HFILL }},
1288
1289                 { &hf_conf_formatb1,
1290                 { "Phasor format", PROTOCOL_ABBREV ".conf.phasor_format", FT_BOOLEAN, 16,
1291                   TFS(&conf_formatb123names), 0x2, NULL, HFILL }},
1292
1293                 { &hf_conf_formatb0,
1294                 { "Phasor notation", PROTOCOL_ABBREV ".conf.phasor_notation", FT_BOOLEAN, 16,
1295                   TFS(&conf_formatb0names), 0x1, NULL, HFILL }},
1296
1297                 { &hf_conf_fnom,
1298                 { "Nominal line freqency", PROTOCOL_ABBREV ".conf.fnom", FT_BOOLEAN, 16,
1299                   TFS(&conf_fnomnames), 0x0001, NULL, HFILL }},
1300
1301                 { &hf_conf_cfgcnt,
1302                 { "Configuration change count", PROTOCOL_ABBREV ".conf.cfgcnt", FT_UINT16, BASE_DEC,
1303                   NULL, 0, NULL, HFILL }},
1304
1305         /* Data types for data frames */
1306                 /* Flags in the STAT word */
1307                 { &hf_data_statb15,
1308                 { "Data valid", PROTOCOL_ABBREV ".data.valid", FT_BOOLEAN, 16,
1309                   TFS(&data_statb15names), 0x8000, NULL, HFILL }},
1310
1311                 { &hf_data_statb14,
1312                 { "PMU error", PROTOCOL_ABBREV ".data.PMUerror", FT_BOOLEAN, 16,
1313                   TFS(&data_statb14names), 0x4000, NULL, HFILL }},
1314
1315                 { &hf_data_statb13,
1316                 { "Time synchronized", PROTOCOL_ABBREV ".data.sync", FT_BOOLEAN, 16,
1317                   TFS(&data_statb13names), 0x2000, NULL, HFILL }},
1318
1319                 { &hf_data_statb12,
1320                 { "Data sorting", PROTOCOL_ABBREV ".data.sorting", FT_BOOLEAN, 16,
1321                   TFS(&data_statb12names), 0x1000, NULL, HFILL }},
1322
1323                 { &hf_data_statb11,
1324                 { "Trigger detected", PROTOCOL_ABBREV ".data.trigger", FT_BOOLEAN, 16,
1325                   TFS(&data_statb11names), 0x0800, NULL, HFILL }},
1326
1327                 { &hf_data_statb10,
1328                 { "Configuration changed", PROTOCOL_ABBREV ".data.CFGchange", FT_BOOLEAN, 16,
1329                   TFS(&data_statb10names), 0x0400, NULL, HFILL }},
1330
1331                 { &hf_data_statb05to04,
1332                 { "Unlocked time", PROTOCOL_ABBREV  ".data.t_unlock", FT_UINT16, BASE_HEX,
1333                   VALS(&data_statb05to04names), 0x0030, NULL, HFILL }},
1334
1335                 { &hf_data_statb03to00,
1336                 { "Trigger reason", PROTOCOL_ABBREV  ".data.trigger_reason", FT_UINT16, BASE_HEX,
1337                   VALS(&data_statb03to00names), 0x000F, NULL, HFILL }},
1338
1339         /* Data type for command frame */
1340                 { &hf_command,
1341                 { "Command", PROTOCOL_ABBREV ".command", FT_UINT16, BASE_HEX,
1342                   VALS(command_names), 0x000F, NULL, HFILL }}
1343         };
1344
1345         /* protocol subtree array */
1346         static gint *ett[] = {
1347                 &ett_synphasor,
1348                 &ett_frtype,
1349                 &ett_timequal,
1350                 &ett_conf,
1351                 &ett_conf_station,
1352                 &ett_conf_format,
1353                 &ett_conf_phnam,
1354                 &ett_conf_annam,
1355                 &ett_conf_dgnam,
1356                 &ett_conf_phconv,
1357                 &ett_conf_anconv,
1358                 &ett_conf_dgmask,
1359                 &ett_data,
1360                 &ett_data_block,
1361                 &ett_data_stat,
1362                 &ett_data_phasors,
1363                 &ett_data_analog,
1364                 &ett_data_digital,
1365                 &ett_command
1366         };
1367
1368         module_t *synphasor_module;
1369
1370         /* register protocol */
1371         proto_synphasor = proto_register_protocol(PROTOCOL_NAME,
1372                                                   PROTOCOL_SHORT_NAME,
1373                                                   PROTOCOL_ABBREV);
1374
1375         proto_register_field_array(proto_synphasor, hf, array_length(hf));
1376         proto_register_subtree_array(ett, array_length(ett));
1377
1378         /* register preferences */
1379         synphasor_module = prefs_register_protocol(proto_synphasor, proto_reg_handoff_synphasor);
1380
1381         /* the port numbers of the lower level protocols */
1382         prefs_register_uint_preference(synphasor_module, "udp_port", "Synchrophasor UDP port",
1383                                        "Set the port number for synchrophasor frames over UDP" \
1384                                        "(if other than the default of 4713)",
1385                                        10, &global_pref_udp_port);
1386         prefs_register_uint_preference(synphasor_module, "tcp_port", "Synchrophasor TCP port",
1387                                        "Set the port number for synchrophasor frames over TCP" \
1388                                        "(if other than the default of 4712)",
1389                                        10, &global_pref_tcp_port);
1390
1391         /* register the initalization routine */
1392         register_init_routine(&synphasor_init);
1393 } /* proto_register_synphasor() */
1394
1395 /* called at startup and when the preferences change */
1396 void proto_reg_handoff_synphasor(void)
1397 {
1398         static gboolean           initialized = FALSE;
1399         static dissector_handle_t synphasor_udp_handle;
1400         static dissector_handle_t synphasor_tcp_handle;
1401         static guint              current_udp_port;
1402         static guint              current_tcp_port;
1403
1404         if (!initialized) {
1405                 synphasor_udp_handle = create_dissector_handle(dissect_udp, proto_synphasor);
1406                 synphasor_tcp_handle = create_dissector_handle(dissect_tcp, proto_synphasor);
1407
1408                 initialized = TRUE;
1409         }
1410         else {
1411                 /* update preferences */
1412                 dissector_delete("udp.port", current_udp_port, synphasor_udp_handle);
1413                 dissector_delete("tcp.port", current_tcp_port, synphasor_tcp_handle);
1414         }
1415
1416         current_udp_port = global_pref_udp_port;
1417         current_tcp_port = global_pref_tcp_port;
1418
1419         dissector_add("udp.port", current_udp_port, synphasor_udp_handle);
1420         dissector_add("tcp.port", current_tcp_port, synphasor_tcp_handle);
1421 } /* proto_reg_handoff_synphasor() */
1422