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