2 * Dissector for IEEE C37.118 synchrophasor frames.
4 * Copyright 2008, Jens Steinhauser <jens.steinhauser@omicron.at>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
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.
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.
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.
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>
38 #define PROTOCOL_NAME "IEEE C37.118 Synchrophasor Protocol"
39 #define PROTOCOL_SHORT_NAME "SYNCHROPHASOR"
40 #define PROTOCOL_ABBREV "synphasor"
42 /* forward references */
43 void proto_reg_handoff_synphasor(void);
45 /* global variables */
47 static int proto_synphasor
= -1;
48 static GSList
*config_frame_list
= NULL
;
50 /* user preferences */
51 static guint global_pref_tcp_port
= 4712;
52 static guint global_pref_udp_port
= 4713;
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;
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;
109 static dissector_handle_t synphasor_udp_handle
;
111 /* the five different frame types for this protocol */
120 /* the channel names in the protocol are all 16 bytes
121 * long (and don't have to be NULL terminated) */
124 /* Structures to save CFG frame content. */
126 /* type to indicate the format for (D)FREQ/PHASORS/ANALOG in data frame */
127 typedef enum { integer
, /* 16 bit signed integer */
128 floating_point
/* single precision floating point */
131 typedef enum { rect
, polar
} phasor_notation_e
;
133 typedef enum { V
, A
} unit_e
;
135 /* holds the information required to dissect a single phasor */
137 char name
[CHNAM_LEN
+ 1];
139 guint32 conv
; /* conversation factor in 10^-5 scale */
142 /* holds the information for an analog value */
144 char name
[CHNAM_LEN
+ 1];
145 guint32 conv
; /* conversation factor, user defined scaling (so it's pretty useless) */
148 /* holds information required to dissect a single PMU block in a data frame */
150 guint16 id
; /* identifies source of block */
151 char name
[CHNAM_LEN
+ 1]; /* holds STN */
152 data_format format_fr
; /* data format of FREQ and DFREQ */
153 data_format format_ph
; /* data format of PHASORS */
154 data_format format_an
; /* data format of ANALOG */
155 phasor_notation_e phasor_notation
; /* format of the phasors */
156 guint fnom
; /* nominal line frequency */
157 guint num_dg
; /* number of digital status words */
158 GArray
*phasors
; /* array of phasor_infos */
159 GArray
*analogs
; /* array of analog_infos */
162 /* holds the id the configuration comes from an and
163 * an array of config_block members */
165 guint32 fnum
; /* frame number */
168 GArray
*config_blocks
; /* Contains a config_block struct for
169 * every PMU included in the config frame */
172 /* strings for type bits in SYNC */
173 static const value_string typenames
[] = {
175 { 1, "Header Frame" },
176 { 2, "Configuration Frame 1" },
177 { 3, "Configuration Frame 2" },
178 { 4, "Command Frame" },
182 /* strings for version bits in SYNC */
183 static const value_string versionnames
[] = {
184 { 1, "IEEE C37.118-2005 initial publication" },
188 /* strings for the time quality flags in FRACSEC */
189 static const value_string timequalcodes
[] = {
190 { 0xF, "Clock failure, time not reliable" },
191 { 0xB, "Clock unlocked, time within 10 s" },
192 { 0xA, "Clock unlocked, time within 1 s" },
193 { 0x9, "Clock unlocked, time within 10^-1 s" },
194 { 0x8, "Clock unlocked, time within 10^-2 s" },
195 { 0x7, "Clock unlocked, time within 10^-3 s" },
196 { 0x6, "Clock unlocked, time within 10^-4 s" },
197 { 0x5, "Clock unlocked, time within 10^-5 s" },
198 { 0x4, "Clock unlocked, time within 10^-6 s" },
199 { 0x3, "Clock unlocked, time within 10^-7 s" },
200 { 0x2, "Clock unlocked, time within 10^-8 s" },
201 { 0x1, "Clock unlocked, time within 10^-9 s" },
202 { 0x0, "Normal operation, clock locked" },
206 /* strings for flags in the FORMAT word of a configuration frame */
207 static const true_false_string conf_formatb123names
= {
211 static const true_false_string conf_formatb0names
= {
216 /* strings to decode ANUNIT in configuration frame */
217 static const range_string conf_anconvnames
[] = {
218 { 0, 0, "single point-on-wave" },
219 { 1, 1, "rms of analog input" },
220 { 2, 2, "peak of input" },
221 { 3, 4, "undefined" },
222 { 5, 64, "reserved" },
223 { 65, 255, "user defined" },
227 /* strings for the FNOM field */
228 static const true_false_string conf_fnomnames
= {
233 /* strings for flags in the STAT word of a data frame */
234 static const true_false_string data_statb15names
= {
238 static const true_false_string data_statb14names
= {
242 static const true_false_string data_statb13names
= {
243 "Synchronization lost",
244 "Clock is synchronized"
246 static const true_false_string data_statb12names
= {
250 static const true_false_string data_statb11names
= {
254 static const true_false_string data_statb10names
= {
258 static const value_string data_statb05to04names
[] = {
259 { 0, "Time locked, best quality" },
260 { 1, "Unlocked for 10s" },
261 { 2, "Unlocked for 100s" },
262 { 3, "Unlocked for over 1000s" },
265 static const value_string data_statb03to00names
[] = {
267 { 0x1, "Magnitude low" },
268 { 0x2, "Magnitude high" },
269 { 0x3, "Phase-angel diff" },
270 { 0x4, "Frequency high/low" },
271 { 0x5, "df/dt high" },
274 { 0x8, "User defined" },
275 { 0x9, "User defined" },
276 { 0xA, "User defined" },
277 { 0xB, "User defined" },
278 { 0xC, "User defined" },
279 { 0xD, "User defined" },
280 { 0xE, "User defined" },
281 { 0xF, "User defined" },
285 /* strings to decode the commands */
286 static const value_string command_names
[] = {
287 { 0, "unknown command" },
288 { 1, "data transmission off" },
289 { 2, "data transmission on" },
290 { 3, "send HDR frame" },
291 { 4, "send CFG-1 frame" },
292 { 5, "send CFG-2 frame" },
293 { 6, "unknown command" },
294 { 7, "unknown command" },
295 { 8, "extended frame" },
296 { 9, "unknown command" },
297 { 10, "unknown command" },
298 { 11, "unknown command" },
299 { 12, "unknown command" },
300 { 13, "unknown command" },
301 { 14, "unknown command" },
302 { 15, "unknown command" },
306 /* Dissects a configuration frame (only the most important stuff, tries
307 * to be fast, does no GUI stuff) and returns a pointer to a config_frame
308 * struct that contains all the information from the frame needed to
309 * dissect a DATA frame.
311 * use 'config_frame_free()' to free the config_frame again
313 static config_frame
* config_frame_fast(tvbuff_t
*tvb
)
315 guint16 idcode
, num_pmu
;
319 /* get a new frame and initialize it */
320 frame
= g_slice_new(config_frame
);
322 frame
->config_blocks
= g_array_new(FALSE
, TRUE
, sizeof(config_block
));
324 idcode
= tvb_get_ntohs(tvb
, 4);
327 num_pmu
= tvb_get_ntohs(tvb
, 18);
328 offset
= 20; /* start of repeating blocks */
331 guint16 format_flags
;
341 /* initialize the block */
342 block
.phasors
= g_array_new(FALSE
, TRUE
, sizeof(phasor_info
));
343 block
.analogs
= g_array_new(FALSE
, TRUE
, sizeof(analog_info
));
344 /* copy the station name from the tvb to block, and add NULL byte */
345 tvb_memcpy(tvb
, block
.name
, offset
, CHNAM_LEN
); offset
+= CHNAM_LEN
;
346 block
.name
[CHNAM_LEN
] = '\0';
348 block
.id
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
350 format_flags
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
351 block
.format_fr
= (format_flags
& 0x0008) ? floating_point
: integer
;
352 block
.format_an
= (format_flags
& 0x0004) ? floating_point
: integer
;
353 block
.format_ph
= (format_flags
& 0x0002) ? floating_point
: integer
;
354 block
.phasor_notation
= (format_flags
& 0x0001) ? polar
: rect
;
356 num_ph
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
357 num_an
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
358 num_dg
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
359 block
.num_dg
= num_dg
;
361 /* the offset of the PHUNIT, ANUNIT, and FNOM blocks */
362 phunit
= offset
+ (num_ph
+ num_an
+ num_dg
* CHNAM_LEN
) * CHNAM_LEN
;
363 anunit
= phunit
+ num_ph
* 4;
364 fnom
= anunit
+ num_an
* 4 + num_dg
* 4;
366 /* read num_ph phasor names and conversation factors */
367 for (i
= 0; i
!= num_ph
; i
++) {
371 /* copy the phasor name from the tvb, and add NULL byte */
372 tvb_memcpy(tvb
, pi
.name
, offset
, CHNAM_LEN
); offset
+= CHNAM_LEN
;
373 pi
.name
[CHNAM_LEN
] = '\0';
375 conv
= tvb_get_ntohl(tvb
, phunit
+ 4 * i
);
376 pi
.unit
= conv
& 0xFF000000 ? A
: V
;
377 pi
.conv
= conv
& 0x00FFFFFF;
379 g_array_append_val(block
.phasors
, pi
);
382 /* read num_an analog value names and conversation factors */
383 for (i
= 0; i
!= num_an
; i
++) {
387 /* copy the phasor name from the tvb, and add NULL byte */
388 tvb_memcpy(tvb
, ai
.name
, offset
, CHNAM_LEN
); offset
+= CHNAM_LEN
;
389 ai
.name
[CHNAM_LEN
] = '\0';
391 conv
= tvb_get_ntohl(tvb
, anunit
+ 4 * i
);
394 g_array_append_val(block
.analogs
, ai
);
397 /* the names for the bits in the digital status words aren't saved,
398 there is no space to display them in the GUI anyway */
401 block
.fnom
= tvb_get_ntohs(tvb
, fnom
) & 0x0001 ? 50 : 60;
407 g_array_append_val(frame
->config_blocks
, block
);
414 /* Frees the memory pointed to by 'frame' and all the contained
415 * config_blocks and the data in their GArrays.
417 static void config_frame_free(config_frame
*frame
)
419 int i
= frame
->config_blocks
->len
;
421 /* free all the config_blocks this frame contains */
425 block
= &g_array_index(frame
->config_blocks
, config_block
, i
);
426 g_array_free(block
->phasors
, TRUE
);
427 g_array_free(block
->analogs
, TRUE
);
430 /* free the array of config blocks itself */
431 g_array_free(frame
->config_blocks
, TRUE
);
433 /* and the config_frame */
434 g_slice_free1(sizeof(config_frame
), frame
);
437 /* called every time the user loads a capture file or starts to capture */
438 static void synphasor_init(void)
440 /* free stuff in the list from a previous run */
441 if (config_frame_list
) {
442 g_slist_foreach(config_frame_list
, (GFunc
) config_frame_free
, NULL
);
444 g_slist_free(config_frame_list
);
446 config_frame_list
= NULL
;
451 /* Checks the CRC of a synchrophasor frame, 'tvb' has to include the whole
452 * frame, including CRC, the calculated CRC is returned in '*computedcrc'.
454 static gboolean
check_crc(tvbuff_t
*tvb
, guint16
*computedcrc
)
457 guint len
= tvb_get_ntohs(tvb
, 2);
459 crc
= tvb_get_ntohs(tvb
, len
- 2);
460 *computedcrc
= crc16_x25_ccitt_tvb(tvb
, len
- 2);
462 if (crc
== *computedcrc
)
468 /* forward declarations of the subdissectors for the data
469 * in the frame that is not common to all types of frames
471 static int dissect_config_frame (tvbuff_t
*, proto_item
*);
472 static int dissect_data_frame (tvbuff_t
*, proto_item
*, packet_info
*);
473 static int dissect_command_frame(tvbuff_t
*, proto_item
*, packet_info
*);
474 /* to keep 'dissect_common()' shorter */
475 static gint
dissect_header(tvbuff_t
*, proto_tree
*);
477 /* Dissects the header (common to all types of frames) and then calls
478 * one of the subdissectors (declared above) for the rest of the frame.
480 static int dissect_common(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
484 guint tvbsize
= tvb_length(tvb
);
486 /* some heuristics */
487 if (tvbsize
< 17 /* 17 bytes = header frame with only a
488 NULL character, useless but valid */
489 || tvb_get_guint8(tvb
, 0) != 0xAA) /* every synchrophasor frame starts with 0xAA */
492 /* write the protocol name to the info column */
493 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PROTOCOL_SHORT_NAME
);
495 frame_type
= tvb_get_guint8(tvb
, 1) >> 4;
497 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s", val_to_str_const(frame_type
, typenames
, "invalid packet type"));
499 /* CFG-2 and DATA frames need special treatment during the first run:
500 * For CFG-2 frames, a 'config_frame' struct is created to hold the
501 * information necessary to decode DATA frames. A pointer to this
502 * struct is saved in the conversation and is copied to the
503 * per-packet information if a DATA frame is dissected.
505 if (!pinfo
->fd
->flags
.visited
) {
506 if (CFG2
== frame_type
&&
507 check_crc(tvb
, &crc
)) {
508 conversation_t
*conversation
;
510 /* fill the config_frame */
511 config_frame
*frame
= config_frame_fast(tvb
);
512 frame
->fnum
= pinfo
->fd
->num
;
513 /* so we can cleanup later */
514 config_frame_list
= g_slist_append(config_frame_list
, frame
);
516 /* find a conversation, create a new if no one exists */
517 conversation
= find_or_create_conversation(pinfo
);
519 /* remove data from a previous CFG-2 frame, only
520 * the most recent configuration frame is relevant */
521 if (conversation_get_proto_data(conversation
, proto_synphasor
))
522 conversation_delete_proto_data(conversation
, proto_synphasor
);
524 conversation_add_proto_data(conversation
, proto_synphasor
, frame
);
526 else if (DATA
== frame_type
) {
527 conversation_t
*conversation
= find_conversation(pinfo
->fd
->num
,
528 &pinfo
->src
, &pinfo
->dst
,
530 pinfo
->srcport
, pinfo
->destport
,
534 config_frame
*conf
= (config_frame
*)conversation_get_proto_data(conversation
, proto_synphasor
);
535 /* no problem if 'conf' is NULL, the DATA frame dissector checks this again */
536 p_add_proto_data(pinfo
->fd
, proto_synphasor
, 0, conf
);
539 } /* if (!visited) */
541 if (tree
) { /* we are being asked for details */
542 proto_tree
*synphasor_tree
= NULL
;
543 proto_item
*temp_item
= NULL
;
544 proto_item
*sub_item
= NULL
;
550 temp_item
= proto_tree_add_item(tree
, proto_synphasor
, tvb
, 0, -1, ENC_NA
);
551 proto_item_append_text(temp_item
, ", %s", val_to_str_const(frame_type
, typenames
,
552 ", invalid packet type"));
554 /* synphasor_tree is where from now on all new elements for this protocol get added */
555 synphasor_tree
= proto_item_add_subtree(temp_item
, ett_synphasor
);
557 framesize
= dissect_header(tvb
, synphasor_tree
);
558 offset
= 14; /* header is 14 bytes long */
560 /* check CRC, call appropriate subdissector for the rest of the frame if CRC is correct*/
561 sub_item
= proto_tree_add_text(synphasor_tree
, tvb
, offset
, tvbsize
- 16, "Data" );
562 temp_item
= proto_tree_add_text(synphasor_tree
, tvb
, tvbsize
- 2, 2 , "Checksum:");
563 if (!check_crc(tvb
, &crc
)) {
564 proto_item_append_text(sub_item
, ", not dissected because of wrong checksum");
565 proto_item_append_text(temp_item
, " 0x%04x [incorrect]", crc
);
568 /* create a new tvb to pass to the subdissector
569 '-16': length of header + 2 CRC bytes */
570 sub_tvb
= tvb_new_subset(tvb
, offset
, tvbsize
- 16, framesize
- 16);
572 /* call subdissector */
573 switch (frame_type
) {
575 dissect_data_frame(sub_tvb
, sub_item
, pinfo
);
577 case HEADER
: /* no further dissection is done/needed */
578 proto_item_append_text(sub_item
, "Header Frame");
582 dissect_config_frame(sub_tvb
, sub_item
);
585 dissect_command_frame(sub_tvb
, sub_item
, pinfo
);
589 proto_item_append_text(sub_item
, " of unknown type");
591 proto_item_append_text(temp_item
, " 0x%04x [correct]", crc
);
594 /*offset += 2;*/ /* CRC */
597 return tvb_length(tvb
);
598 } /* dissect_synphasor() */
600 /* called for synchrophasors over UDP */
601 static int dissect_udp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
603 return dissect_common(tvb
, pinfo
, tree
, data
);
606 /* callback for 'tcp_dissect_pdus()' to give it the length of the frame */
607 static guint
get_pdu_length(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
)
609 return tvb_get_ntohs(tvb
, offset
+ 2);
612 static int dissect_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
614 tcp_dissect_pdus(tvb
, pinfo
, tree
, TRUE
, 4, get_pdu_length
, dissect_common
, data
);
616 return tvb_length(tvb
);
620 /* Dissects the common header of frames.
622 * Returns the framesize, in contrast to most
623 * other helper functions that return the offset.
625 static gint
dissect_header(tvbuff_t
*tvb
, proto_tree
*tree
)
627 proto_tree
*temp_tree
;
628 proto_item
*temp_item
;
634 temp_item
= proto_tree_add_item(tree
, hf_sync
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
635 temp_tree
= proto_item_add_subtree(temp_item
, ett_frtype
);
636 proto_tree_add_item(temp_tree
, hf_sync_frtype
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
637 proto_tree_add_item(temp_tree
, hf_sync_version
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
641 proto_tree_add_item(tree
, hf_frsize
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
642 framesize
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
645 proto_tree_add_item(tree
, hf_idcode
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
650 /* can't use 'proto_tree_add_time()' because we need UTC */
653 time_t soc
= tvb_get_ntohl(tvb
, offset
);
655 strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S", t
);
656 proto_tree_add_string(tree
, hf_soc
, tvb
, offset
, 4, buf
);
661 /* time quality flags */
662 temp_item
= proto_tree_add_text(tree
, tvb
, offset
, 1, "Time quality flags");
663 temp_tree
= proto_item_add_subtree(temp_item
, ett_timequal
);
664 proto_tree_add_item(temp_tree
, hf_timeqal_lsdir
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
665 proto_tree_add_item(temp_tree
, hf_timeqal_lsocc
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
666 proto_tree_add_item(temp_tree
, hf_timeqal_lspend
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
667 proto_tree_add_item(temp_tree
, hf_timeqal_timequalindic
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
670 proto_tree_add_item(tree
, hf_fracsec
, tvb
, offset
, 3, ENC_BIG_ENDIAN
);
676 /* forward declarations of helper functions for 'dissect_config_frame()' */
677 static gint
dissect_CHNAM (tvbuff_t
*tvb
, proto_tree
*tree
, gint offset
, gint cnt
, const char *prefix
);
678 static gint
dissect_PHUNIT (tvbuff_t
*tvb
, proto_tree
*tree
, gint offset
, gint cnt
);
679 static gint
dissect_ANUNIT (tvbuff_t
*tvb
, proto_tree
*tree
, gint offset
, gint cnt
);
680 static gint
dissect_DIGUNIT(tvbuff_t
*tvb
, proto_tree
*tree
, gint offset
, gint cnt
);
682 /* dissects a configuration frame (type 1 and 2) and adds fields to 'config_item' */
683 static int dissect_config_frame(tvbuff_t
*tvb
, proto_item
*config_item
)
685 proto_tree
*config_tree
= NULL
;
686 proto_item
*temp_item
= NULL
;
687 proto_tree
*temp_tree
= NULL
;
691 proto_item_set_text (config_item
, "Configuration data");
692 config_tree
= proto_item_add_subtree(config_item
, ett_conf
);
694 /* TIME_BASE and NUM_PMU */
695 offset
+= 1; /* skip the reserved byte */
696 proto_tree_add_item(config_tree
, hf_conf_timebase
, tvb
, offset
, 3, ENC_BIG_ENDIAN
); offset
+= 3;
697 proto_tree_add_item(config_tree
, hf_conf_numpmu
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
698 /* add number of included PMUs to the text in the list view */
699 num_pmu
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
700 proto_item_append_text(config_item
, ", %"G_GUINT16_FORMAT
" PMU(s) included", num_pmu
);
702 /* dissect the repeating PMU blocks */
703 for (j
= 0; j
< num_pmu
; j
++) {
704 guint16 num_ph
, num_an
, num_dg
;
705 proto_item
*station_item
= NULL
;
706 proto_tree
*station_tree
= NULL
;
709 gint oldoffset
= offset
; /* to calculate the length of the whole PMU block later */
711 /* STN with new tree to add the rest of the PMU block */
712 str
= tvb_get_string(wmem_packet_scope(), tvb
, offset
, CHNAM_LEN
);
713 station_item
= proto_tree_add_text(config_tree
, tvb
, offset
, CHNAM_LEN
, "Station #%i: \"%s\"", j
+ 1, str
);
714 station_tree
= proto_item_add_subtree(station_item
, ett_conf_station
);
718 proto_tree_add_item(station_tree
, hf_idcode
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
721 temp_item
= proto_tree_add_text(station_tree
, tvb
, offset
, 2, "Data format in data frame");
722 temp_tree
= proto_item_add_subtree(temp_item
, ett_conf_format
);
723 proto_tree_add_item(temp_tree
, hf_conf_formatb3
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
724 proto_tree_add_item(temp_tree
, hf_conf_formatb2
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
725 proto_tree_add_item(temp_tree
, hf_conf_formatb1
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
726 proto_tree_add_item(temp_tree
, hf_conf_formatb0
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
729 /* PHNMR, ANNMR, DGNMR */
730 num_ph
= tvb_get_ntohs(tvb
, offset
);
731 num_an
= tvb_get_ntohs(tvb
, offset
+ 2);
732 num_dg
= tvb_get_ntohs(tvb
, offset
+ 4);
733 proto_tree_add_text(station_tree
, tvb
, offset
, 2, "Number of phasors: %u", num_ph
);
734 proto_tree_add_text(station_tree
, tvb
, offset
+ 2, 2, "Number of analog values: %u", num_an
);
735 proto_tree_add_text(station_tree
, tvb
, offset
+ 4, 2, "Number of digital status words: %u", num_dg
);
738 /* CHNAM, the channel names */
739 offset
= dissect_CHNAM(tvb
, station_tree
, offset
, num_ph
, "Phasor name" );
740 offset
= dissect_CHNAM(tvb
, station_tree
, offset
, num_an
, "Analog value" );
741 offset
= dissect_CHNAM(tvb
, station_tree
, offset
, num_dg
* 16, "Digital status label");
743 /* PHUNIT, ANUINT and DIGUNIT */
744 offset
= dissect_PHUNIT (tvb
, station_tree
, offset
, num_ph
);
745 offset
= dissect_ANUNIT (tvb
, station_tree
, offset
, num_an
);
746 offset
= dissect_DIGUNIT(tvb
, station_tree
, offset
, num_dg
);
748 /* FNOM and CFGCNT */
749 proto_tree_add_item(station_tree
, hf_conf_fnom
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
750 proto_tree_add_item(station_tree
, hf_conf_cfgcnt
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
752 /* set the correct length for the "Station :" item */
753 proto_item_set_len(station_item
, offset
- oldoffset
);
754 } /* for() PMU blocks */
758 gint16 tmp
= tvb_get_ntohs(tvb
, offset
);
759 temp_item
= proto_tree_add_text(config_tree
, tvb
, offset
, 2, "Rate of transmission: "); offset
+= 2;
761 proto_item_append_text(temp_item
, "%"G_GINT16_FORMAT
" frame(s) per second", tmp
);
763 proto_item_append_text(temp_item
, "1 frame per %"G_GINT16_FORMAT
" second(s)", (gint16
)-tmp
);
767 } /* dissect_config_frame() */
769 /* forward declarations of helper functions for 'dissect_data_frame()' */
770 static gint
dissect_PHASORS(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, gint offset
);
771 static gint
dissect_DFREQ (tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, gint offset
);
772 static gint
dissect_ANALOG (tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, gint offset
);
773 static gint
dissect_DIGITAL(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, gint offset
);
774 /* calculates the size (in bytes) of a data frame that the config_block describes */
775 #define BLOCKSIZE(x) (2 /* STAT */ \
776 + (x).phasors->len * (integer == (x).format_ph ? 4 : 8) /* PHASORS */ \
777 + (integer == (x).format_fr ? 4 : 8) /* (D)FREQ */ \
778 + (x).analogs->len * (integer == (x).format_an ? 2 : 4) /* ANALOG */ \
779 + (x).num_dg * 2) /* DIGITAL */
781 /* Dissects a data frame */
782 static int dissect_data_frame(tvbuff_t
*tvb
,
783 proto_item
*data_item
, /* all items are placed beneath this item */
784 packet_info
*pinfo
) /* used to find the data from a CFG-2 frame */
786 proto_tree
*data_tree
= NULL
;
791 proto_item_set_text(data_item
, "Measurement data");
792 data_tree
= proto_item_add_subtree(data_item
, ett_data
);
794 /* search for configuration information to dissect the frame */
796 gboolean config_found
= FALSE
;
797 conf
= (config_frame
*)p_get_proto_data(pinfo
->fd
, proto_synphasor
, 0);
800 /* check if the size of the current frame is the
801 size of the frame the config_frame describes */
802 size_t reported_size
= 0;
803 for (i
= 0; i
< conf
->config_blocks
->len
; i
++) {
804 config_block
*block
= &g_array_index(conf
->config_blocks
, config_block
, i
);
805 reported_size
+= BLOCKSIZE(*block
);
808 if (tvb_length(tvb
) == reported_size
) {
809 proto_item_append_text(data_item
, ", using frame number %"G_GUINT32_FORMAT
" as configuration frame",
816 proto_item_append_text(data_item
, ", no configuration frame found");
821 /* dissect a PMU block for every config_block in the frame */
822 for (i
= 0; i
< conf
->config_blocks
->len
; i
++) {
823 config_block
*block
= &g_array_index(conf
->config_blocks
, config_block
, i
);
825 proto_item
*block_item
= proto_tree_add_text(data_tree
, tvb
, offset
, BLOCKSIZE(*block
),
826 "Station: \"%s\"", block
->name
);
827 proto_tree
*block_tree
= proto_item_add_subtree(block_item
, ett_data_block
);
830 proto_item
*temp_item
= proto_tree_add_text(block_tree
, tvb
, offset
, 2, "Flags");
831 proto_tree
*temp_tree
= proto_item_add_subtree(temp_item
, ett_data_stat
);
832 proto_tree_add_item(temp_tree
, hf_data_statb15
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
833 proto_tree_add_item(temp_tree
, hf_data_statb14
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
834 proto_tree_add_item(temp_tree
, hf_data_statb13
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
835 proto_tree_add_item(temp_tree
, hf_data_statb12
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
836 proto_tree_add_item(temp_tree
, hf_data_statb11
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
837 proto_tree_add_item(temp_tree
, hf_data_statb10
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
838 proto_tree_add_item(temp_tree
, hf_data_statb05to04
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
839 proto_tree_add_item(temp_tree
, hf_data_statb03to00
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
842 /* PHASORS, (D)FREQ, ANALOG, and DIGITAL */
843 offset
= dissect_PHASORS(tvb
, block_item
, block
, offset
);
844 offset
= dissect_DFREQ (tvb
, block_item
, block
, offset
);
845 offset
= dissect_ANALOG (tvb
, block_item
, block
, offset
);
846 offset
= dissect_DIGITAL(tvb
, block_item
, block
, offset
);
849 } /* dissect_data_frame() */
851 /* Dissects a command frame and adds fields to config_item.
853 * 'pinfo' is used to add the type of command
854 * to the INFO column in the packet list.
856 static int dissect_command_frame(tvbuff_t
*tvb
,
857 proto_item
*command_item
,
860 proto_tree
*command_tree
= NULL
;
861 guint tvbsize
= tvb_length(tvb
);
864 proto_item_set_text(command_item
, "Command data");
865 command_tree
= proto_item_add_subtree(command_item
, ett_command
);
868 proto_tree_add_item(command_tree
, hf_command
, tvb
, 0, 2, ENC_BIG_ENDIAN
);
870 s
= val_to_str_const(tvb_get_ntohs(tvb
, 0), command_names
, "invalid command");
871 col_append_str(pinfo
->cinfo
, COL_INFO
, ", ");
872 col_append_str(pinfo
->cinfo
, COL_INFO
, s
);
875 if (tvb_get_ntohs(tvb
, 0) == 0x0008) {
876 /* Command: Extended Frame, the extra data is ok */
877 proto_item
* i
= proto_tree_add_text(command_tree
, tvb
, 2, tvbsize
- 2, "Extended frame data");
879 proto_item_append_text(i
, ", but size not multiple of 16-bit word");
882 proto_tree_add_text(command_tree
, tvb
, 2, tvbsize
- 2, "Unknown data");
886 } /* dissect_command_frame() */
888 /****************************************************************/
889 /* after this line: helper functions for 'dissect_data_frame()' */
890 /****************************************************************/
892 /* Dissects a single phasor for 'dissect_PHASORS()' */
893 static int dissect_single_phasor(tvbuff_t
*tvb
, int offset
,
894 double* mag
, double* phase
, /* returns the resulting values here */
895 data_format format
, /* information needed to... */
896 phasor_notation_e notation
) /* ...dissect the phasor */
898 if (floating_point
== format
) {
899 if (polar
== notation
) {
901 *mag
= tvb_get_ntohieee_float(tvb
, offset
);
902 *phase
= tvb_get_ntohieee_float(tvb
, offset
+ 4);
907 real
= tvb_get_ntohieee_float(tvb
, offset
);
908 imag
= tvb_get_ntohieee_float(tvb
, offset
+ 4);
910 *mag
= sqrt(pow(real
, 2) + pow(imag
, 2));
911 *phase
= atan2(imag
, real
);
915 if (polar
== notation
) {
917 *mag
= (guint16
)tvb_get_ntohs(tvb
, offset
);
918 *phase
= (gint16
) tvb_get_ntohs(tvb
, offset
+ 2);
919 *phase
/= 10000.0; /* angle is in radians*10^4 */
924 real
= tvb_get_ntohs(tvb
, offset
);
925 imag
= tvb_get_ntohs(tvb
, offset
+ 2);
927 *mag
= sqrt(pow(real
, 2) + pow(imag
, 2));
928 *phase
= atan2(imag
, real
);
932 return floating_point
== format
? 8 : 4;
935 /* used by 'dissect_data_frame()' to dissect the PHASORS field */
936 static gint
dissect_PHASORS(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, gint offset
)
938 proto_item
*temp_item
= NULL
;
939 proto_tree
*phasor_tree
= NULL
;
942 cnt
= block
->phasors
->len
; /* number of phasors to dissect */
947 length
= block
->phasors
->len
* (floating_point
== block
->format_ph
? 8 : 4);
948 temp_item
= proto_tree_add_text(tree
, tvb
, offset
, length
, "Phasors (%u)", cnt
);
949 phasor_tree
= proto_item_add_subtree(temp_item
, ett_data_phasors
);
951 /* dissect a phasor for every phasor_info saved in the config_block */
952 for (j
= 0; j
< cnt
; j
++) {
956 pi
= &g_array_index(block
->phasors
, phasor_info
, j
);
957 temp_item
= proto_tree_add_text(phasor_tree
, tvb
, offset
,
958 floating_point
== block
->format_ph
? 8 : 4,
959 "Phasor #%u: \"%s\"", j
+ 1, pi
->name
);
961 offset
+= dissect_single_phasor(tvb
, offset
,
964 block
->phasor_notation
);
966 /* for values in integer format, apply conversation factor */
967 if (integer
== block
->format_ph
)
968 mag
= (mag
* pi
->conv
) * 0.00001;
971 #define DEGREE "\xC2\xB0" /* DEGREE signs in UTF-8 */
973 proto_item_append_text(temp_item
, ", %10.2f%c" ANGLE
"%7.2f" DEGREE
,
975 V
== pi
->unit
? 'V' : 'A',
983 /* used by 'dissect_data_frame()' to dissect the FREQ and DFREQ fields */
984 static gint
dissect_DFREQ(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, gint offset
)
986 if (floating_point
== block
->format_fr
) {
989 tmp
= tvb_get_ntohieee_float(tvb
, offset
);
990 proto_tree_add_text(tree
, tvb
, offset
, 4, "Actual frequency value: %fHz", tmp
); offset
+= 4;
992 /* The standard doesn't clearly say how to interpret this value, but
993 * http://www.pes-psrc.org/h/C37_118_H11_FAQ_Jan2008.pdf provides further information.
994 * --> no scaling factor is applied to DFREQ
996 tmp
= tvb_get_ntohieee_float(tvb
, offset
);
997 proto_tree_add_text(tree
, tvb
, offset
, 4, "Rate of change of frequency: %fHz/s", tmp
); offset
+= 4;
1002 tmp
= tvb_get_ntohs(tvb
, offset
);
1003 proto_tree_add_text(tree
, tvb
, offset
, 2,
1004 "Frequency deviation from nominal: %" G_GINT16_FORMAT
"mHz (actual frequency: %.3fHz)",
1005 tmp
, block
->fnom
+ (tmp
/ 1000.0));
1008 tmp
= tvb_get_ntohs(tvb
, offset
);
1009 proto_tree_add_text(tree
, tvb
, offset
, 2, "Rate of change of frequency: %.3fHz/s", tmp
/ 100.0); offset
+= 2;
1014 /* used by 'dissect_data_frame()' to dissect the ANALOG field */
1015 static gint
dissect_ANALOG(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, gint offset
)
1017 proto_tree
*analog_tree
= NULL
;
1018 proto_item
*temp_item
= NULL
;
1021 cnt
= block
->analogs
->len
; /* number of analog values to dissect */
1026 length
= block
->analogs
->len
* (floating_point
== block
->format_an
? 4 : 2);
1027 temp_item
= proto_tree_add_text(tree
, tvb
, offset
, length
, "Analog values (%u)", cnt
);
1029 analog_tree
= proto_item_add_subtree(temp_item
, ett_data_analog
);
1031 for (j
= 0; j
< cnt
; j
++) {
1032 analog_info
*ai
= &g_array_index(block
->analogs
, analog_info
, j
);
1034 temp_item
= proto_tree_add_text(analog_tree
, tvb
, offset
,
1035 floating_point
== block
->format_an
? 4 : 2,
1036 "Analog value #%u: \"%s\"", j
+ 1, ai
->name
);
1038 if (floating_point
== block
->format_an
) {
1039 gfloat tmp
= tvb_get_ntohieee_float(tvb
, offset
); offset
+= 4;
1040 proto_item_append_text(temp_item
, ", %.3f", tmp
);
1043 /* the "standard" doesn't say if this is signed or unsigned,
1044 * so I just use gint16, the scaling of the conversation factor
1045 * is also "user defined", so I just write it after the analog value */
1046 gint16 tmp
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
1047 proto_item_append_text(temp_item
, ", %" G_GINT16_FORMAT
" (conversation factor: %#06x)",
1054 /* used by 'dissect_data_frame()' to dissect the DIGITAL field */
1055 static gint
dissect_DIGITAL(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, gint offset
)
1057 proto_item
*digital_item
= NULL
;
1059 cnt
= block
->num_dg
; /* number of digital status words to dissect */
1064 digital_item
= proto_tree_add_text(tree
, tvb
, offset
, cnt
* 2, "Digital status words (%u)", cnt
);
1065 tree
= proto_item_add_subtree(digital_item
, ett_data_digital
);
1067 for (j
= 0; j
< cnt
; j
++) {
1068 guint16 tmp
= tvb_get_ntohs(tvb
, offset
);
1069 proto_tree_add_text(tree
, tvb
, offset
, 2, "Digital status word #%u: 0x%04x", j
+ 1, tmp
);
1075 /*******************************************************************/
1076 /* after this line: helper functions for 'dissect_config_frame()' */
1077 /*******************************************************************/
1079 /* used by 'dissect_config_frame()' to dissect the PHUNIT field */
1080 static gint
dissect_PHUNIT(tvbuff_t
*tvb
, proto_tree
*tree
, gint offset
, gint cnt
)
1082 proto_item
*temp_item
= NULL
;
1083 proto_tree
*temp_tree
= NULL
;
1089 temp_item
= proto_tree_add_text(tree
, tvb
, offset
, 4 * cnt
, "Phasor conversation factors (%u)", cnt
);
1090 temp_tree
= proto_item_add_subtree(temp_item
, ett_conf_phconv
);
1092 /* Conversion factor for phasor channels. Four bytes for each phasor.
1093 * MSB: 0 = voltage, 1 = current
1094 * Lower 3 Bytes: unsigned 24-bit word in 10^-5 V or A per bit to scale the phasor value
1096 for (i
= 0; i
< cnt
; i
++) {
1097 guint32 tmp
= tvb_get_ntohl(tvb
, offset
);
1098 proto_tree_add_text(temp_tree
, tvb
, offset
, 4,
1099 "#%u factor: %u * 10^-5, unit: %s",
1102 tmp
& 0xFF000000 ? "Ampere" : "Volt");
1109 /* used by 'dissect_config_frame()' to dissect the ANUNIT field */
1110 static gint
dissect_ANUNIT(tvbuff_t
*tvb
, proto_tree
*tree
, gint offset
, gint cnt
)
1112 proto_item
*temp_item
= NULL
;
1113 proto_tree
*temp_tree
= NULL
;
1119 temp_item
= proto_tree_add_text(tree
, tvb
, offset
, 4 * cnt
, "Analog values conversation factors (%u)", cnt
);
1120 temp_tree
= proto_item_add_subtree(temp_item
, ett_conf_anconv
);
1122 /* Conversation factor for analog channels. Four bytes for each analog value.
1123 * MSB: see 'synphasor_conf_anconvnames' in 'synphasor_strings.c'
1124 * Lower 3 Bytes: signed 24-bit word, user-defined scaling
1126 for (i
= 0; i
< cnt
; i
++) {
1127 gint32 tmp
= tvb_get_ntohl(tvb
, offset
);
1128 temp_item
= proto_tree_add_text(temp_tree
, tvb
, offset
, 4,
1129 "Factor for analog value #%i: %s",
1131 try_rval_to_str((tmp
>> 24) & 0x000000FF, conf_anconvnames
));
1134 if ( tmp
& 0x00800000) /* sign bit set */
1137 proto_item_append_text(temp_item
, ", value: %" G_GINT32_FORMAT
, tmp
);
1145 /* used by 'dissect_config_frame()' to dissect the DIGUNIT field */
1146 static gint
dissect_DIGUNIT(tvbuff_t
*tvb
, proto_tree
*tree
, gint offset
, gint cnt
)
1148 proto_item
*temp_item
= NULL
;
1149 proto_tree
*temp_tree
= NULL
;
1155 temp_item
= proto_tree_add_text(tree
, tvb
, offset
, 4 * cnt
, "Masks for digital status words (%u)", cnt
);
1156 temp_tree
= proto_item_add_subtree(temp_item
, ett_conf_dgmask
);
1158 /* Mask words for digital status words. Two 16-bit words for each digital word. The first
1159 * inidcates the normal status of the inputs, the second indicated the valid bits in
1162 for (i
= 0; i
< cnt
; i
++) {
1163 guint32 tmp
= tvb_get_ntohl(tvb
, offset
);
1165 temp_item
= proto_tree_add_text(temp_tree
, tvb
, offset
, 4, "Mask for status word #%u: ", i
+ 1);
1166 proto_item_append_text(temp_item
, "normal state: 0x%04"G_GINT16_MODIFIER
"x", (guint16
)((tmp
& 0xFFFF0000) >> 16));
1167 proto_item_append_text(temp_item
, ", valid bits: 0x%04"G_GINT16_MODIFIER
"x", (guint16
)( tmp
& 0x0000FFFF));
1175 /* used by 'dissect_config_frame()' to dissect the "channel name"-fields */
1176 static gint
dissect_CHNAM(tvbuff_t
*tvb
, proto_tree
*tree
, gint offset
, gint cnt
, const char *prefix
)
1178 proto_item
*temp_item
= NULL
;
1179 proto_tree
*temp_tree
= NULL
;
1185 temp_item
= proto_tree_add_text(tree
, tvb
, offset
, CHNAM_LEN
* cnt
, "%ss (%u)", prefix
, cnt
);
1186 temp_tree
= proto_item_add_subtree(temp_item
, ett_conf_phnam
);
1188 /* dissect the 'cnt' channel names */
1189 for (i
= 0; i
< cnt
; i
++) {
1191 str
= tvb_get_string(wmem_packet_scope(), tvb
, offset
, CHNAM_LEN
);
1192 proto_tree_add_text(temp_tree
, tvb
, offset
, CHNAM_LEN
,
1193 "%s #%i: \"%s\"", prefix
, i
+1, str
);
1194 offset
+= CHNAM_LEN
;
1200 void proto_register_synphasor(void)
1202 static hf_register_info hf
[] = {
1205 { "Synchronization word", PROTOCOL_ABBREV
".sync", FT_UINT16
, BASE_HEX
,
1206 NULL
, 0x0, NULL
, HFILL
}},
1207 /* Flags in the Sync word */
1209 { "Frame Type", PROTOCOL_ABBREV
".frtype", FT_UINT16
, BASE_HEX
,
1210 VALS(typenames
), 0x0070, NULL
, HFILL
}},
1213 { "Version", PROTOCOL_ABBREV
".version", FT_UINT16
, BASE_DEC
,
1214 VALS(versionnames
), 0x000F, NULL
, HFILL
}},
1217 { "Framesize", PROTOCOL_ABBREV
".frsize", FT_UINT16
, BASE_DEC
,
1218 NULL
, 0x0, NULL
, HFILL
}},
1221 { "PMU/DC ID number", PROTOCOL_ABBREV
".idcode", FT_UINT16
, BASE_DEC
,
1222 NULL
, 0x0, NULL
, HFILL
}},
1225 { "SOC time stamp (UTC)", PROTOCOL_ABBREV
".soc", FT_STRINGZ
, BASE_NONE
,
1226 NULL
, 0x0, NULL
, HFILL
}},
1228 /* Time quality flags in fracsec */
1229 { &hf_timeqal_lsdir
,
1230 { "Leap second direction", PROTOCOL_ABBREV
".timeqal.lsdir", FT_BOOLEAN
, 8,
1231 NULL
, 0x40, NULL
, HFILL
}},
1233 { &hf_timeqal_lsocc
,
1234 { "Leap second occurred", PROTOCOL_ABBREV
".timeqal.lsocc", FT_BOOLEAN
, 8,
1235 NULL
, 0x20, NULL
, HFILL
}},
1237 { &hf_timeqal_lspend
,
1238 { "Leap second pending", PROTOCOL_ABBREV
".timeqal.lspend", FT_BOOLEAN
, 8,
1239 NULL
, 0x10, NULL
, HFILL
}},
1241 { &hf_timeqal_timequalindic
,
1242 { "Time Quality indicator code", PROTOCOL_ABBREV
".timeqal.timequalindic", FT_UINT8
, BASE_HEX
,
1243 VALS(timequalcodes
), 0x0F, NULL
, HFILL
}},
1245 /* Fraction of second */
1247 { "Fraction of second (raw)", PROTOCOL_ABBREV
".fracsec", FT_UINT24
, BASE_DEC
,
1248 NULL
, 0x0, NULL
, HFILL
}},
1250 /* Data types for configuration frames */
1251 { &hf_conf_timebase
,
1252 { "Resolution of fractional second time stamp", PROTOCOL_ABBREV
".conf.timebase", FT_UINT24
, BASE_DEC
,
1253 NULL
, 0x0, NULL
, HFILL
}},
1256 { "Number of PMU blocks included in the frame", PROTOCOL_ABBREV
".conf.numpmu", FT_UINT16
, BASE_DEC
,
1257 NULL
, 0x0, NULL
, HFILL
}},
1259 /* Bits in the FORMAT word */
1260 { &hf_conf_formatb3
,
1261 { "FREQ/DFREQ format", PROTOCOL_ABBREV
".conf.dfreq_format", FT_BOOLEAN
, 16,
1262 TFS(&conf_formatb123names
), 0x8, NULL
, HFILL
}},
1264 { &hf_conf_formatb2
,
1265 { "Analog values format", PROTOCOL_ABBREV
".conf.analog_format", FT_BOOLEAN
, 16,
1266 TFS(&conf_formatb123names
), 0x4, NULL
, HFILL
}},
1268 { &hf_conf_formatb1
,
1269 { "Phasor format", PROTOCOL_ABBREV
".conf.phasor_format", FT_BOOLEAN
, 16,
1270 TFS(&conf_formatb123names
), 0x2, NULL
, HFILL
}},
1272 { &hf_conf_formatb0
,
1273 { "Phasor notation", PROTOCOL_ABBREV
".conf.phasor_notation", FT_BOOLEAN
, 16,
1274 TFS(&conf_formatb0names
), 0x1, NULL
, HFILL
}},
1277 { "Nominal line freqency", PROTOCOL_ABBREV
".conf.fnom", FT_BOOLEAN
, 16,
1278 TFS(&conf_fnomnames
), 0x0001, NULL
, HFILL
}},
1281 { "Configuration change count", PROTOCOL_ABBREV
".conf.cfgcnt", FT_UINT16
, BASE_DEC
,
1282 NULL
, 0, NULL
, HFILL
}},
1284 /* Data types for data frames */
1285 /* Flags in the STAT word */
1287 { "Data valid", PROTOCOL_ABBREV
".data.valid", FT_BOOLEAN
, 16,
1288 TFS(&data_statb15names
), 0x8000, NULL
, HFILL
}},
1291 { "PMU error", PROTOCOL_ABBREV
".data.PMUerror", FT_BOOLEAN
, 16,
1292 TFS(&data_statb14names
), 0x4000, NULL
, HFILL
}},
1295 { "Time synchronized", PROTOCOL_ABBREV
".data.sync", FT_BOOLEAN
, 16,
1296 TFS(&data_statb13names
), 0x2000, NULL
, HFILL
}},
1299 { "Data sorting", PROTOCOL_ABBREV
".data.sorting", FT_BOOLEAN
, 16,
1300 TFS(&data_statb12names
), 0x1000, NULL
, HFILL
}},
1303 { "Trigger detected", PROTOCOL_ABBREV
".data.trigger", FT_BOOLEAN
, 16,
1304 TFS(&data_statb11names
), 0x0800, NULL
, HFILL
}},
1307 { "Configuration changed", PROTOCOL_ABBREV
".data.CFGchange", FT_BOOLEAN
, 16,
1308 TFS(&data_statb10names
), 0x0400, NULL
, HFILL
}},
1310 { &hf_data_statb05to04
,
1311 { "Unlocked time", PROTOCOL_ABBREV
".data.t_unlock", FT_UINT16
, BASE_HEX
,
1312 VALS(data_statb05to04names
), 0x0030, NULL
, HFILL
}},
1314 { &hf_data_statb03to00
,
1315 { "Trigger reason", PROTOCOL_ABBREV
".data.trigger_reason", FT_UINT16
, BASE_HEX
,
1316 VALS(data_statb03to00names
), 0x000F, NULL
, HFILL
}},
1318 /* Data type for command frame */
1320 { "Command", PROTOCOL_ABBREV
".command", FT_UINT16
, BASE_HEX
,
1321 VALS(command_names
), 0x000F, NULL
, HFILL
}}
1324 /* protocol subtree array */
1325 static gint
*ett
[] = {
1347 module_t
*synphasor_module
;
1349 /* register protocol */
1350 proto_synphasor
= proto_register_protocol(PROTOCOL_NAME
,
1351 PROTOCOL_SHORT_NAME
,
1354 /* Registering protocol to be called by another dissector */
1355 synphasor_udp_handle
= new_register_dissector("synphasor", dissect_udp
, proto_synphasor
);
1357 proto_register_field_array(proto_synphasor
, hf
, array_length(hf
));
1358 proto_register_subtree_array(ett
, array_length(ett
));
1360 /* register preferences */
1361 synphasor_module
= prefs_register_protocol(proto_synphasor
, proto_reg_handoff_synphasor
);
1363 /* the port numbers of the lower level protocols */
1364 prefs_register_uint_preference(synphasor_module
, "udp_port", "Synchrophasor UDP port",
1365 "Set the port number for synchrophasor frames over UDP" \
1366 "(if other than the default of 4713)",
1367 10, &global_pref_udp_port
);
1368 prefs_register_uint_preference(synphasor_module
, "tcp_port", "Synchrophasor TCP port",
1369 "Set the port number for synchrophasor frames over TCP" \
1370 "(if other than the default of 4712)",
1371 10, &global_pref_tcp_port
);
1373 /* register the initalization routine */
1374 register_init_routine(&synphasor_init
);
1375 } /* proto_register_synphasor() */
1377 /* called at startup and when the preferences change */
1378 void proto_reg_handoff_synphasor(void)
1380 static gboolean initialized
= FALSE
;
1381 static dissector_handle_t synphasor_tcp_handle
;
1382 static guint current_udp_port
;
1383 static guint current_tcp_port
;
1386 synphasor_tcp_handle
= new_create_dissector_handle(dissect_tcp
, proto_synphasor
);
1391 /* update preferences */
1392 dissector_delete_uint("udp.port", current_udp_port
, synphasor_udp_handle
);
1393 dissector_delete_uint("tcp.port", current_tcp_port
, synphasor_tcp_handle
);
1396 current_udp_port
= global_pref_udp_port
;
1397 current_tcp_port
= global_pref_tcp_port
;
1399 dissector_add_uint("udp.port", current_udp_port
, synphasor_udp_handle
);
1400 dissector_add_uint("tcp.port", current_tcp_port
, synphasor_tcp_handle
);
1401 } /* proto_reg_handoff_synphasor() */