diff --git a/examples/freq_float_PMU.py b/examples/freq_float_PMU.py new file mode 100644 index 0000000..af083d8 --- /dev/null +++ b/examples/freq_float_PMU.py @@ -0,0 +1,54 @@ +import random + +from synchrophasor.frame import ConfigFrame2 +from synchrophasor.pmu import Pmu + + +""" +randomPMU will listen on ip:port for incoming connections. +After request to start sending measurements - random +values for phasors will be sent. +""" + +#This example is used to test data sending from PMU to PDC when FREQ is random floating point. All changes from original project can be seen in commits. + + +if __name__ == "__main__": + + pmu = Pmu(ip="127.0.0.1", port=5080) + pmu.logger.setLevel("DEBUG") + + cfg = ConfigFrame2(5080, # PMU_ID + 1000000, # TIME_BASE + 1, # Number of PMUs included in data frame + "Random Station", # Station name + 5080, # Data-stream ID(s) + (True, True, True, True), # Data format - POLAR; PH - REAL; AN - REAL; FREQ - REAL; + 3, # Number of phasors + 1, # Number of analog values + 1, # Number of digital status words + ["VA", "VB", "VC", "ANALOG1", "BREAKER 1 STATUS", + "BREAKER 2 STATUS", "BREAKER 3 STATUS", "BREAKER 4 STATUS", "BREAKER 5 STATUS", + "BREAKER 6 STATUS", "BREAKER 7 STATUS", "BREAKER 8 STATUS", "BREAKER 9 STATUS", + "BREAKER A STATUS", "BREAKER B STATUS", "BREAKER C STATUS", "BREAKER D STATUS", + "BREAKER E STATUS", "BREAKER F STATUS", "BREAKER G STATUS"], # Channel Names + [(0, "v"), (0, "v"), + (0, "v")], # Conversion factor for phasor channels - (float representation, not important) + [(1, "pow")], # Conversion factor for analog channels + [(0x0000, 0xffff)], # Mask words for digital status words + 50, # Nominal frequency + 1, # Configuration change count + 30) # Rate of phasor data transmission + + pmu.set_configuration(cfg) + pmu.set_header("Hey! I'm randomPMU! Guess what? I'm sending random measurements values!") + + pmu.run() + + while True: + if pmu.clients: + pmu.send_data(phasors=[(random.uniform(215.0, 240.0), random.uniform(-0.1, 0.3)), + (random.uniform(215.0, 240.0), random.uniform(1.9, 2.2)), + (random.uniform(215.0, 240.0), random.uniform(3.0, 3.14))], + analog=[9.91], + digital=[0x0001], freq=random.uniform(49.49, 50.55)) diff --git a/examples/freq_integer_PMU.py b/examples/freq_integer_PMU.py new file mode 100644 index 0000000..1b2fef0 --- /dev/null +++ b/examples/freq_integer_PMU.py @@ -0,0 +1,56 @@ +import random + +from synchrophasor.frame import ConfigFrame2 +from synchrophasor.pmu import Pmu + + +""" +randomPMU will listen on ip:port for incoming connections. +After request to start sending measurements - random +values for phasors will be sent. +""" + +#This example is used to test data sending from PMU to PDC when FREQ is random short integer. All changes from original project can be seen in commits. + + +if __name__ == "__main__": + + pmu = Pmu(ip="127.0.0.1", port=5080) + pmu.logger.setLevel("DEBUG") + + cfg = ConfigFrame2(5080, # PMU_ID + 1000000, # TIME_BASE + 1, # Number of PMUs included in data frame + "Random Station", # Station name + 5080, # Data-stream ID(s) + (True, True, True, False), # Data format - POLAR; PH - REAL; AN - REAL; FREQ - REAL; + 3, # Number of phasors + 1, # Number of analog values + 1, # Number of digital status words + ["VA", "VB", "VC", "ANALOG1", "BREAKER 1 STATUS", + "BREAKER 2 STATUS", "BREAKER 3 STATUS", "BREAKER 4 STATUS", "BREAKER 5 STATUS", + "BREAKER 6 STATUS", "BREAKER 7 STATUS", "BREAKER 8 STATUS", "BREAKER 9 STATUS", + "BREAKER A STATUS", "BREAKER B STATUS", "BREAKER C STATUS", "BREAKER D STATUS", + "BREAKER E STATUS", "BREAKER F STATUS", "BREAKER G STATUS"], # Channel Names + [(0, "v"), (0, "v"), + (0, "v")], # Conversion factor for phasor channels - (float representation, not important) + [(1, "pow")], # Conversion factor for analog channels + [(0x0000, 0xffff)], # Mask words for digital status words + 50, # Nominal frequency + 1, # Configuration change count + 30) # Rate of phasor data transmission + + pmu.set_configuration(cfg) + pmu.set_header("Hey! I'm randomPMU! Guess what? I'm sending random measurements values!") + + pmu.run() + + while True: + if pmu.clients: + pmu.send_data(phasors=[(random.uniform(215.0, 240.0), random.uniform(-0.1, 0.3)), + (random.uniform(215.0, 240.0), random.uniform(1.9, 2.2)), + (random.uniform(215.0, 240.0), random.uniform(3.0, 3.14))], + analog=[9.91], + digital=[0x0001], freq=random.randint(-4949, 5055)) + + pmu.join() \ No newline at end of file diff --git a/synchrophasor/frame.py b/synchrophasor/frame.py index e92468e..3160893 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: @@ -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 @@ -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 @@ -2140,13 +2140,13 @@ def _freq2int(freq, data_format): if isinstance(data_format, int): data_format = DataFrame._int2format(data_format) - if data_format[3]: # FREQ/DFREQ floating point - if not -32.767 <= freq <= 32.767: - raise ValueError("FREQ must be in range -32.767 <= FREQ <= 32.767.") + if data_format[3]: # FREQ/DFREQ floating point, actual frequency value, input in Hz. + if not -32767 <= freq <= 32767: + raise ValueError("FREQ must be in range -32767 <= FREQ <= 32767.") freq = unpack("!I", pack("!f", float(freq)))[0] else: - if not -32767 <= freq <= 32767: + if not -32767 <= freq <= 32767: #FREQ 16-bit integer, should be frequency deviation from nominal(mHz). It is divided by 1000 and added to FNOM. raise ValueError("FREQ must be 16-bit signed integer. -32767 <= FREQ <= 32767.") freq = unpack("!H", pack("!h", freq))[0] @@ -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() })