From e0923d3829f186e17871ffa126fe6b84aa3b2009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Luki=C4=87?= Date: Thu, 28 Jul 2022 12:54:51 +0200 Subject: [PATCH 1/5] Received unknown message from PMU #35 First, in line 1920 in frame.py: stat = measurement_status << 2 This should be written this way: stat = measurement_status << 1 because, according to the standard, PMU sync takes only 1 bit, so if we shift this two times, we have one zero more than we actually need to have. Next, in line 1955 should be: measurement_status = DataFrame.MEASUREMENT_STATUS_WORDS[stat >> 14] If we shift this one 15 times, we will loose less significant bit in Data error bits. So, this whole part of code should be written like this: --- synchrophasor/frame.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/synchrophasor/frame.py b/synchrophasor/frame.py index e92468e..e5f0778 100644 --- a/synchrophasor/frame.py +++ b/synchrophasor/frame.py @@ -1917,7 +1917,7 @@ def _stat2int(measurement_status="ok", sync=True, sorting="timestamp", trigger=F if isinstance(trigger_reason, str): trigger_reason = DataFrame.TRIGGER_REASON[trigger_reason] - stat = measurement_status << 2 + stat = measurement_status << 1 if not sync: stat |= 1 @@ -1952,7 +1952,7 @@ def _stat2int(measurement_status="ok", sync=True, sorting="timestamp", trigger=F @staticmethod def _int2stat(stat): - measurement_status = DataFrame.MEASUREMENT_STATUS_WORDS[stat >> 15] + measurement_status = DataFrame.MEASUREMENT_STATUS_WORDS[stat >> 14] sync = bool(stat & 0x2000) if stat & 0x1000: @@ -1964,8 +1964,8 @@ def _int2stat(stat): cfg_change = bool(stat & 0x400) modified = bool(stat & 0x200) - time_quality = DataFrame.TIME_QUALITY_WORDS[stat & 0x1c0] - unlocked = DataFrame.UNLOCKED_TIME_WORDS[stat & 0x30] + time_quality = DataFrame.TIME_QUALITY_WORDS[(stat & 0x1c0)>>6] + unlocked = DataFrame.UNLOCKED_TIME_WORDS[(stat & 0x30)>>4] trigger_reason = DataFrame.TRIGGER_REASON_WORDS[stat & 0xf] return measurement_status, sync, sorting, trigger, cfg_change, modified, time_quality, unlocked, trigger_reason From 286a245a4a6210a0a5f03545214f481a6af9f05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Luki=C4=87?= Date: Thu, 28 Jul 2022 14:11:15 +0200 Subject: [PATCH 2/5] Timestamp issues #7 The thing is that if frasec: resolves as False if frasec is zero. We will fix this issue by checking if frasec is not None: (and if soc is not None:). Methods set_soc() and set_frasec() will validate values. --- synchrophasor/frame.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synchrophasor/frame.py b/synchrophasor/frame.py index e5f0778..d6e13ff 100644 --- a/synchrophasor/frame.py +++ b/synchrophasor/frame.py @@ -226,12 +226,12 @@ def set_time(self, soc=None, frasec=None): t = time() # Get current timestamp - if soc: + if soc is not None: self.set_soc(soc) else: self.set_soc(int(t)) # Get current timestamp - if frasec: + if frasec is not None: if isinstance(frasec, collections.Sequence): self.set_frasec(*frasec) else: From 133cc710d97b8cf908a4f6520da9c23abc07b353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Luki=C4=87?= Date: Thu, 28 Jul 2022 14:13:23 +0200 Subject: [PATCH 3/5] Timestamp issues the second #10 Should be (2^24)-1 because according to the standard actual frasec is used to be 24-bit integer. --- synchrophasor/frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synchrophasor/frame.py b/synchrophasor/frame.py index d6e13ff..e647bd4 100644 --- a/synchrophasor/frame.py +++ b/synchrophasor/frame.py @@ -392,7 +392,7 @@ def _int2frasec(frasec_int): leap_occ = bool(leap_occ) leap_pen = bool(leap_pen) - fr_seconds = frasec_int & (2**23-1) + fr_seconds = frasec_int & (2**24-1) return fr_seconds, leap_dir, leap_occ, leap_pen, time_quality From 81dcb6a98e2681166aea9e44e3f35cdaf610b368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Luki=C4=87?= Date: Thu, 28 Jul 2022 14:39:10 +0200 Subject: [PATCH 4/5] Frequency error #4 In C37.118.2-2011 - IEEE Standard for Synchrophasor Data Transfer for Power Systems says that frequency can be 32 bit floating point in witch case we would use real value of freq and 16 intiger in witch case we would represent frequency as real value of frequency + deviation frequency --- synchrophasor/frame.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synchrophasor/frame.py b/synchrophasor/frame.py index e647bd4..e151c8d 100644 --- a/synchrophasor/frame.py +++ b/synchrophasor/frame.py @@ -2357,7 +2357,7 @@ def get_measurements(self): "phasors": self.get_phasors()[i], "analog": self.get_analog()[i], "digital": self.get_digital()[i], - "frequency": self.cfg.get_fnom()[i] + self.get_freq()[i] / 1000, + "frequency":(self.get_freq()[i]) if (self.cfg.get_data_format()[i])[3] else self.cfg.get_fnom()[i] + self.get_freq()[i] / 1000, "rocof": self.get_dfreq()[i]} measurements.append(measurement) @@ -2368,7 +2368,7 @@ def get_measurements(self): "phasors": self.get_phasors(), "analog": self.get_analog(), "digital": self.get_digital(), - "frequency": self.cfg.get_fnom() + self.get_freq() / 1000, + "frequency": (self.get_freq()) if (self.cfg.get_data_format())[3] else self.cfg.get_fnom() + self.get_freq() / 1000, "rocof": self.get_dfreq() }) From 550c6e487edb70d126d718f29cc5c142054aa03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Luki=C4=87?= Date: Fri, 29 Jul 2022 13:10:06 +0200 Subject: [PATCH 5/5] Update frame.py --- synchrophasor/frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synchrophasor/frame.py b/synchrophasor/frame.py index e151c8d..b6aa851 100644 --- a/synchrophasor/frame.py +++ b/synchrophasor/frame.py @@ -226,7 +226,7 @@ def set_time(self, soc=None, frasec=None): t = time() # Get current timestamp - if soc is not None: + if soc is not None: self.set_soc(soc) else: self.set_soc(int(t)) # Get current timestamp