Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-synphasor.c
blob9d05d20b3f33615ae1f2c68ce9efe67f411d066f
1 /* packet-synphasor.c
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
15 #include "config.h"
17 #include <math.h>
19 #include <epan/packet.h>
20 #include <epan/crc16-tvb.h>
21 #include <epan/expert.h>
22 #include <epan/proto_data.h>
23 #include <epan/tfs.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 */
49 #define CHNAM_LEN 16
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 */
59 static int ett_conf;
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 */
76 static int ett_data;
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() */
87 static int hf_sync;
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;
95 static int hf_frsize;
96 static int hf_soc;
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 */
184 enum FrameType {
185 DATA = 0,
186 HEADER,
187 CFG1,
188 CFG2,
189 CMD,
190 CFG3
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 */
198 } data_format;
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 */
205 typedef struct {
206 char name[MAX_NAME_LEN + 1];
207 unit_e unit;
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 */
211 } phasor_info;
213 /* holds the information for an analog value */
214 typedef struct {
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 */
219 } analog_info;
221 /* holds information required to dissect a single PMU block in a data frame */
222 typedef struct {
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 */
234 } config_block;
236 /* holds the id the configuration comes from an and
237 * an array of config_block members */
238 typedef struct {
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 */
244 } config_frame;
246 /* strings for type bits in SYNC */
247 static const value_string typenames[] = {
248 { 0, "Data Frame" },
249 { 1, "Header Frame" },
250 { 2, "Configuration Frame 1" },
251 { 3, "Configuration Frame 2" },
252 { 4, "Command Frame" },
253 { 5, "Configuration Frame 3" },
254 { 0, NULL }
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" },
261 { 0, NULL }
264 /* strings for the time quality flags in FRACSEC */
265 static const true_false_string leapseconddir = {
266 "Add",
267 "Delete"
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" },
283 { 0 , NULL }
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",
289 "16-bit integer"
291 static const true_false_string conf_formatb0names = {
292 "polar",
293 "rectangular"
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" },
304 { 0, 0, NULL }
307 /* strings for the FNOM field */
308 static const true_false_string conf_fnomnames = {
309 "50Hz",
310 "60Hz"
313 static const true_false_string conf_phasor_mod_b15 = {
314 "Modification applied, type not here defined",
315 "None"
318 static const true_false_string conf_phasor_mod_b10 = {
319 "Pseudo-phasor value (combined from other phasors)",
320 "None"
323 static const true_false_string conf_phasor_mod_b09 = {
324 "Phasor phase adjusted for rotation",
325 "None"
328 static const true_false_string conf_phasor_mod_b08 = {
329 "Phasor phase adjusted for calibration",
330 "None"
333 static const true_false_string conf_phasor_mod_b07 = {
334 "Phasor magnitude adjusted for calibration",
335 "None"
338 static const true_false_string conf_phasor_mod_b06 = {
339 "Filtered without changing sampling",
340 "None"
343 static const true_false_string conf_phasor_mod_b05 = {
344 "Down sampled with non-FIR filter",
345 "None"
348 static const true_false_string conf_phasor_mod_b04 = {
349 "Down sampled with FIR filter",
350 "None"
353 static const true_false_string conf_phasor_mod_b03 = {
354 "Down sampled by reselection",
355 "None"
358 static const true_false_string conf_phasor_mod_b02 = {
359 "Up sampled with extrapolation",
360 "None"
363 static const true_false_string conf_phasor_mod_b01 = {
364 "Up sampled with interpolation",
365 "None"
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" },
385 { 0, NULL }
388 static const true_false_string conf_phasor_type_b03 = {
389 "Current",
390 "Voltage"
393 static const value_string conf_phasor_type_b02to00[] = {
394 { 0, "Zero sequence" },
395 { 1, "Positive sequence"},
396 { 2, "Negative sequence"},
397 { 3, "Reserved" },
398 { 4, "Phase A" },
399 { 5, "Phase B" },
400 { 6, "Phase C" },
401 { 7, "Reserved" },
402 { 0, NULL }
405 static const true_false_string conf_phasor_user_defined = {
406 "Flags set",
407 "No flags set"
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)" },
416 { 0, NULL }
418 static const true_false_string data_statb13names = {
419 "Synchronization lost",
420 "Clock is synchronized"
422 static const true_false_string data_statb12names = {
423 "By arrival",
424 "By timestamp"
426 static const true_false_string data_statb11names = {
427 "Trigger detected",
428 "No trigger"
430 static const true_false_string data_statb10names = {
431 "Within 1 minute",
432 "No"
434 static const true_false_string data_statb09names = {
435 "Data modified by a post-processing device",
436 "Data not modified"
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" },
447 { 0, NULL }
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" },
454 { 0, NULL }
456 static const value_string data_statb03to00names[] = {
457 { 0x0, "Manual" },
458 { 0x1, "Magnitude low" },
459 { 0x2, "Magnitude high" },
460 { 0x3, "Phase-angel diff" },
461 { 0x4, "Frequency high or low" },
462 { 0x5, "df/dt high" },
463 { 0x6, "Reserved" },
464 { 0x7, "Digital" },
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" },
473 { 0, NULL }
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 /******************************************************************************
506 * functions
507 ******************************************************************************/
509 /* read in the size length for names found in config 3 frames
510 0 - no name
511 1-255 - length of name
513 static uint8_t get_name_length(tvbuff_t *tvb, int offset)
515 uint8_t name_length;
517 /* read the size of the name */
518 name_length = tvb_get_uint8(tvb, offset);
520 return name_length;
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)
528 uint16_t crc;
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)
535 return true;
537 return false;
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)
549 uint16_t num_pmu;
550 int offset;
551 config_frame *frame;
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
559 offset = 4;
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 */
567 offset += 3;
568 num_pmu = tvb_get_ntohs(tvb, offset);
570 // Start of repeating blocks
571 offset += 2;
573 while (num_pmu) {
574 uint16_t format_flags;
575 int num_ph,
576 num_an,
577 num_dg;
578 int i,
579 phunit,
580 anunit,
581 fnom;
582 config_block block;
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++) {
611 phasor_info pi;
612 uint32_t conv;
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;
621 pi.conv_cfg3 = 1;
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++) {
629 analog_info ai;
630 uint32_t conv;
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);
637 ai.conv = conv;
638 ai.conv_cfg3 = 1;
639 ai.offset_cfg3 = 0;
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 */
647 /* save FNOM */
648 block.fnom = tvb_get_ntohs(tvb, fnom) & 0x0001 ? 50 : 60;
649 offset = fnom + 2;
651 /* skip CFGCNT */
652 offset += 2;
654 wmem_array_append_one(frame->config_blocks, block);
655 num_pmu--;
658 return frame;
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)
670 uint16_t num_pmu;
671 int offset;
672 config_frame *frame;
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
683 offset = 4;
684 frame->id = tvb_get_ntohs(tvb, offset);
686 /* Skip to CONT_IDX -- Fragmented Frames not supported at this time */
687 offset += 10;
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 */
695 offset += 3;
696 num_pmu = tvb_get_ntohs(tvb, offset);
698 /* start of repeating blocks */
699 offset += 2;
700 while ((num_pmu) && (frame_not_fragmented)) {
701 uint16_t format_flags;
702 int num_ph,
703 num_an,
704 num_dg;
705 int i;
706 uint8_t name_length;
707 config_block block;
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);
716 offset += 1;
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);
726 offset += 2;
728 /* skip over Global PMU ID */
729 offset += G_PMU_ID_LEN;
731 format_flags = tvb_get_ntohs(tvb, offset);
732 offset += 2;
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);
740 offset += 2;
742 num_an = tvb_get_ntohs(tvb, offset);
743 offset += 2;
745 num_dg = tvb_get_ntohs(tvb, offset);
746 offset += 2;
747 block.num_dg = num_dg;
749 /* grab phasor names */
750 if (num_ph > 0)
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);
757 offset += 1;
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 */
767 if (num_an > 0)
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);
774 offset += 1;
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 */
784 if (num_dg > 0)
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 */
793 if (num_ph > 0)
795 for (i = 0; i != num_ph; i++) {
796 uint32_t phasor_unit;
798 /* get unit */
799 phasor_unit = tvb_get_ntohl(tvb, offset);
800 pi[i].unit = phasor_unit & 0x00000800 ? A : V;
801 pi[i].conv = 1;
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]);
807 offset += 12;
811 /* get analog conversion factors */
812 if (num_an > 0)
814 for (i = 0; i != num_an; i++) {
815 ai[i].conv = 1;
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]);
821 offset += 8;
825 /* skip digital masks */
826 if (num_dg > 0)
828 for (i = 0; i != num_dg; i++) {
829 offset += 4;
833 /* Skip to FNOM */
834 offset += 21;
836 /* save FNOM */
837 block.fnom = tvb_get_ntohs(tvb, offset) & 0x0001 ? 50 : 60;
838 offset += 2;
840 /* skip CFGCNT - offset ready for next PMU */
841 offset += 2;
843 wmem_array_append_one(frame->config_blocks, block);
844 num_pmu--;
847 return frame;
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;
859 config_frame *conf;
861 int offset = 0;
862 uint16_t framesize;
864 conf = (config_frame *)p_get_proto_data(wmem_file_scope(), pinfo, proto_synphasor, 0);
866 /* SYNC and flags */
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);
871 offset += 2;
873 /* FRAMESIZE */
874 proto_tree_add_item(tree, hf_frsize, tvb, offset, 2, ENC_BIG_ENDIAN);
875 framesize = tvb_get_ntohs(tvb, offset); offset += 2;
877 /* IDCODE */
878 proto_tree_add_item(tree, hf_idcode_stream_source, tvb, offset, 2, ENC_BIG_ENDIAN);
879 offset += 2;
881 /* SOC */
882 proto_tree_add_item(tree, hf_soc, tvb, offset, 4, ENC_TIME_SECS | ENC_BIG_ENDIAN);
883 offset += 4;
885 /* FRACSEC */
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);
892 offset += 1;
894 // Add RAW FRACSEC
895 proto_tree_add_item(tree, hf_fracsec_raw, tvb, offset, 3, ENC_BIG_ENDIAN);
897 // If exist configuration frame, add fracsec in milliseconds
898 if (conf){
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);
902 } else
905 /*offset += 3;*/
907 return framesize;
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) {
920 /* float, polar */
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);
927 else {
928 /* float, rect */
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);
936 else {
937 if (polar == block->phasor_notation) {
938 /* int, polar */
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;
959 else{
960 *mag = (*mag_real_unscaled * pi->conv) * 0.00001;
963 *real = (*mag) * cos(*phase);
964 *imag = (*mag) * sin(*phase);
966 else {
967 /* int, rect */
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;
990 else {
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;
1006 unsigned length;
1007 int j;
1008 int cnt = wmem_array_get_count(block->phasors); /* number of phasors to dissect */
1010 if (0 == cnt)
1011 return offset;
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;
1024 phasor_info *pi;
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,
1034 block,pi);
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);
1047 #undef SYNP_ANGLE
1049 return offset;
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);
1057 offset += 4;
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);
1063 offset += 4;
1065 else {
1066 int16_t tmp;
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));
1071 offset += 2;
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;
1076 return offset;
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;
1083 unsigned length;
1084 int j;
1085 int cnt = wmem_array_get_count(block->analogs); /* number of analog values to dissect */
1087 if (0 == cnt)
1088 return offset;
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) {
1105 float tmp;
1107 tmp = tvb_get_ntohieee_float(tvb, offset);
1108 offset += 4;
1110 proto_item_append_text(temp_item, ", %.3f", tmp);
1112 else {
1113 /* the "standard" doesn't say if this is signed or unsigned,
1114 * so I just use int16_t */
1115 int16_t tmp_i;
1116 float tmp_f;
1118 tmp_i = tvb_get_ntohs(tvb, offset);
1119 offset += 2;
1121 tmp_f = (tmp_i * ai->conv_cfg3) + ai->offset_cfg3;
1123 proto_item_append_text(temp_item, ", %.3f", tmp_f);
1126 else
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);
1132 else {
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)",
1138 tmp, ai->conv);
1142 return offset;
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)
1148 int j;
1149 int cnt = block->num_dg; /* number of digital status words to dissect */
1151 if (0 == cnt)
1152 return offset;
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);
1160 offset += 2;
1162 return offset;
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;
1169 int i;
1171 if (0 == cnt)
1172 return offset;
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",
1185 i + 1,
1186 tmp & 0x00FFFFFF,
1187 tmp & 0xFF000000 ? "Ampere" : "Volt");
1188 offset += 4;
1191 return offset;
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;
1198 int i;
1200 if (0 == cnt) {
1201 return offset;
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);
1237 offset += 2;
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);
1242 offset += 1;
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);
1250 offset += 1;
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);
1255 offset += 4;
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);
1260 offset += 4;
1263 return offset;
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;
1271 int i;
1273 if (0 == cnt)
1274 return offset;
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",
1287 i + 1,
1288 try_rval_to_str((tmp >> 24) & 0x000000FF, conf_anconvnames));
1290 tmp &= 0x00FFFFFF;
1291 if ( tmp & 0x00800000) /* sign bit set */
1292 tmp |= 0xFF000000;
1294 proto_item_append_text(temp_item, ", value: %" PRId32, tmp);
1296 offset += 4;
1299 return offset;
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;
1306 int i;
1308 if (0 == cnt) {
1309 return offset;
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);
1329 offset += 4;
1331 /* angle adjustment */
1332 proto_tree_add_item(single_analog_scalefactor_tree, hf_conf_analog_offset,
1333 tvb, offset, 4, ENC_BIG_ENDIAN);
1334 offset += 4;
1337 return offset;
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;
1344 int i;
1346 if (0 == cnt)
1347 return offset;
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
1354 * the status word
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;
1363 return offset;
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;
1370 int i;
1372 if (0 == cnt)
1373 return offset;
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++) {
1380 char *str;
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;
1387 return offset;
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;
1394 int i;
1395 uint8_t name_length;
1396 int temp_offset;
1397 int subsection_length = 0;
1399 if (0 == cnt) {
1400 return offset;
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++) {
1417 char *str;
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);
1425 offset += 1;
1427 proto_tree_add_string(chnam_tree, hf_conf_chnam, tvb, offset, 1, str);
1428 offset += name_length;
1431 return offset;
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;
1438 int offset = 0;
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;
1458 char *str;
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;
1469 /* IDCODE */
1470 proto_tree_add_item(station_tree, hf_idcode_data_source, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
1472 /* FORMAT */
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);
1479 offset += 2;
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);
1488 offset += 6;
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 */
1508 /* DATA_RATE */
1510 int16_t tmp = tvb_get_ntohis(tvb, offset);
1511 if (tmp > 0)
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);
1514 else
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);
1517 offset += 2;
1520 return offset;
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;
1527 int offset = 0;
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);
1533 /* CONT_IDX */
1534 proto_tree_add_item(config_tree, hf_cont_idx, tvb, offset, 2, ENC_BIG_ENDIAN);
1535 offset += 2;
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);
1541 offset += 3;
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);
1547 offset += 2;
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;
1555 int oldoffset;
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);
1575 offset += 1;
1577 /* Station Name */
1578 proto_tree_add_string(station_tree, hf_station_name, tvb, offset, 1, str);
1579 offset += name_length;
1581 /* IDCODE */
1582 proto_tree_add_item(station_tree, hf_idcode_data_source, tvb, offset, 2, ENC_BIG_ENDIAN);
1583 offset += 2;
1585 /* G_PMU_ID */
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;
1599 /* FORMAT */
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);
1606 offset += 2;
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);
1615 offset += 6;
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);
1638 /* PMU_LAT */
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);
1643 else {
1644 proto_tree_add_item(wgs84_tree, hf_conf_pmu_lat, tvb, offset, 4, ENC_BIG_ENDIAN);
1646 offset += 4;
1648 /* PMU_LON */
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);
1653 else {
1654 proto_tree_add_item(wgs84_tree, hf_conf_pmu_lon, tvb, offset, 4, ENC_BIG_ENDIAN);
1656 offset += 4;
1658 /* PMU_ELEV */
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);
1663 else {
1664 proto_tree_add_item(wgs84_tree, hf_conf_pmu_elev, tvb, offset, 4, ENC_BIG_ENDIAN);
1666 offset += 4;
1668 /* SVC_CLASS */
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");
1676 else {
1677 proto_tree_add_string(station_tree, hf_conf_svc_class, tvb, offset, 1, "Unknown");
1679 offset += 1;
1681 /* WINDOW */
1682 proto_tree_add_item(station_tree, hf_conf_window, tvb, offset, 4, ENC_BIG_ENDIAN);
1683 offset += 4;
1685 /*GRP_DLY */
1686 proto_tree_add_item(station_tree, hf_conf_grp_dly, tvb, offset, 4, ENC_BIG_ENDIAN);
1687 offset += 4;
1689 /* FNOM and CFGCNT */
1690 proto_tree_add_item(station_tree, hf_conf_fnom, tvb, offset, 2, ENC_BIG_ENDIAN);
1691 offset += 2;
1693 proto_tree_add_item(station_tree, hf_conf_cfgcnt, tvb, offset, 2, ENC_BIG_ENDIAN);
1694 offset += 2;
1696 /* set the correct length for the "Station :" item */
1697 proto_item_set_len(station_item, offset - oldoffset);
1698 } /* for() PMU blocks */
1700 /* DATA_RATE */
1702 int16_t tmp = tvb_get_ntohis(tvb, offset);
1703 if (tmp > 0) {
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);
1707 else {
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);
1711 offset += 2;
1714 return offset;
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;
1730 int offset = 0;
1731 unsigned i;
1732 config_frame *conf;
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);
1742 if (conf) {
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");
1761 return 0;
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);
1773 /* STAT */
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);
1793 offset += 2;
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);
1801 return 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,
1811 packet_info *pinfo)
1813 proto_tree *command_tree;
1814 unsigned tvbsize = tvb_reported_length(tvb);
1815 const char *s;
1817 proto_item_set_text(command_item, "Command data");
1818 command_tree = proto_item_add_subtree(command_item, ett_command);
1820 /* CMD */
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);
1827 if (tvbsize > 2) {
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);
1831 if (tvbsize % 2)
1832 expert_add_info(pinfo, ti, &ei_synphasor_extended_frame_data);
1834 else
1835 proto_tree_add_item(command_tree, hf_synphasor_unknown_data, tvb, 2, tvbsize - 2, ENC_NA);
1838 return tvbsize;
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_)
1846 uint8_t frame_type;
1847 uint16_t crc;
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 */
1854 return 0;
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);
1909 if (conversation) {
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;
1921 int offset;
1922 uint16_t framesize;
1923 tvbuff_t *sub_tvb;
1924 bool crc_good;
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);
1941 if (!crc_good) {
1942 proto_item_append_text(sub_item, ", not dissected because of wrong checksum");
1944 else {
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) {
1951 case DATA:
1952 dissect_data_frame(sub_tvb, sub_item, pinfo);
1953 break;
1954 case HEADER: /* no further dissection is done/needed */
1955 proto_item_append_text(sub_item, "Header Frame");
1956 break;
1957 case CFG1:
1958 case CFG2:
1959 dissect_config_frame(sub_tvb, sub_item);
1960 break;
1961 case CMD:
1962 dissect_command_frame(sub_tvb, sub_item, pinfo);
1963 break;
1964 case CFG3:
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)");
1971 else {
1972 dissect_config_3_frame(sub_tvb, sub_item);
1974 break;
1975 default:
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[] = {
2015 /* Sync word */
2016 { &hf_sync,
2017 { "Synchronization word", "synphasor.sync", FT_UINT16, BASE_HEX,
2018 NULL, 0x0, NULL, HFILL }},
2020 /* Flags in the Sync word */
2021 { &hf_sync_frtype,
2022 { "Frame Type", "synphasor.frtype", FT_UINT16, BASE_HEX,
2023 VALS(typenames), 0x0070, NULL, HFILL }},
2025 { &hf_sync_version,
2026 { "Version", "synphasor.version", FT_UINT16, BASE_DEC,
2027 VALS(versionnames), 0x000F, NULL, HFILL }},
2029 { &hf_frsize,
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 }},
2037 { &hf_station_name,
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 }},
2049 { &hf_g_pmu_id,
2050 { "Global PMU ID (raw hex bytes)", "synphasor.gpmuid", FT_BYTES, BASE_NONE,
2051 NULL, 0x0, NULL, HFILL }},
2053 { &hf_soc,
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 */
2075 { &hf_fracsec_raw,
2076 { "Fraction of second (raw)", "synphasor.fracsec_raw", FT_UINT24, BASE_DEC,
2077 NULL, 0x0, NULL, HFILL }},
2079 { &hf_fracsec_ms,
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 */
2084 { &hf_cont_idx,
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 }},
2092 { &hf_conf_numpmu,
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 }},
2117 { &hf_conf_chnam,
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 }},
2193 { &hf_conf_pmu_lat,
2194 { "PMU Latitude", "synphasor.conf.pmu_latitude", FT_FLOAT,
2195 BASE_NONE | BASE_UNIT_STRING, UNS(&units_degree_degrees), 0x0, NULL, HFILL }},
2197 { &hf_conf_pmu_lon,
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 }},
2221 { &hf_conf_window,
2222 { "PM window length", "synphasor.conf.window", FT_UINT32,
2223 BASE_DEC | BASE_UNIT_STRING, UNS(&units_microsecond_microseconds), 0x0, NULL, HFILL }},
2225 { &hf_conf_grp_dly,
2226 { "PM group delay", "synphasor.conf.grp_dly", FT_UINT32,
2227 BASE_DEC | BASE_UNIT_STRING, UNS(&units_microsecond_microseconds), 0x0, NULL, HFILL }},
2229 { &hf_conf_fnom,
2230 { "Nominal line frequency", "synphasor.conf.fnom", FT_BOOLEAN, 16,
2231 TFS(&conf_fnomnames), 0x0001, NULL, HFILL }},
2233 { &hf_conf_cfgcnt,
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 }},
2247 { &hf_data_statb13,
2248 { "Time synchronized", "synphasor.data.sync", FT_BOOLEAN, 16,
2249 TFS(&data_statb13names), 0x2000, NULL, HFILL }},
2251 { &hf_data_statb12,
2252 { "Data sorting", "synphasor.data.sorting", FT_BOOLEAN, 16,
2253 TFS(&data_statb12names), 0x1000, NULL, HFILL }},
2255 { &hf_data_statb11,
2256 { "Trigger detected", "synphasor.data.trigger", FT_BOOLEAN, 16,
2257 TFS(&data_statb11names), 0x0800, NULL, HFILL }},
2259 { &hf_data_statb10,
2260 { "Configuration changed", "synphasor.data.CFGchange", FT_BOOLEAN, 16,
2261 TFS(&data_statb10names), 0x0400, NULL, HFILL }},
2263 { &hf_data_statb09,
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 */
2280 { &hf_command,
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[] = {
2309 &ett_synphasor,
2310 &ett_frtype,
2311 &ett_timequal,
2312 &ett_conf,
2313 &ett_conf_station,
2314 &ett_conf_format,
2315 &ett_conf_phnam,
2316 &ett_conf_annam,
2317 &ett_conf_dgnam,
2318 &ett_conf_phconv,
2319 &ett_conf_phlist,
2320 &ett_conf_phflags,
2321 &ett_conf_phmod_flags,
2322 &ett_conf_ph_user_flags,
2323 &ett_conf_anconv,
2324 &ett_conf_anlist,
2325 &ett_conf_dgmask,
2326 &ett_conf_chnam,
2327 &ett_conf_wgs84,
2328 &ett_data,
2329 &ett_data_block,
2330 &ett_data_stat,
2331 &ett_data_phasors,
2332 &ett_data_analog,
2333 &ett_data_digital,
2334 &ett_command,
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
2372 * Local variables:
2373 * c-basic-offset: 8
2374 * tab-width: 8
2375 * indent-tabs-mode: t
2376 * End:
2378 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2379 * :indentSize=8:tabSize=8:noTabs=false: