2 * Dissector for IEEE C37.118 synchrophasor frames.
4 * Copyright 2008, Jens Steinhauser <jens.steinhauser@omicron.at>
5 * Copyright 2019, Dwayne Rich <dwayne_rich@selinc.com>
6 * Copyright 2020, Dmitriy Eliseev <eliseev_d@ntcees.ru>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
19 #include <epan/packet.h>
20 #include <epan/crc16-tvb.h>
21 #include <epan/expert.h>
22 #include <epan/proto_data.h>
24 #include <epan/unit_strings.h>
26 #include <wsutil/array.h>
27 #include "packet-tcp.h"
29 #include <wsutil/utf8_entities.h>
31 #define PNAME "IEEE C37.118 Synchrophasor Protocol"
32 #define PSNAME "SYNCHROPHASOR"
33 #define PFNAME "synphasor"
35 /* forward references */
36 void proto_register_synphasor(void);
37 void proto_reg_handoff_synphasor(void);
39 /* global variables */
41 static int proto_synphasor
;
43 /* user preferences */
44 #define SYNPHASOR_TCP_PORT 4712 /* Not IANA registered */
45 #define SYNPHASOR_UDP_PORT 4713 /* Not IANA registered */
47 /* Config 1 & 2 frames have channel names that are all 16 bytes long */
48 /* Config 3 frame channel names have a variable length with a max of 255 characters */
50 #define MAX_NAME_LEN 255
51 #define G_PMU_ID_LEN 16
53 /* the ett... variables hold the state (open/close) of the treeview in the GUI */
54 static int ett_synphasor
; /* root element for this protocol */
55 /* used in the common header */
56 static int ett_frtype
;
57 static int ett_timequal
;
58 /* used for config frames */
60 static int ett_conf_station
;
61 static int ett_conf_format
;
62 static int ett_conf_phnam
;
63 static int ett_conf_annam
;
64 static int ett_conf_dgnam
;
65 static int ett_conf_phconv
;
66 static int ett_conf_phlist
;
67 static int ett_conf_phflags
;
68 static int ett_conf_phmod_flags
;
69 static int ett_conf_ph_user_flags
;
70 static int ett_conf_anconv
;
71 static int ett_conf_anlist
;
72 static int ett_conf_dgmask
;
73 static int ett_conf_chnam
;
74 static int ett_conf_wgs84
;
75 /* used for data frames */
77 static int ett_data_block
;
78 static int ett_data_stat
;
79 static int ett_data_phasors
;
80 static int ett_data_analog
;
81 static int ett_data_digital
;
82 /* used for command frames */
83 static int ett_command
;
84 static int ett_status_word_mask
;
86 /* handles to the header fields hf[] in proto_register_synphasor() */
88 static int hf_sync_frtype
;
89 static int hf_sync_version
;
90 static int hf_station_name_len
;
91 static int hf_station_name
;
92 static int hf_idcode_stream_source
;
93 static int hf_idcode_data_source
;
94 static int hf_g_pmu_id
;
97 static int hf_timeqal_lsdir
;
98 static int hf_timeqal_lsocc
;
99 static int hf_timeqal_lspend
;
100 static int hf_timeqal_timequalindic
;
101 static int hf_fracsec_raw
;
102 static int hf_fracsec_ms
;
103 static int hf_cont_idx
;
104 static int hf_conf_timebase
;
105 static int hf_conf_numpmu
;
106 static int hf_conf_formatb3
;
107 static int hf_conf_formatb2
;
108 static int hf_conf_formatb1
;
109 static int hf_conf_formatb0
;
110 static int hf_conf_chnam_len
;
111 static int hf_conf_chnam
;
112 static int hf_conf_phasor_mod_b15
;
113 static int hf_conf_phasor_mod_b10
;
114 static int hf_conf_phasor_mod_b09
;
115 static int hf_conf_phasor_mod_b08
;
116 static int hf_conf_phasor_mod_b07
;
117 static int hf_conf_phasor_mod_b06
;
118 static int hf_conf_phasor_mod_b05
;
119 static int hf_conf_phasor_mod_b04
;
120 static int hf_conf_phasor_mod_b03
;
121 static int hf_conf_phasor_mod_b02
;
122 static int hf_conf_phasor_mod_b01
;
123 static int hf_conf_phasor_type_b03
;
124 static int hf_conf_phasor_type_b02to00
;
125 static int hf_conf_phasor_user_data
;
126 static int hf_conf_phasor_scale_factor
;
127 static int hf_conf_phasor_angle_offset
;
128 static int hf_conf_analog_scale_factor
;
129 static int hf_conf_analog_offset
;
130 static int hf_conf_pmu_lat
;
131 static int hf_conf_pmu_lon
;
132 static int hf_conf_pmu_elev
;
133 static int hf_conf_pmu_lat_unknown
;
134 static int hf_conf_pmu_lon_unknown
;
135 static int hf_conf_pmu_elev_unknown
;
136 static int hf_conf_svc_class
;
137 static int hf_conf_window
;
138 static int hf_conf_grp_dly
;
139 static int hf_conf_fnom
;
140 static int hf_conf_cfgcnt
;
141 static int hf_data_statb15to14
;
142 static int hf_data_statb13
;
143 static int hf_data_statb12
;
144 static int hf_data_statb11
;
145 static int hf_data_statb10
;
146 static int hf_data_statb09
;
147 static int hf_data_statb08to06
;
148 static int hf_data_statb05to04
;
149 static int hf_data_statb03to00
;
150 static int hf_command
;
151 static int hf_cfg_frame_num
;
153 /* Generated from convert_proto_tree_add_text.pl */
154 static int hf_synphasor_data
;
155 static int hf_synphasor_checksum
;
156 static int hf_synphasor_checksum_status
;
157 static int hf_synphasor_num_phasors
;
158 static int hf_synphasor_num_analog_values
;
159 static int hf_synphasor_num_digital_status_words
;
160 static int hf_synphasor_rate_of_transmission
;
161 static int hf_synphasor_phasor
;
162 static int hf_synphasor_actual_frequency_value
;
163 static int hf_synphasor_rate_change_frequency
;
164 static int hf_synphasor_frequency_deviation_from_nominal
;
165 static int hf_synphasor_analog_value
;
166 static int hf_synphasor_digital_status_word
;
167 static int hf_synphasor_conversion_factor
;
168 static int hf_synphasor_factor_for_analog_value
;
169 static int hf_synphasor_channel_name
;
170 static int hf_synphasor_extended_frame_data
;
171 static int hf_synphasor_unknown_data
;
172 static int hf_synphasor_status_word_mask_normal_state
;
173 static int hf_synphasor_status_word_mask_valid_bits
;
175 static expert_field ei_synphasor_extended_frame_data
;
176 static expert_field ei_synphasor_checksum
;
177 static expert_field ei_synphasor_data_error
;
178 static expert_field ei_synphasor_pmu_not_sync
;
180 static dissector_handle_t synphasor_udp_handle
;
181 static dissector_handle_t synphasor_tcp_handle
;
183 /* the different frame types for this protocol */
193 /* Structures to save CFG frame content. */
195 /* type to indicate the format for (D)FREQ/PHASORS/ANALOG in data frame */
196 typedef enum { integer
, /* 16 bit signed integer */
197 floating_point
/* single precision floating point */
200 typedef enum { rect
, polar
} phasor_notation_e
;
202 typedef enum { V
, A
} unit_e
;
204 /* holds the information required to dissect a single phasor */
206 char name
[MAX_NAME_LEN
+ 1];
208 uint32_t conv
; /* cfg-2 conversion factor in 10^-5 scale */
209 float conv_cfg3
; /* cfg-3 conversion scale factor */
210 float angle_offset_cfg3
; /* cfg-3 angle offset */
213 /* holds the information for an analog value */
215 char name
[MAX_NAME_LEN
+ 1];
216 uint32_t conv
; /* cfg-2 conversion scale factor, user defined scaling (so it's pretty useless) */
217 float conv_cfg3
; /* cfg-3 conversion scale factor */
218 float offset_cfg3
; /* cfg-3 conversion offset */
221 /* holds information required to dissect a single PMU block in a data frame */
223 uint16_t id
; /* (Data Source ID) identifies source of block */
224 char name
[MAX_NAME_LEN
+ 1]; /* holds STN */
225 uint8_t cfg_frame_type
; /* Config Frame Type (1,2,3,...) */
226 data_format format_fr
; /* data format of FREQ and DFREQ */
227 data_format format_ph
; /* data format of PHASORS */
228 data_format format_an
; /* data format of ANALOG */
229 phasor_notation_e phasor_notation
; /* format of the phasors */
230 unsigned fnom
; /* nominal line frequency */
231 unsigned num_dg
; /* number of digital status words */
232 wmem_array_t
*phasors
; /* array of phasor_infos */
233 wmem_array_t
*analogs
; /* array of analog_infos */
236 /* holds the id the configuration comes from an and
237 * an array of config_block members */
239 uint32_t fnum
; /* frame number */
240 uint16_t id
; /* (Stream Source ID) identifies source of stream */
241 uint32_t time_base
; /* Time base - resolution of FRACSEC time stamp. */
242 wmem_array_t
*config_blocks
; /* Contains a config_block struct for
243 * every PMU included in the config frame */
246 /* strings for type bits in SYNC */
247 static const value_string typenames
[] = {
249 { 1, "Header Frame" },
250 { 2, "Configuration Frame 1" },
251 { 3, "Configuration Frame 2" },
252 { 4, "Command Frame" },
253 { 5, "Configuration Frame 3" },
257 /* strings for version bits in SYNC */
258 static const value_string versionnames
[] = {
259 { 1, "Defined in IEEE Std C37.118-2005" },
260 { 2, "Added in IEEE Std C37.118.2-2011" },
264 /* strings for the time quality flags in FRACSEC */
265 static const true_false_string leapseconddir
= {
269 static const value_string timequalcodes
[] = {
270 { 0xF, "Clock failure, time not reliable" },
271 { 0xB, "Clock unlocked, time within 10 s" },
272 { 0xA, "Clock unlocked, time within 1 s" },
273 { 0x9, "Clock unlocked, time within 10^-1 s" },
274 { 0x8, "Clock unlocked, time within 10^-2 s" },
275 { 0x7, "Clock unlocked, time within 10^-3 s" },
276 { 0x6, "Clock unlocked, time within 10^-4 s" },
277 { 0x5, "Clock unlocked, time within 10^-5 s" },
278 { 0x4, "Clock unlocked, time within 10^-6 s" },
279 { 0x3, "Clock unlocked, time within 10^-7 s" },
280 { 0x2, "Clock unlocked, time within 10^-8 s" },
281 { 0x1, "Clock unlocked, time within 10^-9 s" },
282 { 0x0, "Normal operation, clock locked" },
286 /* strings for flags in the FORMAT word of a configuration frame */
287 static const true_false_string conf_formatb123names
= {
288 "32-bit IEEE floating point",
291 static const true_false_string conf_formatb0names
= {
296 /* strings to decode ANUNIT in configuration frame */
297 static const range_string conf_anconvnames
[] = {
298 { 0, 0, "single point-on-wave" },
299 { 1, 1, "rms of analog input" },
300 { 2, 2, "peak of input" },
301 { 3, 4, "undefined" },
302 { 5, 64, "reserved" },
303 { 65, 255, "user defined" },
307 /* strings for the FNOM field */
308 static const true_false_string conf_fnomnames
= {
313 static const true_false_string conf_phasor_mod_b15
= {
314 "Modification applied, type not here defined",
318 static const true_false_string conf_phasor_mod_b10
= {
319 "Pseudo-phasor value (combined from other phasors)",
323 static const true_false_string conf_phasor_mod_b09
= {
324 "Phasor phase adjusted for rotation",
328 static const true_false_string conf_phasor_mod_b08
= {
329 "Phasor phase adjusted for calibration",
333 static const true_false_string conf_phasor_mod_b07
= {
334 "Phasor magnitude adjusted for calibration",
338 static const true_false_string conf_phasor_mod_b06
= {
339 "Filtered without changing sampling",
343 static const true_false_string conf_phasor_mod_b05
= {
344 "Down sampled with non-FIR filter",
348 static const true_false_string conf_phasor_mod_b04
= {
349 "Down sampled with FIR filter",
353 static const true_false_string conf_phasor_mod_b03
= {
354 "Down sampled by reselection",
358 static const true_false_string conf_phasor_mod_b02
= {
359 "Up sampled with extrapolation",
363 static const true_false_string conf_phasor_mod_b01
= {
364 "Up sampled with interpolation",
368 static const value_string conf_phasor_type
[] = {
369 { 0, "Voltage, Zero sequence" },
370 { 1, "Voltage, Positive sequence" },
371 { 2, "Voltage, Negative sequence" },
372 { 3, "Voltage, Reserved" },
373 { 4, "Voltage, Phase A" },
374 { 5, "Voltage, Phase B" },
375 { 6, "Voltage, Phase C" },
376 { 7, "Voltage, Reserved" },
377 { 8, "Current, Zero sequence" },
378 { 9, "Current, Positive sequence" },
379 { 10, "Current, Negative sequence" },
380 { 11, "Current, Reserved" },
381 { 12, "Current, Phase A" },
382 { 13, "Current, Phase B" },
383 { 14, "Current, Phase C" },
384 { 15, "Current, Reserved" },
388 static const true_false_string conf_phasor_type_b03
= {
393 static const value_string conf_phasor_type_b02to00
[] = {
394 { 0, "Zero sequence" },
395 { 1, "Positive sequence"},
396 { 2, "Negative sequence"},
405 static const true_false_string conf_phasor_user_defined
= {
410 /* strings for flags in the STAT word of a data frame */
411 static const value_string data_statb15to14names
[] = {
412 { 0, "Good measurement data, no errors" },
413 { 1, "PMU error, no information about data" },
414 { 2, "PMU in test mode or absent data tags have been inserted (do not use values)" },
415 { 3, "PMU error (do not use values)" },
418 static const true_false_string data_statb13names
= {
419 "Synchronization lost",
420 "Clock is synchronized"
422 static const true_false_string data_statb12names
= {
426 static const true_false_string data_statb11names
= {
430 static const true_false_string data_statb10names
= {
434 static const true_false_string data_statb09names
= {
435 "Data modified by a post-processing device",
438 static const value_string data_statb08to06names
[] = {
439 { 0, "Not used (indicates code from previous version of profile)" },
440 { 1, "Estimated maximum time error < 100 ns" },
441 { 2, "Estimated maximum time error < 1 " UTF8_MICRO_SIGN
"s" },
442 { 3, "Estimated maximum time error < 10 " UTF8_MICRO_SIGN
"s" },
443 { 4, "Estimated maximum time error < 100 " UTF8_MICRO_SIGN
"s" },
444 { 5, "Estimated maximum time error < 1 ms" },
445 { 6, "Estimated maximum time error < 10 ms" },
446 { 7, "Estimated maximum time error > 10 ms or time error unknown" },
449 static const value_string data_statb05to04names
[] = {
450 { 0, "Locked or unlocked less than 10 s"},
451 { 1, "Unlocked for 10-100 s" },
452 { 2, "Unlocked for 100-1000 s" },
453 { 3, "Unlocked for over 1000 s" },
456 static const value_string data_statb03to00names
[] = {
458 { 0x1, "Magnitude low" },
459 { 0x2, "Magnitude high" },
460 { 0x3, "Phase-angel diff" },
461 { 0x4, "Frequency high or low" },
462 { 0x5, "df/dt high" },
465 { 0x8, "User defined" },
466 { 0x9, "User defined" },
467 { 0xA, "User defined" },
468 { 0xB, "User defined" },
469 { 0xC, "User defined" },
470 { 0xD, "User defined" },
471 { 0xE, "User defined" },
472 { 0xF, "User defined" },
476 /* strings to decode the commands (CMD Field) according Table 15, p.26
477 * 0000 0000 0000 0001 - Turn off transmission of data frames
478 * 0000 0000 0000 0010 - Turn on transmission of data frames
479 * 0000 0000 0000 0011 - Send HDR frame
480 * 0000 0000 0000 0100 - Send CFG-1 frame.
481 * 0000 0000 0000 0101 - Send CFG-2 frame.
482 * 0000 0000 0000 0110 - Send CFG-3 frame (optional command).
483 * 0000 0000 0000 1000 - Extended frame.
484 * 0000 0000 xxxx xxxx - All undesignated codes reserved.
485 * 0000 yyyy xxxx xxxx - All codes where yyyy ≠0 available for user designation.
486 * zzzz xxxx xxxx xxxx - All codes where zzzz ≠0 reserved.
488 static const range_string command_names
[] = {
489 { 0x0000, 0x0000, "reserved codes" },
490 { 0x0001, 0x0001, "data transmission off" },
491 { 0x0002, 0x0002, "data transmission on" },
492 { 0x0003, 0x0003, "send HDR frame" },
493 { 0x0004, 0x0004, "send CFG-1 frame" },
494 { 0x0005, 0x0005, "send CFG-2 frame" },
495 { 0x0006, 0x0006, "send CFG-3 frame" },
496 { 0x0007, 0x0007, "reserved codes" },
497 { 0x0008, 0x0008, "extended frame" },
498 { 0x0009, 0x00FF, "reserved codes" },
499 { 0x0100, 0x0FFF, "user designation" },
500 { 0x1000, 0xFFFF, "reserved codes" },
501 { 0x0000, 0x0000, NULL
}
505 /******************************************************************************
507 ******************************************************************************/
509 /* read in the size length for names found in config 3 frames
511 1-255 - length of name
513 static uint8_t get_name_length(tvbuff_t
*tvb
, int offset
)
517 /* read the size of the name */
518 name_length
= tvb_get_uint8(tvb
, offset
);
523 /* Checks the CRC of a synchrophasor frame, 'tvb' has to include the whole
524 * frame, including CRC, the calculated CRC is returned in '*computedcrc'.
526 static bool check_crc(tvbuff_t
*tvb
, uint16_t *computedcrc
)
529 unsigned len
= tvb_get_ntohs(tvb
, 2);
531 crc
= tvb_get_ntohs(tvb
, len
- 2);
532 *computedcrc
= crc16_x25_ccitt_tvb(tvb
, len
- 2);
534 if (crc
== *computedcrc
)
540 /* Dissects a configuration frame (only the most important stuff, tries
541 * to be fast, does no GUI stuff) and returns a pointer to a config_frame
542 * struct that contains all the information from the frame needed to
543 * dissect a DATA frame.
545 * use 'config_frame_free()' to free the config_frame again
547 static config_frame
*config_frame_fast(tvbuff_t
*tvb
)
553 /* get a new frame and initialize it */
554 frame
= wmem_new(wmem_file_scope(), config_frame
);
556 frame
->config_blocks
= wmem_array_new(wmem_file_scope(), sizeof(config_block
));
558 // Start with Stream Source ID - identifies source of stream
560 frame
->id
= tvb_get_ntohs(tvb
, offset
);
562 /* Skip to time base for FRACSEC */
563 offset
+= 11; // high 8 bits reserved for flags, so +1 byte
564 frame
->time_base
= tvb_get_uint24(tvb
, offset
,ENC_BIG_ENDIAN
);
566 /* Next number of PMU blocks */
568 num_pmu
= tvb_get_ntohs(tvb
, offset
);
570 // Start of repeating blocks
574 uint16_t format_flags
;
584 /* initialize the block */
585 block
.phasors
= wmem_array_new(wmem_file_scope(), sizeof(phasor_info
));
586 block
.analogs
= wmem_array_new(wmem_file_scope(), sizeof(analog_info
));
587 /* copy the station name from the tvb to block, and add NULL byte */
588 tvb_memcpy(tvb
, block
.name
, offset
, CHNAM_LEN
); offset
+= CHNAM_LEN
;
589 block
.name
[CHNAM_LEN
] = '\0';
590 block
.cfg_frame_type
= 2;
591 block
.id
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
593 format_flags
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
594 block
.format_fr
= (format_flags
& 0x0008) ? floating_point
: integer
;
595 block
.format_an
= (format_flags
& 0x0004) ? floating_point
: integer
;
596 block
.format_ph
= (format_flags
& 0x0002) ? floating_point
: integer
;
597 block
.phasor_notation
= (format_flags
& 0x0001) ? polar
: rect
;
599 num_ph
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
600 num_an
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
601 num_dg
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
602 block
.num_dg
= num_dg
;
604 /* the offset of the PHUNIT, ANUNIT, and FNOM blocks */
605 phunit
= offset
+ (num_ph
+ num_an
+ num_dg
* CHNAM_LEN
) * CHNAM_LEN
;
606 anunit
= phunit
+ num_ph
* 4;
607 fnom
= anunit
+ num_an
* 4 + num_dg
* 4;
609 /* read num_ph phasor names and conversion factors */
610 for (i
= 0; i
!= num_ph
; i
++) {
614 /* copy the phasor name from the tvb, and add NULL byte */
615 tvb_memcpy(tvb
, pi
.name
, offset
, CHNAM_LEN
); offset
+= CHNAM_LEN
;
616 pi
.name
[CHNAM_LEN
] = '\0';
618 conv
= tvb_get_ntohl(tvb
, phunit
+ 4 * i
);
619 pi
.unit
= conv
& 0xFF000000 ? A
: V
;
620 pi
.conv
= conv
& 0x00FFFFFF;
622 pi
.angle_offset_cfg3
= 0;
624 wmem_array_append_one(block
.phasors
, pi
);
627 /* read num_an analog value names and conversion factors */
628 for (i
= 0; i
!= num_an
; i
++) {
632 /* copy the phasor name from the tvb, and add NULL byte */
633 tvb_memcpy(tvb
, ai
.name
, offset
, CHNAM_LEN
); offset
+= CHNAM_LEN
;
634 ai
.name
[CHNAM_LEN
] = '\0';
636 conv
= tvb_get_ntohl(tvb
, anunit
+ 4 * i
);
641 wmem_array_append_one(block
.analogs
, ai
);
644 /* the names for the bits in the digital status words aren't saved,
645 there is no space to display them in the GUI anyway */
648 block
.fnom
= tvb_get_ntohs(tvb
, fnom
) & 0x0001 ? 50 : 60;
654 wmem_array_append_one(frame
->config_blocks
, block
);
659 } /* config_frame_fast() */
661 /* Dissects a configuration 3 frame (only the most important stuff, tries
662 * to be fast, does no GUI stuff) and returns a pointer to a config_frame
663 * struct that contains all the information from the frame needed to
664 * dissect a DATA frame.
666 * use 'config_frame_free()' to free the config_frame again
668 static config_frame
* config_3_frame_fast(tvbuff_t
*tvb
)
673 phasor_info
*pi
= NULL
;
674 analog_info
*ai
= NULL
;
675 bool frame_not_fragmented
;
677 /* get a new frame and initialize it */
678 frame
= wmem_new(wmem_file_scope(), config_frame
);
680 frame
->config_blocks
= wmem_array_new(wmem_file_scope(), sizeof(config_block
));
682 // Start with Stream Source ID - identifies source of stream
684 frame
->id
= tvb_get_ntohs(tvb
, offset
);
686 /* Skip to CONT_IDX -- Fragmented Frames not supported at this time */
688 frame_not_fragmented
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
) == 0;
690 /* Skip to time base for FRACSEC */
691 offset
+= 3; // high 8 bits reserved for flags, so +1 byte
692 frame
->time_base
= tvb_get_uint24(tvb
, offset
,ENC_BIG_ENDIAN
);
694 /* Skip to number of PMU blocks */
696 num_pmu
= tvb_get_ntohs(tvb
, offset
);
698 /* start of repeating blocks */
700 while ((num_pmu
) && (frame_not_fragmented
)) {
701 uint16_t format_flags
;
709 /* initialize the block */
710 block
.phasors
= wmem_array_new(wmem_file_scope(), sizeof(phasor_info
));
711 block
.analogs
= wmem_array_new(wmem_file_scope(), sizeof(analog_info
));
713 /* copy the station name from the tvb to block, and add NULL byte */
714 /* first byte is name size */
715 name_length
= get_name_length(tvb
, offset
);
718 tvb_memcpy(tvb
, block
.name
, offset
, name_length
);
719 offset
+= name_length
;
721 block
.name
[name_length
] = '\0';
722 block
.cfg_frame_type
= 3;
724 /* Block ID and Global PMU ID */
725 block
.id
= tvb_get_ntohs(tvb
, offset
);
728 /* skip over Global PMU ID */
729 offset
+= G_PMU_ID_LEN
;
731 format_flags
= tvb_get_ntohs(tvb
, offset
);
734 block
.format_fr
= (format_flags
& 0x0008) ? floating_point
: integer
;
735 block
.format_an
= (format_flags
& 0x0004) ? floating_point
: integer
;
736 block
.format_ph
= (format_flags
& 0x0002) ? floating_point
: integer
;
737 block
.phasor_notation
= (format_flags
& 0x0001) ? polar
: rect
;
739 num_ph
= tvb_get_ntohs(tvb
, offset
);
742 num_an
= tvb_get_ntohs(tvb
, offset
);
745 num_dg
= tvb_get_ntohs(tvb
, offset
);
747 block
.num_dg
= num_dg
;
749 /* grab phasor names */
752 pi
= (phasor_info
*)wmem_alloc(wmem_file_scope(), sizeof(phasor_info
)*num_ph
);
754 for (i
= 0; i
!= num_ph
; i
++) {
755 /* copy the phasor name from the tvb, and add NULL byte */
756 name_length
= get_name_length(tvb
, offset
);
759 tvb_memcpy(tvb
, pi
[i
].name
, offset
, name_length
);
760 offset
+= name_length
;
762 pi
[i
].name
[name_length
] = '\0';
766 /* grab analog names */
769 ai
= (analog_info
*)wmem_alloc(wmem_file_scope(), sizeof(analog_info
)*num_an
);
771 for (i
= 0; i
!= num_an
; i
++) {
772 /* copy the phasor name from the tvb, and add NULL byte */
773 name_length
= get_name_length(tvb
, offset
);
776 tvb_memcpy(tvb
, ai
[i
].name
, offset
, name_length
);
777 offset
+= name_length
;
779 ai
[i
].name
[name_length
] = '\0';
783 /* skip digital names */
786 for (i
= 0; i
!= num_dg
* 16; i
++) {
787 name_length
= get_name_length(tvb
, offset
);
788 offset
+= name_length
+ 1;
792 /* get phasor conversion factors */
795 for (i
= 0; i
!= num_ph
; i
++) {
796 uint32_t phasor_unit
;
799 phasor_unit
= tvb_get_ntohl(tvb
, offset
);
800 pi
[i
].unit
= phasor_unit
& 0x00000800 ? A
: V
;
802 pi
[i
].conv_cfg3
= tvb_get_ntohieee_float(tvb
, offset
+ 4);
803 pi
[i
].angle_offset_cfg3
= tvb_get_ntohieee_float(tvb
, offset
+ 8);
805 wmem_array_append_one(block
.phasors
, pi
[i
]);
811 /* get analog conversion factors */
814 for (i
= 0; i
!= num_an
; i
++) {
816 ai
[i
].conv_cfg3
= tvb_get_ntohieee_float(tvb
, offset
);
817 ai
[i
].offset_cfg3
= tvb_get_ntohieee_float(tvb
, offset
+ 4);
819 wmem_array_append_one(block
.analogs
, ai
[i
]);
825 /* skip digital masks */
828 for (i
= 0; i
!= num_dg
; i
++) {
837 block
.fnom
= tvb_get_ntohs(tvb
, offset
) & 0x0001 ? 50 : 60;
840 /* skip CFGCNT - offset ready for next PMU */
843 wmem_array_append_one(frame
->config_blocks
, block
);
848 } /* config_3_frame_fast() */
850 /* Dissects the common header of frames.
852 * Returns the framesize, in contrast to most
853 * other helper functions that return the offset.
855 static int dissect_header(tvbuff_t
*tvb
, proto_tree
*tree
, packet_info
*pinfo
)
857 proto_tree
*temp_tree
;
858 proto_item
*temp_item
;
864 conf
= (config_frame
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_synphasor
, 0);
867 temp_item
= proto_tree_add_item(tree
, hf_sync
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
868 temp_tree
= proto_item_add_subtree(temp_item
, ett_frtype
);
869 proto_tree_add_item(temp_tree
, hf_sync_frtype
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
870 proto_tree_add_item(temp_tree
, hf_sync_version
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
874 proto_tree_add_item(tree
, hf_frsize
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
875 framesize
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
878 proto_tree_add_item(tree
, hf_idcode_stream_source
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
882 proto_tree_add_item(tree
, hf_soc
, tvb
, offset
, 4, ENC_TIME_SECS
| ENC_BIG_ENDIAN
);
886 /* time quality flags */
887 temp_tree
= proto_tree_add_subtree(tree
, tvb
, offset
, 1, ett_timequal
, NULL
, "Time quality flags");
888 proto_tree_add_item(temp_tree
, hf_timeqal_lsdir
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
889 proto_tree_add_item(temp_tree
, hf_timeqal_lsocc
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
890 proto_tree_add_item(temp_tree
, hf_timeqal_lspend
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
891 proto_tree_add_item(temp_tree
, hf_timeqal_timequalindic
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
895 proto_tree_add_item(tree
, hf_fracsec_raw
, tvb
, offset
, 3, ENC_BIG_ENDIAN
);
897 // If exist configuration frame, add fracsec in milliseconds
899 uint32_t fracsec_raw
= tvb_get_uint24(tvb
, offset
, ENC_BIG_ENDIAN
);
900 float fracsec_ms
= 1000.0f
*fracsec_raw
/conf
->time_base
;
901 proto_tree_add_float(tree
, hf_fracsec_ms
, tvb
, offset
, 3, fracsec_ms
);
910 /* Dissects a single phasor for 'dissect_PHASORS()' */
911 static int dissect_single_phasor(tvbuff_t
*tvb
, int offset
,
912 double *mag
, double *phase
, /* returns the resulting values in polar format here */
913 double* real
, double* imag
, /* returns the resulting values in rectangular format here*/
914 double* mag_real_unscaled
, double* phase_imag_unscaled
, /* returns unscaled values*/
915 config_block
*block
, /* information needed to... */
916 phasor_info
* pi
) /* ...dissect the phasor */
918 if (floating_point
== block
->format_ph
) {
919 if (polar
== block
->phasor_notation
) {
921 *mag
= tvb_get_ntohieee_float(tvb
, offset
);
922 *phase
= tvb_get_ntohieee_float(tvb
, offset
+ 4);
924 *real
= (*mag
) * cos(*phase
);
925 *imag
= (*mag
) * sin(*phase
);
929 *real
= tvb_get_ntohieee_float(tvb
, offset
);
930 *imag
= tvb_get_ntohieee_float(tvb
, offset
+ 4);
932 *mag
= sqrt(pow(*real
, 2) + pow(*imag
, 2));
933 *phase
= atan2(*imag
, *real
);
937 if (polar
== block
->phasor_notation
) {
939 *mag_real_unscaled
= tvb_get_ntohs(tvb
, offset
);
940 *phase_imag_unscaled
= tvb_get_ntohis(tvb
, offset
+ 2);
942 /* For fixed-point data in polar format all values are permissible for the magnitude
943 field. However, the angle field is restricted to ±31416. A value of 0x8000 (–32768) used in the angle field
944 will be used to signify absent data.
945 bullet 6.3.1 page 16 IEEE Std C37.118.2-2011
947 if (*phase_imag_unscaled
== -32768) {
948 *phase_imag_unscaled
= NAN
;
949 *mag_real_unscaled
= NAN
;
952 *phase
= *phase_imag_unscaled
/10000.0; /* angle is in radians*10^4 */
954 /* for values in integer format, consider conversation factor */
955 if (block
->cfg_frame_type
== 3){
956 *mag
= (*mag_real_unscaled
* pi
->conv_cfg3
);
957 *phase
= *phase
- pi
->angle_offset_cfg3
;
960 *mag
= (*mag_real_unscaled
* pi
->conv
) * 0.00001;
963 *real
= (*mag
) * cos(*phase
);
964 *imag
= (*mag
) * sin(*phase
);
968 *mag_real_unscaled
= tvb_get_ntohis(tvb
, offset
);
969 *phase_imag_unscaled
= tvb_get_ntohis(tvb
, offset
+ 2);
971 /* For fixed-point data in rectangular format the PDC will use
972 0x8000 (–32768) as the substitute for the absent data.
973 bullet 6.3.1 page 16 IEEE Std C37.118.2-2011
975 if (*mag_real_unscaled
== -32768) {
976 *mag_real_unscaled
= NAN
;
978 if (*phase_imag_unscaled
== -32768) {
979 *phase_imag_unscaled
= NAN
;
982 *mag
= sqrt(pow(*mag_real_unscaled
, 2) + pow(*phase_imag_unscaled
, 2));
983 *phase
= atan2(*phase_imag_unscaled
, *mag_real_unscaled
);
985 /* for values in integer format, consider conversation factor */
986 if (block
->cfg_frame_type
== 3) {
987 *mag
= (*mag
* pi
->conv_cfg3
);
988 *phase
= *phase
- pi
->angle_offset_cfg3
;
991 *mag
= (*mag
* pi
->conv
) * 0.00001;
994 *real
= (*mag
) * cos(*phase
);
995 *imag
= (*mag
) * sin(*phase
);
999 return floating_point
== block
->format_ph
? 8 : 4;
1002 /* used by 'dissect_data_frame()' to dissect the PHASORS field */
1003 static int dissect_PHASORS(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, int offset
)
1005 proto_tree
*phasor_tree
;
1008 int cnt
= wmem_array_get_count(block
->phasors
); /* number of phasors to dissect */
1013 length
= wmem_array_get_count(block
->phasors
) * (floating_point
== block
->format_ph
? 8 : 4);
1014 phasor_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, length
, ett_data_phasors
, NULL
,
1015 "Phasors (%u), notation: %s, format: %s", cnt
,
1016 block
->phasor_notation
? "polar" : "rectangular",
1017 block
->format_ph
? "floating point" : "integer");
1019 /* dissect a phasor for every phasor_info saved in the config_block */
1020 for (j
= 0; j
< cnt
; j
++) {
1021 proto_item
*temp_item
;
1022 double mag
, phase
,real
, imag
;
1023 double mag_real_unscaled
= NAN
, phase_imag_unscaled
= NAN
;
1026 pi
= (phasor_info
*)wmem_array_index(block
->phasors
, j
);
1027 temp_item
= proto_tree_add_string_format(phasor_tree
, hf_synphasor_phasor
, tvb
, offset
,
1028 floating_point
== block
->format_ph
? 8 : 4, pi
->name
,
1029 "Phasor #%u: \"%s\"", j
+ 1, pi
->name
);
1031 offset
+= dissect_single_phasor(tvb
, offset
,
1032 &mag
, &phase
, &real
, &imag
,
1033 &mag_real_unscaled
, &phase_imag_unscaled
,
1036 #define SYNP_ANGLE "\xe2\x88\xa0" /* 8736 / 0x2220 */
1038 char phasor_unit
= V
== pi
->unit
? 'V' : 'A';
1040 proto_item_append_text(temp_item
, ", %10.3F%c " SYNP_ANGLE
"%7.3F" UTF8_DEGREE_SIGN
" alt %7.3F+j%7.3F%c",
1041 mag
, phasor_unit
, phase
* 180.0 / G_PI
,
1042 real
, imag
, phasor_unit
);
1043 if (integer
== block
->format_ph
) {
1044 proto_item_append_text(temp_item
, "; unscaled: %5.0F, %5.0F",
1045 mag_real_unscaled
, phase_imag_unscaled
);
1052 /* used by 'dissect_data_frame()' to dissect the FREQ and DFREQ fields */
1053 static int dissect_DFREQ(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, int offset
)
1055 if (floating_point
== block
->format_fr
) {
1056 proto_tree_add_item(tree
, hf_synphasor_actual_frequency_value
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1059 /* In new version of the standard IEEE Std C37.118.2-2011: "Can be 16-bit integer or IEEE floating point, same as FREQ above."
1060 * --> no scaling factor is applied to DFREQ
1062 proto_tree_add_item(tree
, hf_synphasor_rate_change_frequency
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1068 tmp
= tvb_get_ntohs(tvb
, offset
);
1069 proto_tree_add_int_format_value(tree
, hf_synphasor_frequency_deviation_from_nominal
, tvb
, offset
, 2, tmp
,
1070 "%dmHz (actual frequency: %.3fHz)", tmp
, block
->fnom
+ (tmp
/ 1000.0));
1073 tmp
= tvb_get_ntohs(tvb
, offset
);
1074 proto_tree_add_float_format_value(tree
, hf_synphasor_rate_change_frequency
, tvb
, offset
, 2, (float)(tmp
/ 100.0), "%.3fHz/s", tmp
/ 100.0); offset
+= 2;
1079 /* used by 'dissect_data_frame()' to dissect the ANALOG field */
1080 static int dissect_ANALOG(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, int offset
)
1082 proto_tree
*analog_tree
;
1085 int cnt
= wmem_array_get_count(block
->analogs
); /* number of analog values to dissect */
1090 length
= wmem_array_get_count(block
->analogs
) * (floating_point
== block
->format_an
? 4 : 2);
1091 analog_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, length
, ett_data_analog
, NULL
,
1092 "Analog values (%u)", cnt
);
1094 for (j
= 0; j
< cnt
; j
++) {
1095 proto_item
*temp_item
;
1096 analog_info
*ai
= (analog_info
*)wmem_array_index(block
->analogs
, j
);
1098 temp_item
= proto_tree_add_string_format(analog_tree
, hf_synphasor_analog_value
, tvb
, offset
,
1099 floating_point
== block
->format_an
? 4 : 2, ai
->name
,
1100 "Analog value #%u: \"%s\"", j
+ 1, ai
->name
);
1102 if (block
->cfg_frame_type
== 3)
1104 if (floating_point
== block
->format_an
) {
1107 tmp
= tvb_get_ntohieee_float(tvb
, offset
);
1110 proto_item_append_text(temp_item
, ", %.3f", tmp
);
1113 /* the "standard" doesn't say if this is signed or unsigned,
1114 * so I just use int16_t */
1118 tmp_i
= tvb_get_ntohs(tvb
, offset
);
1121 tmp_f
= (tmp_i
* ai
->conv_cfg3
) + ai
->offset_cfg3
;
1123 proto_item_append_text(temp_item
, ", %.3f", tmp_f
);
1128 if (floating_point
== block
->format_an
) {
1129 float tmp
= tvb_get_ntohieee_float(tvb
, offset
); offset
+= 4;
1130 proto_item_append_text(temp_item
, ", %.3f", tmp
);
1133 /* the "standard" doesn't say if this is signed or unsigned,
1134 * so I just use int16_t; the scaling of the conversion factor
1135 * is also "user defined", so I just write it after the analog value */
1136 int16_t tmp
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
1137 proto_item_append_text(temp_item
, ", %" PRId16
" (conversion factor: %#06x)",
1145 /* used by 'dissect_data_frame()' to dissect the DIGITAL field */
1146 static int dissect_DIGITAL(tvbuff_t
*tvb
, proto_tree
*tree
, config_block
*block
, int offset
)
1149 int cnt
= block
->num_dg
; /* number of digital status words to dissect */
1154 tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, cnt
* 2, ett_data_digital
, NULL
,
1155 "Digital status words (%u)", cnt
);
1157 for (j
= 0; j
< cnt
; j
++) {
1158 uint16_t tmp
= tvb_get_ntohs(tvb
, offset
);
1159 proto_tree_add_uint_format(tree
, hf_synphasor_digital_status_word
, tvb
, offset
, 2, tmp
, "Digital status word #%u: 0x%04x", j
+ 1, tmp
);
1165 /* used by 'dissect_config_frame()' to dissect the PHUNIT field */
1166 static int dissect_PHUNIT(tvbuff_t
*tvb
, proto_tree
*tree
, int offset
, int cnt
)
1168 proto_tree
*temp_tree
;
1174 temp_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, 4 * cnt
, ett_conf_phconv
, NULL
,
1175 "Phasor conversion factors (%u)", cnt
);
1177 /* Conversion factor for phasor channels. Four bytes for each phasor.
1178 * MSB: 0 = voltage, 1 = current
1179 * Lower 3 Bytes: unsigned 24-bit word in 10^-5 V or A per bit to scale the phasor value
1181 for (i
= 0; i
< cnt
; i
++) {
1182 uint32_t tmp
= tvb_get_ntohl(tvb
, offset
);
1183 proto_tree_add_uint_format(temp_tree
, hf_synphasor_conversion_factor
, tvb
, offset
, 4,
1184 tmp
, "#%u factor: %u * 10^-5, unit: %s",
1187 tmp
& 0xFF000000 ? "Ampere" : "Volt");
1194 /* used by 'dissect_config_3_frame()' to dissect the PHSCALE field */
1195 static int dissect_PHSCALE(tvbuff_t
*tvb
, proto_tree
*tree
, int offset
, int cnt
)
1197 proto_tree
*temp_tree
;
1204 temp_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, 12 * cnt
, ett_conf_phconv
, NULL
,
1205 "Phasor scaling and data flags (%u)", cnt
);
1207 for (i
= 0; i
< cnt
; i
++) {
1208 proto_tree
*single_phasor_scaling_and_flags_tree
;
1209 proto_tree
*phasor_flag1_tree
;
1210 proto_tree
*phasor_flag2_tree
;
1211 proto_tree
*data_flag_tree
;
1213 single_phasor_scaling_and_flags_tree
= proto_tree_add_subtree_format(temp_tree
, tvb
, offset
, 12,
1214 ett_conf_phlist
, NULL
,
1215 "Phasor #%u", i
+ 1);
1217 data_flag_tree
= proto_tree_add_subtree_format(single_phasor_scaling_and_flags_tree
, tvb
, offset
, 4,
1218 ett_conf_phflags
, NULL
, "Phasor Data flags: %s",
1219 val_to_str_const(tvb_get_uint8(tvb
, offset
+ 2), conf_phasor_type
, "Unknown"));
1221 /* first and second bytes - phasor modification flags*/
1222 phasor_flag1_tree
= proto_tree_add_subtree_format(data_flag_tree
, tvb
, offset
, 2, ett_conf_phmod_flags
,
1223 NULL
, "Modification Flags: 0x%04x",
1224 tvb_get_ntohs(tvb
, offset
));
1226 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b15
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1227 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b10
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1228 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b09
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1229 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b08
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1230 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b07
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1231 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b06
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1232 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b05
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1233 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b04
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1234 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b03
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1235 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b02
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1236 proto_tree_add_item(phasor_flag1_tree
, hf_conf_phasor_mod_b01
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1239 /* third byte - phasor type*/
1240 proto_tree_add_item(data_flag_tree
, hf_conf_phasor_type_b03
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1241 proto_tree_add_item(data_flag_tree
, hf_conf_phasor_type_b02to00
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1244 /* fourth byte - user designation*/
1245 phasor_flag2_tree
= proto_tree_add_subtree_format(data_flag_tree
, tvb
, offset
, 1, ett_conf_ph_user_flags
,
1246 NULL
, "User designated flags: 0x%02x",
1247 tvb_get_uint8(tvb
, offset
));
1249 proto_tree_add_item(phasor_flag2_tree
, hf_conf_phasor_user_data
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1252 /* phasor scalefactor */
1253 proto_tree_add_item(single_phasor_scaling_and_flags_tree
, hf_conf_phasor_scale_factor
,
1254 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1257 /* angle adjustment */
1258 proto_tree_add_item(single_phasor_scaling_and_flags_tree
, hf_conf_phasor_angle_offset
,
1259 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1266 /* used by 'dissect_config_frame()' to dissect the ANUNIT field */
1267 static int dissect_ANUNIT(tvbuff_t
*tvb
, proto_tree
*tree
, int offset
, int cnt
)
1269 proto_item
*temp_item
;
1270 proto_tree
*temp_tree
;
1276 temp_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, 4 * cnt
, ett_conf_anconv
, NULL
,
1277 "Analog values conversion factors (%u)", cnt
);
1279 /* Conversion factor for analog channels. Four bytes for each analog value.
1280 * MSB: see 'synphasor_conf_anconvnames' in 'synphasor_strings.c'
1281 * Lower 3 Bytes: signed 24-bit word, user-defined scaling
1283 for (i
= 0; i
< cnt
; i
++) {
1284 int32_t tmp
= tvb_get_ntohl(tvb
, offset
);
1285 temp_item
= proto_tree_add_uint_format(temp_tree
, hf_synphasor_factor_for_analog_value
, tvb
, offset
, 4,
1286 tmp
, "Factor for analog value #%i: %s",
1288 try_rval_to_str((tmp
>> 24) & 0x000000FF, conf_anconvnames
));
1291 if ( tmp
& 0x00800000) /* sign bit set */
1294 proto_item_append_text(temp_item
, ", value: %" PRId32
, tmp
);
1302 /* used by 'dissect_config_3_frame()' to dissect the ANSCALE field */
1303 static int dissect_ANSCALE(tvbuff_t
*tvb
, proto_tree
*tree
, int offset
, int cnt
)
1305 proto_tree
*temp_tree
;
1312 temp_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, 8 * cnt
, ett_conf_anconv
, NULL
,
1313 "Analog values conversion factors (%u)", cnt
);
1315 /* Conversion factor for analog channels. Four bytes for each analog value.
1316 * MSB: see 'synphasor_conf_anconvnames' in 'synphasor_strings.c'
1317 * Lower 3 Bytes: signed 24-bit word, user-defined scaling
1319 for (i
= 0; i
< cnt
; i
++) {
1320 proto_tree
*single_analog_scalefactor_tree
;
1322 single_analog_scalefactor_tree
= proto_tree_add_subtree_format(temp_tree
, tvb
, offset
, 8,
1323 ett_conf_phlist
, NULL
,
1324 "Analog #%u", i
+ 1);
1326 /* analog scalefactor */
1327 proto_tree_add_item(single_analog_scalefactor_tree
, hf_conf_analog_scale_factor
,
1328 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1331 /* angle adjustment */
1332 proto_tree_add_item(single_analog_scalefactor_tree
, hf_conf_analog_offset
,
1333 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1340 /* used by 'dissect_config_frame()' to dissect the DIGUNIT field */
1341 static int dissect_DIGUNIT(tvbuff_t
*tvb
, proto_tree
*tree
, int offset
, int cnt
)
1343 proto_tree
*temp_tree
, *mask_tree
;
1349 temp_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, 4 * cnt
, ett_conf_dgmask
, NULL
,
1350 "Masks for digital status words (%u)", cnt
);
1352 /* Mask words for digital status words. Two 16-bit words for each digital word. The first
1353 * indicates the normal status of the inputs, the second indicated the valid bits in
1356 for (i
= 0; i
< cnt
; i
++) {
1358 mask_tree
= proto_tree_add_subtree_format(temp_tree
, tvb
, offset
, 4, ett_status_word_mask
, NULL
, "Mask for status word #%u: ", i
+ 1);
1359 proto_tree_add_item(mask_tree
, hf_synphasor_status_word_mask_normal_state
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
1360 proto_tree_add_item(mask_tree
, hf_synphasor_status_word_mask_valid_bits
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
1366 /* used by 'dissect_config_frame()' to dissect the "channel name"-fields */
1367 static int dissect_CHNAM(tvbuff_t
*tvb
, proto_tree
*tree
, int offset
, int cnt
, const char *prefix
)
1369 proto_tree
*temp_tree
;
1375 temp_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, CHNAM_LEN
* cnt
, ett_conf_phnam
, NULL
,
1376 "%ss (%u)", prefix
, cnt
);
1378 /* dissect the 'cnt' channel names */
1379 for (i
= 0; i
< cnt
; i
++) {
1381 str
= (char *)tvb_get_string_enc(wmem_packet_scope(), tvb
, offset
, CHNAM_LEN
, ENC_ASCII
);
1382 proto_tree_add_string_format(temp_tree
, hf_synphasor_channel_name
, tvb
, offset
, CHNAM_LEN
,
1383 str
, "%s #%i: \"%s\"", prefix
, i
+1, str
);
1384 offset
+= CHNAM_LEN
;
1390 /* used by 'dissect_config_3_frame()' to dissect the "channel name"-fields */
1391 static int dissect_config_3_CHNAM(tvbuff_t
*tvb
, proto_tree
*tree
, int offset
, int cnt
, const char *prefix
)
1393 proto_tree
*temp_tree
, *chnam_tree
;
1395 uint8_t name_length
;
1397 int subsection_length
= 0;
1403 /* get the subsection length */
1404 temp_offset
= offset
;
1405 for (i
= 0; i
< cnt
; i
++) {
1406 name_length
= get_name_length(tvb
, temp_offset
);
1407 /* count the length byte and the actual name */
1408 subsection_length
+= name_length
+ 1;
1409 temp_offset
+= name_length
+ 1;
1412 temp_tree
= proto_tree_add_subtree_format(tree
, tvb
, offset
, subsection_length
, ett_conf_phnam
,
1413 NULL
, "%ss (%u)", prefix
, cnt
);
1415 /* dissect the 'cnt' channel names */
1416 for (i
= 0; i
< cnt
; i
++) {
1419 name_length
= get_name_length(tvb
, offset
);
1420 str
= (char *)tvb_get_string_enc(wmem_packet_scope(), tvb
, offset
+ 1, name_length
, ENC_ASCII
);
1421 chnam_tree
= proto_tree_add_subtree_format(temp_tree
, tvb
, offset
, name_length
+ 1, ett_conf
,
1422 NULL
, "%s #%i: \"%s\"", prefix
, i
+ 1, str
);
1424 proto_tree_add_item(chnam_tree
, hf_conf_chnam_len
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1427 proto_tree_add_string(chnam_tree
, hf_conf_chnam
, tvb
, offset
, 1, str
);
1428 offset
+= name_length
;
1434 /* dissects a configuration frame (type 1 and 2) and adds fields to 'config_item' */
1435 static int dissect_config_frame(tvbuff_t
*tvb
, proto_item
*config_item
)
1437 proto_tree
*config_tree
;
1439 uint16_t num_pmu
, j
;
1441 proto_item_set_text (config_item
, "Configuration data");
1442 config_tree
= proto_item_add_subtree(config_item
, ett_conf
);
1444 /* TIME_BASE and NUM_PMU */
1445 offset
+= 1; /* skip the reserved byte */
1446 proto_tree_add_item(config_tree
, hf_conf_timebase
, tvb
, offset
, 3, ENC_BIG_ENDIAN
); offset
+= 3;
1447 proto_tree_add_item(config_tree
, hf_conf_numpmu
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1448 /* add number of included PMUs to the text in the list view */
1449 num_pmu
= tvb_get_ntohs(tvb
, offset
); offset
+= 2;
1450 proto_item_append_text(config_item
, ", %"PRIu16
" PMU(s) included", num_pmu
);
1452 /* dissect the repeating PMU blocks */
1453 for (j
= 0; j
< num_pmu
; j
++) {
1454 uint16_t num_ph
, num_an
, num_dg
;
1455 proto_item
*station_item
;
1456 proto_tree
*station_tree
;
1457 proto_tree
*temp_tree
;
1460 int oldoffset
= offset
; /* to calculate the length of the whole PMU block later */
1462 /* STN with new tree to add the rest of the PMU block */
1463 str
= (char *)tvb_get_string_enc(wmem_packet_scope(), tvb
, offset
, CHNAM_LEN
, ENC_ASCII
);
1464 station_tree
= proto_tree_add_subtree_format(config_tree
, tvb
, offset
, CHNAM_LEN
,
1465 ett_conf_station
, &station_item
,
1466 "Station #%i: \"%s\"", j
+ 1, str
);
1467 offset
+= CHNAM_LEN
;
1470 proto_tree_add_item(station_tree
, hf_idcode_data_source
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
1473 temp_tree
= proto_tree_add_subtree(station_tree
, tvb
, offset
, 2, ett_conf_format
, NULL
,
1474 "Data format in data frame");
1475 proto_tree_add_item(temp_tree
, hf_conf_formatb3
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1476 proto_tree_add_item(temp_tree
, hf_conf_formatb2
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1477 proto_tree_add_item(temp_tree
, hf_conf_formatb1
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1478 proto_tree_add_item(temp_tree
, hf_conf_formatb0
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1481 /* PHNMR, ANNMR, DGNMR */
1482 num_ph
= tvb_get_ntohs(tvb
, offset
);
1483 num_an
= tvb_get_ntohs(tvb
, offset
+ 2);
1484 num_dg
= tvb_get_ntohs(tvb
, offset
+ 4);
1485 proto_tree_add_uint(station_tree
, hf_synphasor_num_phasors
, tvb
, offset
, 2, num_ph
);
1486 proto_tree_add_uint(station_tree
, hf_synphasor_num_analog_values
, tvb
, offset
+ 2, 2, num_an
);
1487 proto_tree_add_uint(station_tree
, hf_synphasor_num_digital_status_words
, tvb
, offset
+ 4, 2, num_dg
);
1490 /* CHNAM, the channel names */
1491 offset
= dissect_CHNAM(tvb
, station_tree
, offset
, num_ph
, "Phasor name" );
1492 offset
= dissect_CHNAM(tvb
, station_tree
, offset
, num_an
, "Analog value" );
1493 offset
= dissect_CHNAM(tvb
, station_tree
, offset
, num_dg
* 16, "Digital status label");
1495 /* PHUNIT, ANUINT and DIGUNIT */
1496 offset
= dissect_PHUNIT (tvb
, station_tree
, offset
, num_ph
);
1497 offset
= dissect_ANUNIT (tvb
, station_tree
, offset
, num_an
);
1498 offset
= dissect_DIGUNIT(tvb
, station_tree
, offset
, num_dg
);
1500 /* FNOM and CFGCNT */
1501 proto_tree_add_item(station_tree
, hf_conf_fnom
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
1502 proto_tree_add_item(station_tree
, hf_conf_cfgcnt
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
1504 /* set the correct length for the "Station :" item */
1505 proto_item_set_len(station_item
, offset
- oldoffset
);
1506 } /* for() PMU blocks */
1510 int16_t tmp
= tvb_get_ntohis(tvb
, offset
);
1512 proto_tree_add_int_format_value(config_tree
, hf_synphasor_rate_of_transmission
, tvb
, offset
, 2, tmp
,
1513 "%d frame(s) per second", tmp
);
1515 proto_tree_add_int_format_value(config_tree
, hf_synphasor_rate_of_transmission
, tvb
, offset
, 2, tmp
,
1516 "1 frame per %d second(s)", (int16_t)-tmp
);
1521 } /* dissect_config_frame() */
1523 /* dissects a configuration frame type 3 and adds fields to 'config_item' */
1524 static int dissect_config_3_frame(tvbuff_t
*tvb
, proto_item
*config_item
)
1526 proto_tree
*config_tree
, *wgs84_tree
;
1528 uint16_t num_pmu
, j
;
1530 proto_item_set_text(config_item
, "Configuration data");
1531 config_tree
= proto_item_add_subtree(config_item
, ett_conf
);
1534 proto_tree_add_item(config_tree
, hf_cont_idx
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1537 /* TIME_BASE and NUM_PMU */
1538 offset
+= 1; /* skip the reserved byte */
1540 proto_tree_add_item(config_tree
, hf_conf_timebase
, tvb
, offset
, 3, ENC_BIG_ENDIAN
);
1543 proto_tree_add_item(config_tree
, hf_conf_numpmu
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1545 /* add number of included PMUs to the text in the list view */
1546 num_pmu
= tvb_get_ntohs(tvb
, offset
);
1549 proto_item_append_text(config_item
, ", %"PRIu16
" PMU(s) included", num_pmu
);
1551 /* dissect the repeating PMU blocks */
1552 for (j
= 0; j
< num_pmu
; j
++) {
1553 uint16_t num_ph
, num_an
, num_dg
, i
;
1554 uint8_t name_length
;
1556 float pmu_lat
, pmu_long
, pmu_elev
;
1557 proto_item
*station_item
;
1558 proto_tree
*station_tree
;
1559 proto_tree
*temp_tree
;
1560 char *str
, *service_class
;
1561 char *unspecified_location
= "Unspecified Location";
1562 uint8_t g_pmu_id_array
[G_PMU_ID_LEN
];
1564 oldoffset
= offset
; /* to calculate the length of the whole PMU block later */
1566 /* STN with new tree to add the rest of the PMU block */
1567 name_length
= get_name_length(tvb
, offset
);
1568 str
= (char *)tvb_get_string_enc(wmem_packet_scope(), tvb
, offset
+ 1, name_length
, ENC_ASCII
);
1569 station_tree
= proto_tree_add_subtree_format(config_tree
, tvb
, offset
, name_length
+ 1,
1570 ett_conf_station
, &station_item
,
1571 "Station #%i: \"%s\"", j
+ 1, str
);
1573 /* Station Name Length */
1574 proto_tree_add_item(station_tree
, hf_station_name_len
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1578 proto_tree_add_string(station_tree
, hf_station_name
, tvb
, offset
, 1, str
);
1579 offset
+= name_length
;
1582 proto_tree_add_item(station_tree
, hf_idcode_data_source
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1586 /* A 128 bit display as raw bytes */
1587 for (i
= 0; i
< G_PMU_ID_LEN
; i
++) {
1588 g_pmu_id_array
[i
] = tvb_get_uint8(tvb
, offset
+ i
);
1591 proto_tree_add_bytes_format(station_tree
, hf_g_pmu_id
, tvb
, offset
, G_PMU_ID_LEN
, 0,
1592 "Global PMU ID (raw bytes): %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
1593 g_pmu_id_array
[0], g_pmu_id_array
[1], g_pmu_id_array
[2], g_pmu_id_array
[3],
1594 g_pmu_id_array
[4], g_pmu_id_array
[5], g_pmu_id_array
[6], g_pmu_id_array
[7],
1595 g_pmu_id_array
[8], g_pmu_id_array
[9], g_pmu_id_array
[10], g_pmu_id_array
[11],
1596 g_pmu_id_array
[12], g_pmu_id_array
[13], g_pmu_id_array
[14], g_pmu_id_array
[15]);
1597 offset
+= G_PMU_ID_LEN
;
1600 temp_tree
= proto_tree_add_subtree(station_tree
, tvb
, offset
, 2, ett_conf_format
, NULL
,
1601 "Data format in data frame");
1602 proto_tree_add_item(temp_tree
, hf_conf_formatb3
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1603 proto_tree_add_item(temp_tree
, hf_conf_formatb2
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1604 proto_tree_add_item(temp_tree
, hf_conf_formatb1
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1605 proto_tree_add_item(temp_tree
, hf_conf_formatb0
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1608 /* PHNMR, ANNMR, DGNMR */
1609 num_ph
= tvb_get_ntohs(tvb
, offset
);
1610 num_an
= tvb_get_ntohs(tvb
, offset
+ 2);
1611 num_dg
= tvb_get_ntohs(tvb
, offset
+ 4);
1612 proto_tree_add_uint(station_tree
, hf_synphasor_num_phasors
, tvb
, offset
, 2, num_ph
);
1613 proto_tree_add_uint(station_tree
, hf_synphasor_num_analog_values
, tvb
, offset
+ 2, 2, num_an
);
1614 proto_tree_add_uint(station_tree
, hf_synphasor_num_digital_status_words
, tvb
, offset
+ 4, 2, num_dg
);
1617 /* CHNAM, the channel names */
1618 offset
= dissect_config_3_CHNAM(tvb
, station_tree
, offset
, num_ph
, "Phasor name");
1619 offset
= dissect_config_3_CHNAM(tvb
, station_tree
, offset
, num_an
, "Analog value");
1620 offset
= dissect_config_3_CHNAM(tvb
, station_tree
, offset
, num_dg
* 16, "Digital label");
1622 /* PHUNIT, ANUINT and DIGUNIT */
1623 offset
= dissect_PHSCALE(tvb
, station_tree
, offset
, num_ph
);
1624 offset
= dissect_ANSCALE(tvb
, station_tree
, offset
, num_an
);
1626 offset
= dissect_DIGUNIT(tvb
, station_tree
, offset
, num_dg
);
1628 /* subtree for coordinate info*/
1629 wgs84_tree
= proto_tree_add_subtree_format(station_tree
, tvb
, offset
, 12, ett_conf_wgs84
, NULL
,
1630 "World Geodetic System 84 data");
1632 /* preview latitude, longitude, and elevation values */
1633 /* INFINITY is an unspecified location, otherwise use the actual float value */
1634 pmu_lat
= tvb_get_ntohieee_float(tvb
, offset
);
1635 pmu_long
= tvb_get_ntohieee_float(tvb
, offset
+ 4);
1636 pmu_elev
= tvb_get_ntohieee_float(tvb
, offset
+ 8);
1639 if (isinf(pmu_lat
)) {
1640 proto_tree_add_float_format_value(wgs84_tree
, hf_conf_pmu_lat_unknown
, tvb
, offset
,
1641 4, INFINITY
, "%s", unspecified_location
);
1644 proto_tree_add_item(wgs84_tree
, hf_conf_pmu_lat
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1649 if (isinf(pmu_long
)) {
1650 proto_tree_add_float_format_value(wgs84_tree
, hf_conf_pmu_lon_unknown
, tvb
, offset
,
1651 4, INFINITY
, "%s", unspecified_location
);
1654 proto_tree_add_item(wgs84_tree
, hf_conf_pmu_lon
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1659 if (isinf(pmu_elev
)) {
1660 proto_tree_add_float_format_value(wgs84_tree
, hf_conf_pmu_elev_unknown
, tvb
, offset
,
1661 4, INFINITY
, "%s", unspecified_location
);
1664 proto_tree_add_item(wgs84_tree
, hf_conf_pmu_elev
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1669 service_class
= (char *)tvb_get_string_enc(wmem_packet_scope(), tvb
, offset
, 1, ENC_ASCII
);
1670 if ((strcmp(service_class
, "P") == 0) || (strcmp(service_class
, "p") == 0)) {
1671 proto_tree_add_string(station_tree
, hf_conf_svc_class
, tvb
, offset
, 1, "Protection");
1673 else if ((strcmp(service_class
, "M") == 0) || (strcmp(service_class
, "m") == 0)) {
1674 proto_tree_add_string(station_tree
, hf_conf_svc_class
, tvb
, offset
, 1, "Monitoring");
1677 proto_tree_add_string(station_tree
, hf_conf_svc_class
, tvb
, offset
, 1, "Unknown");
1682 proto_tree_add_item(station_tree
, hf_conf_window
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1686 proto_tree_add_item(station_tree
, hf_conf_grp_dly
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1689 /* FNOM and CFGCNT */
1690 proto_tree_add_item(station_tree
, hf_conf_fnom
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1693 proto_tree_add_item(station_tree
, hf_conf_cfgcnt
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1696 /* set the correct length for the "Station :" item */
1697 proto_item_set_len(station_item
, offset
- oldoffset
);
1698 } /* for() PMU blocks */
1702 int16_t tmp
= tvb_get_ntohis(tvb
, offset
);
1704 proto_tree_add_int_format_value(config_tree
, hf_synphasor_rate_of_transmission
, tvb
, offset
, 2, tmp
,
1705 "%d frame(s) per second", tmp
);
1708 proto_tree_add_int_format_value(config_tree
, hf_synphasor_rate_of_transmission
, tvb
, offset
, 2, tmp
,
1709 "1 frame per %d second(s)", (int16_t)-tmp
);
1715 } /* dissect_config_3_frame() */
1717 /* calculates the size (in bytes) of a data frame that the config_block describes */
1718 #define SYNP_BLOCKSIZE(x) (2 /* STAT */ \
1719 + wmem_array_get_count((x).phasors) * (integer == (x).format_ph ? 4 : 8) /* PHASORS */ \
1720 + (integer == (x).format_fr ? 4 : 8) /* (D)FREQ */ \
1721 + wmem_array_get_count((x).analogs) * (integer == (x).format_an ? 2 : 4) /* ANALOG */ \
1722 + (x).num_dg * 2) /* DIGITAL */
1724 /* Dissects a data frame */
1725 static int dissect_data_frame(tvbuff_t
*tvb
,
1726 proto_item
*data_item
, /* all items are placed beneath this item */
1727 packet_info
*pinfo
) /* used to find the data from a CFG-2 or CFG-3 frame */
1729 proto_tree
*data_tree
;
1734 proto_item_set_text(data_item
, "Measurement data");
1735 data_tree
= proto_item_add_subtree(data_item
, ett_data
);
1737 /* search for configuration information to dissect the frame */
1739 bool config_found
= false;
1740 conf
= (config_frame
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_synphasor
, 0);
1743 /* check if the size of the current frame is the
1744 size of the frame the config_frame describes */
1745 size_t reported_size
= 0;
1746 for (i
= 0; i
< wmem_array_get_count(conf
->config_blocks
); i
++) {
1747 config_block
*block
= (config_block
*)wmem_array_index(conf
->config_blocks
, i
);
1748 reported_size
+= SYNP_BLOCKSIZE(*block
);
1751 if (tvb_reported_length(tvb
) == reported_size
) {
1752 // Add link to CFG Frame
1753 proto_item
* item
= proto_tree_add_uint(data_tree
, hf_cfg_frame_num
, tvb
, 0,0, conf
->fnum
);
1754 proto_item_set_generated(item
);
1755 config_found
= true;
1759 if (!config_found
) {
1760 proto_item_append_text(data_item
, ", no configuration frame found");
1765 /* dissect a PMU block for every config_block in the frame */
1766 for (i
= 0; i
< wmem_array_get_count(conf
->config_blocks
); i
++) {
1767 config_block
*block
= (config_block
*)wmem_array_index(conf
->config_blocks
, i
);
1769 proto_tree
*block_tree
= proto_tree_add_subtree_format(data_tree
, tvb
, offset
, SYNP_BLOCKSIZE(*block
),
1770 ett_data_block
, NULL
,
1771 "Station: \"%s\"", block
->name
);
1774 proto_tree
*temp_tree
= proto_tree_add_subtree(block_tree
, tvb
, offset
, 2, ett_data_stat
, NULL
, "Flags");
1776 proto_item
*temp_item
= proto_tree_add_item(temp_tree
, hf_data_statb15to14
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1777 uint16_t flag_bits
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
) >> 14; // Get bits 15-14
1778 if (flag_bits
!= 0) {
1779 expert_add_info(pinfo
, temp_item
, &ei_synphasor_data_error
);
1781 temp_item
= proto_tree_add_item(temp_tree
, hf_data_statb13
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1782 flag_bits
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
); // Get flag bits
1783 if ((flag_bits
>> 13)&1) { // Check 13 bit
1784 expert_add_info(pinfo
, temp_item
, &ei_synphasor_pmu_not_sync
);
1786 proto_tree_add_item(temp_tree
, hf_data_statb12
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1787 proto_tree_add_item(temp_tree
, hf_data_statb11
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1788 proto_tree_add_item(temp_tree
, hf_data_statb10
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1789 proto_tree_add_item(temp_tree
, hf_data_statb09
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1790 proto_tree_add_item(temp_tree
, hf_data_statb08to06
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1791 proto_tree_add_item(temp_tree
, hf_data_statb05to04
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1792 proto_tree_add_item(temp_tree
, hf_data_statb03to00
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1795 /* PHASORS, (D)FREQ, ANALOG, and DIGITAL */
1796 offset
= dissect_PHASORS(tvb
, block_tree
, block
, offset
);
1797 offset
= dissect_DFREQ (tvb
, block_tree
, block
, offset
);
1798 offset
= dissect_ANALOG (tvb
, block_tree
, block
, offset
);
1799 offset
= dissect_DIGITAL(tvb
, block_tree
, block
, offset
);
1802 } /* dissect_data_frame() */
1804 /* Dissects a command frame and adds fields to config_item.
1806 * 'pinfo' is used to add the type of command
1807 * to the INFO column in the packet list.
1809 static int dissect_command_frame(tvbuff_t
*tvb
,
1810 proto_item
*command_item
,
1813 proto_tree
*command_tree
;
1814 unsigned tvbsize
= tvb_reported_length(tvb
);
1817 proto_item_set_text(command_item
, "Command data");
1818 command_tree
= proto_item_add_subtree(command_item
, ett_command
);
1821 proto_tree_add_item(command_tree
, hf_command
, tvb
, 0, 2, ENC_BIG_ENDIAN
);
1823 s
= rval_to_str_const(tvb_get_ntohs(tvb
, 0), command_names
, "invalid command");
1824 col_append_str(pinfo
->cinfo
, COL_INFO
, ", ");
1825 col_append_str(pinfo
->cinfo
, COL_INFO
, s
);
1828 if (tvb_get_ntohs(tvb
, 0) == 0x0008) {
1829 /* Command: Extended Frame, the extra data is ok */
1830 proto_item
*ti
= proto_tree_add_item(command_tree
, hf_synphasor_extended_frame_data
, tvb
, 2, tvbsize
- 2, ENC_NA
);
1832 expert_add_info(pinfo
, ti
, &ei_synphasor_extended_frame_data
);
1835 proto_tree_add_item(command_tree
, hf_synphasor_unknown_data
, tvb
, 2, tvbsize
- 2, ENC_NA
);
1839 } /* dissect_command_frame() */
1841 /* Dissects the header (common to all types of frames) and then calls
1842 * one of the subdissectors (declared above) for the rest of the frame.
1844 static int dissect_common(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
1848 unsigned tvbsize
= tvb_reported_length(tvb
);
1850 /* some heuristics */
1851 if (tvbsize
< 17 /* 17 bytes = header frame with only a
1852 NULL character, useless but valid */
1853 || tvb_get_uint8(tvb
, 0) != 0xAA) /* every synchrophasor frame starts with 0xAA */
1856 /* write the protocol name to the info column */
1857 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PSNAME
);
1859 frame_type
= tvb_get_uint8(tvb
, 1) >> 4;
1861 col_set_str(pinfo
->cinfo
, COL_INFO
, val_to_str_const(frame_type
, typenames
, "invalid packet type"));
1863 /* CFG-2, CFG3, and DATA frames need special treatment during the first run:
1864 * For CFG-2 & CFG-3 frames, a 'config_frame' struct is created to hold the
1865 * information necessary to decode DATA frames. A pointer to this
1866 * struct is saved in the conversation and is copied to the
1867 * per-packet information if a DATA frame is dissected.
1869 if (!pinfo
->fd
->visited
) {
1870 if (CFG2
== frame_type
&&
1871 check_crc(tvb
, &crc
)) {
1872 conversation_t
*conversation
;
1874 /* fill the config_frame */
1875 config_frame
*frame
= config_frame_fast(tvb
);
1876 frame
->fnum
= pinfo
->num
;
1878 /* find a conversation, create a new one if none exists */
1879 conversation
= find_or_create_conversation(pinfo
);
1881 /* remove data from a previous CFG-2 frame, only
1882 * the most recent configuration frame is relevant */
1883 if (conversation_get_proto_data(conversation
, proto_synphasor
))
1884 conversation_delete_proto_data(conversation
, proto_synphasor
);
1886 conversation_add_proto_data(conversation
, proto_synphasor
, frame
);
1888 else if ((CFG3
== frame_type
) && check_crc(tvb
, &crc
)) {
1889 conversation_t
*conversation
;
1890 config_frame
*frame
;
1892 /* fill the config_frame */
1893 frame
= config_3_frame_fast(tvb
);
1894 frame
->fnum
= pinfo
->num
;
1896 /* find a conversation, create a new one if none exists */
1897 conversation
= find_or_create_conversation(pinfo
);
1899 /* remove data from a previous CFG-3 frame, only
1900 * the most recent configuration frame is relevant */
1901 if (conversation_get_proto_data(conversation
, proto_synphasor
)) {
1902 conversation_delete_proto_data(conversation
, proto_synphasor
);
1905 conversation_add_proto_data(conversation
, proto_synphasor
, frame
);
1907 // Add conf to any frame for dissection fracsec
1908 conversation_t
*conversation
= find_conversation_pinfo(pinfo
, 0);
1910 config_frame
*conf
= (config_frame
*)conversation_get_proto_data(conversation
, proto_synphasor
);
1911 /* no problem if 'conf' is NULL, the frame dissector checks this again */
1912 p_add_proto_data(wmem_file_scope(), pinfo
, proto_synphasor
, 0, conf
);
1914 } /* if (!visited) */
1917 proto_tree
*synphasor_tree
;
1918 proto_item
*temp_item
;
1919 proto_item
*sub_item
;
1926 temp_item
= proto_tree_add_item(tree
, proto_synphasor
, tvb
, 0, -1, ENC_NA
);
1927 proto_item_append_text(temp_item
, ", %s", val_to_str_const(frame_type
, typenames
,
1928 ", invalid packet type"));
1930 /* synphasor_tree is where from now on all new elements for this protocol get added */
1931 synphasor_tree
= proto_item_add_subtree(temp_item
, ett_synphasor
);
1932 // Add pinfo for dissection fracsec
1933 framesize
= dissect_header(tvb
, synphasor_tree
, pinfo
);
1934 offset
= 14; /* header is 14 bytes long */
1936 /* check CRC, call appropriate subdissector for the rest of the frame if CRC is correct*/
1937 sub_item
= proto_tree_add_item(synphasor_tree
, hf_synphasor_data
, tvb
, offset
, tvbsize
- 16, ENC_NA
);
1938 crc_good
= check_crc(tvb
, &crc
);
1939 proto_tree_add_checksum(synphasor_tree
, tvb
, tvbsize
- 2, hf_synphasor_checksum
, hf_synphasor_checksum_status
, &ei_synphasor_checksum
,
1940 pinfo
, crc16_x25_ccitt_tvb(tvb
, tvb_get_ntohs(tvb
, 2) - 2), ENC_BIG_ENDIAN
, PROTO_CHECKSUM_VERIFY
);
1942 proto_item_append_text(sub_item
, ", not dissected because of wrong checksum");
1945 /* create a new tvb to pass to the subdissector
1946 '-16': length of header + 2 CRC bytes */
1947 sub_tvb
= tvb_new_subset_length_caplen(tvb
, offset
, tvbsize
- 16, framesize
- 16);
1949 /* call subdissector */
1950 switch (frame_type
) {
1952 dissect_data_frame(sub_tvb
, sub_item
, pinfo
);
1954 case HEADER
: /* no further dissection is done/needed */
1955 proto_item_append_text(sub_item
, "Header Frame");
1959 dissect_config_frame(sub_tvb
, sub_item
);
1962 dissect_command_frame(sub_tvb
, sub_item
, pinfo
);
1965 /* Note: The C37.118-2.2001 stanadard is vague on how to handle fragmented frames.
1966 Until further clarification is given, fragmented frames with the CONT_IDX
1967 are not supported. */
1968 if (tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
) != 0) {
1969 proto_item_append_text(sub_item
, ", CFG-3 Fragmented Frame (Not Supported)");
1972 dissect_config_3_frame(sub_tvb
, sub_item
);
1976 proto_item_append_text(sub_item
, " of unknown type");
1978 proto_item_append_text(temp_item
, " [correct]");
1981 /* remaining 2 bytes are the CRC */
1984 return tvb_reported_length(tvb
);
1985 } /* dissect_common() */
1987 /* called for synchrophasors over UDP */
1988 static int dissect_udp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1990 return dissect_common(tvb
, pinfo
, tree
, data
);
1993 /* callback for 'tcp_dissect_pdus()' to give it the length of the frame */
1994 static unsigned get_pdu_length(packet_info
*pinfo _U_
, tvbuff_t
*tvb
,
1995 int offset
, void *data _U_
)
1997 return tvb_get_ntohs(tvb
, offset
+ 2);
2000 static int dissect_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
2002 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, 4, get_pdu_length
, dissect_common
, data
);
2004 return tvb_reported_length(tvb
);
2007 /*******************************************************************/
2008 /* after this line: Wireshark Register Routines */
2009 /*******************************************************************/
2011 /* Register Synchrophasor Protocol with Wireshark*/
2012 void proto_register_synphasor(void)
2014 static hf_register_info hf
[] = {
2017 { "Synchronization word", "synphasor.sync", FT_UINT16
, BASE_HEX
,
2018 NULL
, 0x0, NULL
, HFILL
}},
2020 /* Flags in the Sync word */
2022 { "Frame Type", "synphasor.frtype", FT_UINT16
, BASE_HEX
,
2023 VALS(typenames
), 0x0070, NULL
, HFILL
}},
2026 { "Version", "synphasor.version", FT_UINT16
, BASE_DEC
,
2027 VALS(versionnames
), 0x000F, NULL
, HFILL
}},
2030 { "Framesize", "synphasor.frsize", FT_UINT16
, BASE_DEC
| BASE_UNIT_STRING
,
2031 UNS(&units_byte_bytes
), 0x0, NULL
, HFILL
}},
2033 { &hf_station_name_len
,
2034 { "Station name length", "synphasor.station_name_len", FT_UINT8
,
2035 BASE_DEC
| BASE_UNIT_STRING
, UNS(&units_byte_bytes
), 0x0, NULL
, HFILL
}},
2038 { "Station name", "synphasor.station_name", FT_STRING
, BASE_NONE
,
2039 NULL
, 0x0, NULL
, HFILL
}},
2041 { &hf_idcode_stream_source
,
2042 { "PMU/DC ID number (Stream source ID)", "synphasor.idcode_stream_source", FT_UINT16
, BASE_DEC
,
2043 NULL
, 0x0, NULL
, HFILL
}},
2045 { &hf_idcode_data_source
,
2046 { "PMU/DC ID number (Data source ID)", "synphasor.idcode_data_source", FT_UINT16
, BASE_DEC
,
2047 NULL
, 0x0, NULL
, HFILL
}},
2050 { "Global PMU ID (raw hex bytes)", "synphasor.gpmuid", FT_BYTES
, BASE_NONE
,
2051 NULL
, 0x0, NULL
, HFILL
}},
2054 { "SOC time stamp", "synphasor.soc", FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_UTC
,
2055 NULL
, 0x0, NULL
, HFILL
}},
2057 /* Time quality flags in fracsec */
2058 { &hf_timeqal_lsdir
,
2059 { "Leap second direction", "synphasor.timeqal.lsdir", FT_BOOLEAN
, 8,
2060 TFS(&leapseconddir
), 0x40, NULL
, HFILL
}},
2062 { &hf_timeqal_lsocc
,
2063 { "Leap second occurred", "synphasor.timeqal.lsocc", FT_BOOLEAN
, 8,
2064 NULL
, 0x20, NULL
, HFILL
}},
2066 { &hf_timeqal_lspend
,
2067 { "Leap second pending", "synphasor.timeqal.lspend", FT_BOOLEAN
, 8,
2068 NULL
, 0x10, NULL
, HFILL
}},
2070 { &hf_timeqal_timequalindic
,
2071 { "Message Time Quality indicator code", "synphasor.timeqal.timequalindic", FT_UINT8
, BASE_HEX
,
2072 VALS(timequalcodes
), 0x0F, NULL
, HFILL
}},
2074 /* Fraction of second */
2076 { "Fraction of second (raw)", "synphasor.fracsec_raw", FT_UINT24
, BASE_DEC
,
2077 NULL
, 0x0, NULL
, HFILL
}},
2080 { "Fraction of second", "synphasor.fracsec_ms", FT_FLOAT
, BASE_NONE
| BASE_UNIT_STRING
,
2081 UNS(&units_millisecond_milliseconds
), 0x0, NULL
, HFILL
}},
2083 /* Data types for configuration frames */
2085 { "Continuation index", "synphasor.conf.contindx", FT_UINT16
, BASE_DEC
,
2086 NULL
, 0x0, NULL
, HFILL
}},
2088 { &hf_conf_timebase
,
2089 { "Resolution of fractional second time stamp", "synphasor.conf.timebase", FT_UINT24
, BASE_DEC
,
2090 NULL
, 0x0, NULL
, HFILL
}},
2093 { "Number of PMU blocks included in the frame", "synphasor.conf.numpmu", FT_UINT16
, BASE_DEC
,
2094 NULL
, 0x0, NULL
, HFILL
}},
2096 /* Bits in the FORMAT word */
2097 { &hf_conf_formatb3
,
2098 { "FREQ/DFREQ format", "synphasor.conf.dfreq_format", FT_BOOLEAN
, 16,
2099 TFS(&conf_formatb123names
), 0x8, NULL
, HFILL
}},
2101 { &hf_conf_formatb2
,
2102 { "Analog values format", "synphasor.conf.analog_format", FT_BOOLEAN
, 16,
2103 TFS(&conf_formatb123names
), 0x4, NULL
, HFILL
}},
2105 { &hf_conf_formatb1
,
2106 { "Phasor format", "synphasor.conf.phasor_format", FT_BOOLEAN
, 16,
2107 TFS(&conf_formatb123names
), 0x2, NULL
, HFILL
}},
2109 { &hf_conf_formatb0
,
2110 { "Phasor notation", "synphasor.conf.phasor_notation", FT_BOOLEAN
, 16,
2111 TFS(&conf_formatb0names
), 0x1, NULL
, HFILL
}},
2113 { &hf_conf_chnam_len
,
2114 { "Channel name length", "synphasor.conf.chnam_len", FT_UINT8
,
2115 BASE_DEC
| BASE_UNIT_STRING
, UNS(&units_byte_bytes
), 0x0, NULL
, HFILL
}},
2118 { "Channel name", "synphasor.conf.chnam", FT_STRING
, BASE_NONE
,
2119 NULL
, 0x0, NULL
, HFILL
}},
2121 { &hf_conf_phasor_mod_b15
,
2122 { "Modification", "synphasor.conf.phasor_mod.type_not_def", FT_BOOLEAN
, 16,
2123 TFS(&conf_phasor_mod_b15
), 0x8000, NULL
, HFILL
}},
2125 { &hf_conf_phasor_mod_b10
,
2126 { "Modification", "synphasor.conf.phasor_mod.pseudo_phasor", FT_BOOLEAN
, 16,
2127 TFS(&conf_phasor_mod_b10
), 0x0400, NULL
, HFILL
}},
2129 { &hf_conf_phasor_mod_b09
,
2130 { "Modification", "synphasor.conf.phasor_mod.phase_rotation", FT_BOOLEAN
, 16,
2131 TFS(&conf_phasor_mod_b09
), 0x0200, NULL
, HFILL
}},
2133 { &hf_conf_phasor_mod_b08
,
2134 { "Modification", "synphasor.conf.phasor_mod.phase_calibration", FT_BOOLEAN
, 16,
2135 TFS(&conf_phasor_mod_b08
), 0x0100, NULL
, HFILL
}},
2137 { &hf_conf_phasor_mod_b07
,
2138 { "Modification", "synphasor.conf.phasor_mod.mag_calibration", FT_BOOLEAN
, 16,
2139 TFS(&conf_phasor_mod_b07
), 0x0080, NULL
, HFILL
}},
2141 { &hf_conf_phasor_mod_b06
,
2142 { "Modification", "synphasor.conf.phasor_mod.filtered", FT_BOOLEAN
, 16,
2143 TFS(&conf_phasor_mod_b06
), 0x0040, NULL
, HFILL
}},
2145 { &hf_conf_phasor_mod_b05
,
2146 { "Modification", "synphasor.conf.phasor_mod.downsampled", FT_BOOLEAN
, 16,
2147 TFS(&conf_phasor_mod_b05
), 0x0020, NULL
, HFILL
}},
2149 { &hf_conf_phasor_mod_b04
,
2150 { "Modification", "synphasor.conf.phasor_mod.downsampled_fir", FT_BOOLEAN
, 16,
2151 TFS(&conf_phasor_mod_b04
), 0x0010, NULL
, HFILL
}},
2153 { &hf_conf_phasor_mod_b03
,
2154 { "Modification", "synphasor.conf.phasor_mod.downsampled_reselect", FT_BOOLEAN
, 16,
2155 TFS(&conf_phasor_mod_b03
), 0x0008, NULL
, HFILL
}},
2157 { &hf_conf_phasor_mod_b02
,
2158 { "Modification", "synphasor.conf.phasor_mod.upsampled_extrapolation", FT_BOOLEAN
, 16,
2159 TFS(&conf_phasor_mod_b02
), 0x0004, NULL
, HFILL
}},
2161 { &hf_conf_phasor_mod_b01
,
2162 { "Modification", "synphasor.conf.phasor_mod.upsampled_interpolation", FT_BOOLEAN
, 16,
2163 TFS(&conf_phasor_mod_b01
), 0x0002, NULL
, HFILL
}},
2165 { &hf_conf_phasor_type_b03
,
2166 { "Phasor Type", "synphasor.conf.phasor_type", FT_BOOLEAN
, 8,
2167 TFS(&conf_phasor_type_b03
), 0x8, NULL
, HFILL
}},
2169 { &hf_conf_phasor_type_b02to00
,
2170 { "Phasor Type", "synphasor.conf.phasor_component", FT_UINT8
, BASE_HEX
,
2171 VALS(conf_phasor_type_b02to00
), 0x7, NULL
, HFILL
}},
2173 { &hf_conf_phasor_user_data
,
2174 { "Binary format", "synphasor.conf.phasor_user_flags", FT_BOOLEAN
, 8,
2175 TFS(&conf_phasor_user_defined
), 0xff, NULL
, HFILL
}},
2177 { &hf_conf_phasor_scale_factor
,
2178 { "Phasor scale factor", "synphasor.conf.phasor_scale_factor", FT_FLOAT
,
2179 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2181 { &hf_conf_phasor_angle_offset
,
2182 { "Phasor angle offset", "synphasor.conf.phasor_angle_offset", FT_FLOAT
,
2183 BASE_NONE
| BASE_UNIT_STRING
, UNS(&units_degree_degrees
), 0x0, NULL
, HFILL
}},
2185 { &hf_conf_analog_scale_factor
,
2186 { "Analog scale factor", "synphasor.conf.analog_scale_factor", FT_FLOAT
,
2187 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2189 { &hf_conf_analog_offset
,
2190 { "Analog offset", "synphasor.conf.analog_offset", FT_FLOAT
,
2191 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2194 { "PMU Latitude", "synphasor.conf.pmu_latitude", FT_FLOAT
,
2195 BASE_NONE
| BASE_UNIT_STRING
, UNS(&units_degree_degrees
), 0x0, NULL
, HFILL
}},
2198 { "PMU Longitude", "synphasor.conf.pmu_longitude", FT_FLOAT
,
2199 BASE_NONE
| BASE_UNIT_STRING
, UNS(&units_degree_degrees
), 0x0, NULL
, HFILL
}},
2201 { &hf_conf_pmu_elev
,
2202 { "PMU Elevation", "synphasor.conf.pmu_elevation", FT_FLOAT
,
2203 BASE_NONE
| BASE_UNIT_STRING
, UNS(&units_meter_meters
), 0x0, NULL
, HFILL
}},
2205 { &hf_conf_pmu_lat_unknown
,
2206 { "PMU Latitude", "synphasor.conf.pmu_latitude", FT_FLOAT
, BASE_NONE
,
2207 NULL
, 0x0, NULL
, HFILL
}},
2209 { &hf_conf_pmu_lon_unknown
,
2210 { "PMU Longitude", "synphasor.conf.pmu_longitude", FT_FLOAT
, BASE_NONE
,
2211 NULL
, 0x0, NULL
, HFILL
}},
2213 { &hf_conf_pmu_elev_unknown
,
2214 { "PMU Elevation", "synphasor.conf.pmu_elevation", FT_FLOAT
, BASE_NONE
,
2215 NULL
, 0x0, NULL
, HFILL
}},
2217 { &hf_conf_svc_class
,
2218 { "Service class", "synphasor.conf.svc_class", FT_STRING
, BASE_NONE
,
2219 NULL
, 0x0, NULL
, HFILL
}},
2222 { "PM window length", "synphasor.conf.window", FT_UINT32
,
2223 BASE_DEC
| BASE_UNIT_STRING
, UNS(&units_microsecond_microseconds
), 0x0, NULL
, HFILL
}},
2226 { "PM group delay", "synphasor.conf.grp_dly", FT_UINT32
,
2227 BASE_DEC
| BASE_UNIT_STRING
, UNS(&units_microsecond_microseconds
), 0x0, NULL
, HFILL
}},
2230 { "Nominal line frequency", "synphasor.conf.fnom", FT_BOOLEAN
, 16,
2231 TFS(&conf_fnomnames
), 0x0001, NULL
, HFILL
}},
2234 { "Configuration change count", "synphasor.conf.cfgcnt", FT_UINT16
, BASE_DEC
,
2235 NULL
, 0, NULL
, HFILL
}},
2237 /* Data types for data frames */
2238 /* Link to CFG Frame */
2239 { &hf_cfg_frame_num
,
2240 { "Dissected using configuration from frame", "synphasor.data.conf_frame", FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,"", HFILL
}},
2242 /* Flags in the STAT word */
2243 { &hf_data_statb15to14
,
2244 { "Data error", "synphasor.data.status", FT_UINT16
, BASE_HEX
,
2245 VALS(data_statb15to14names
), 0xC000, NULL
, HFILL
}},
2248 { "Time synchronized", "synphasor.data.sync", FT_BOOLEAN
, 16,
2249 TFS(&data_statb13names
), 0x2000, NULL
, HFILL
}},
2252 { "Data sorting", "synphasor.data.sorting", FT_BOOLEAN
, 16,
2253 TFS(&data_statb12names
), 0x1000, NULL
, HFILL
}},
2256 { "Trigger detected", "synphasor.data.trigger", FT_BOOLEAN
, 16,
2257 TFS(&data_statb11names
), 0x0800, NULL
, HFILL
}},
2260 { "Configuration changed", "synphasor.data.CFGchange", FT_BOOLEAN
, 16,
2261 TFS(&data_statb10names
), 0x0400, NULL
, HFILL
}},
2264 { "Data modified indicator", "synphasor.data.data_modified", FT_BOOLEAN
, 16,
2265 TFS(&data_statb09names
), 0x0200, NULL
, HFILL
}},
2267 { &hf_data_statb08to06
,
2268 { "PMU Time Quality", "synphasor.data.pmu_tq", FT_UINT16
, BASE_HEX
,
2269 VALS(data_statb08to06names
), 0x01C0, NULL
, HFILL
}},
2271 { &hf_data_statb05to04
,
2272 { "Unlocked time", "synphasor.data.t_unlock", FT_UINT16
, BASE_HEX
,
2273 VALS(data_statb05to04names
), 0x0030, NULL
, HFILL
}},
2275 { &hf_data_statb03to00
,
2276 { "Trigger reason", "synphasor.data.trigger_reason", FT_UINT16
, BASE_HEX
,
2277 VALS(data_statb03to00names
), 0x000F, NULL
, HFILL
}},
2279 /* Data type for command frame */
2281 { "Command", "synphasor.command", FT_UINT16
, BASE_HEX
|BASE_RANGE_STRING
,
2282 RVALS(command_names
), 0x0, NULL
, HFILL
}},
2284 /* Generated from convert_proto_tree_add_text.pl */
2285 { &hf_synphasor_data
, { "Data", "synphasor.data", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2286 { &hf_synphasor_checksum
, { "Checksum", "synphasor.checksum", FT_UINT16
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
2287 { &hf_synphasor_checksum_status
, { "Checksum Status", "synphasor.checksum.status", FT_UINT8
, BASE_NONE
, VALS(proto_checksum_vals
), 0x0, NULL
, HFILL
}},
2288 { &hf_synphasor_num_phasors
, { "Number of phasors", "synphasor.num_phasors", FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
2289 { &hf_synphasor_num_analog_values
, { "Number of analog values", "synphasor.num_analog_values", FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
2290 { &hf_synphasor_num_digital_status_words
, { "Number of digital status words", "synphasor.num_digital_status_words", FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
2291 { &hf_synphasor_rate_of_transmission
, { "Rate of transmission", "synphasor.rate_of_transmission", FT_INT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
2292 { &hf_synphasor_phasor
, { "Phasor", "synphasor.phasor", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2293 { &hf_synphasor_actual_frequency_value
, { "Actual frequency value", "synphasor.actual_frequency_value", FT_FLOAT
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_hz
), 0x0, NULL
, HFILL
}},
2294 { &hf_synphasor_rate_change_frequency
, { "Rate of change of frequency", "synphasor.rate_change_frequency", FT_FLOAT
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_hz_s
), 0x0, NULL
, HFILL
}},
2295 { &hf_synphasor_frequency_deviation_from_nominal
, { "Frequency deviation from nominal", "synphasor.frequency_deviation_from_nominal", FT_INT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
2296 { &hf_synphasor_analog_value
, { "Analog value", "synphasor.analog_value", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2297 { &hf_synphasor_digital_status_word
, { "Digital status word", "synphasor.digital_status_word", FT_UINT16
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
2298 { &hf_synphasor_conversion_factor
, { "conversion factor", "synphasor.conversion_factor", FT_UINT32
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
2299 { &hf_synphasor_factor_for_analog_value
, { "Factor for analog value", "synphasor.factor_for_analog_value", FT_UINT32
, BASE_DEC
, NULL
, 0x000000FF, NULL
, HFILL
}},
2300 { &hf_synphasor_channel_name
, { "Channel name", "synphasor.channel_name", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2301 { &hf_synphasor_extended_frame_data
, { "Extended frame data", "synphasor.extended_frame_data", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2302 { &hf_synphasor_unknown_data
, { "Unknown data", "synphasor.data.unknown", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
2303 { &hf_synphasor_status_word_mask_normal_state
, { "Normal state", "synphasor.status_word_mask.normal_state", FT_UINT16
, BASE_HEX
, NULL
, 0xFFFF, NULL
, HFILL
}},
2304 { &hf_synphasor_status_word_mask_valid_bits
, { "Valid bits", "synphasor.status_word_mask.valid_bits", FT_UINT16
, BASE_HEX
, NULL
, 0xFFFF, NULL
, HFILL
}},
2307 /* protocol subtree array */
2308 static int *ett
[] = {
2321 &ett_conf_phmod_flags
,
2322 &ett_conf_ph_user_flags
,
2335 &ett_status_word_mask
2338 static ei_register_info ei
[] = {
2339 { &ei_synphasor_extended_frame_data
, { "synphasor.extended_frame_data.unaligned", PI_PROTOCOL
, PI_WARN
, "Size not multiple of 16-bit word", EXPFILL
}},
2340 { &ei_synphasor_checksum
, { "synphasor.bad_checksum", PI_CHECKSUM
, PI_ERROR
, "Bad checksum", EXPFILL
}},
2341 { &ei_synphasor_data_error
, { "synphasor.data_error", PI_RESPONSE_CODE
, PI_NOTE
, "Data Error flag set", EXPFILL
}},
2342 { &ei_synphasor_pmu_not_sync
, { "synphasor.pmu_not_sync", PI_RESPONSE_CODE
, PI_NOTE
, "PMU not sync flag set", EXPFILL
}},
2345 expert_module_t
* expert_synphasor
;
2347 /* register protocol */
2348 proto_synphasor
= proto_register_protocol(PNAME
, PSNAME
, PFNAME
);
2350 /* Registering protocol to be called by another dissector */
2351 synphasor_udp_handle
= register_dissector("synphasor", dissect_udp
, proto_synphasor
);
2352 synphasor_tcp_handle
= register_dissector("synphasor.tcp", dissect_tcp
, proto_synphasor
);
2354 proto_register_field_array(proto_synphasor
, hf
, array_length(hf
));
2355 proto_register_subtree_array(ett
, array_length(ett
));
2356 expert_synphasor
= expert_register_protocol(proto_synphasor
);
2357 expert_register_field_array(expert_synphasor
, ei
, array_length(ei
));
2359 } /* proto_register_synphasor() */
2361 /* called at startup and when the preferences change */
2362 void proto_reg_handoff_synphasor(void)
2364 dissector_add_for_decode_as("rtacser.data", synphasor_udp_handle
);
2365 dissector_add_uint_with_preference("udp.port", SYNPHASOR_UDP_PORT
, synphasor_udp_handle
);
2366 dissector_add_uint_with_preference("tcp.port", SYNPHASOR_TCP_PORT
, synphasor_tcp_handle
);
2367 } /* proto_reg_handoff_synphasor() */
2370 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2375 * indent-tabs-mode: t
2378 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2379 * :indentSize=8:tabSize=8:noTabs=false: