Phasor Measurement Unit Using Microcontroller

 

PMU Test Setup in the Lab

A PMU measures the instantaneous magnitudes and phases of voltages, currents, power and other parameters of an Electric Grid which effectively represents a snapshot of the health of the system, which is used by the operators in the control room to manage the Grid. The basic building blocks of a PMU are shown in the following diagram.

A three phase voltage sensor was designed with LEM LV-25P voltage sensors which outputs three unipolar sine waves, i.e. their voltage level lies between 0v to 3.3 volt. A three phase sine to square wave converter circuit is used to provide 3.3 v pulses for each half cycle of the AC supply. A GPS Disciplined Oscillator generates 3200 pulses per second synchronized to the PPS signal from a GPS module, which time the sampling of the signals by the ADC of the microcontroller. The Phasor micro-controller was connected to these signals, which after calculating the phasors and time stamping them reported to the local display, as well as to the computer. The computer runs a Graphical User Interface created with Python which displays the data from the PMU in real time

Block Diagram of the developed Prototype PMU

 

 

https://www.youtube.com/watch?v=cICzhhKIH4Q

https://www.youtube.com/watch?v=TGHwUf2Evdo

https://www.youtube.com/watch?v=HaE2b_z7Brc

Programmable Three Phase Test Signal Generator

A programmable signal generator which can be used to generate three signals, as desired by just altering the programming of the micro-controller, i.e it can be used to create three phase sine waves with user defined phase shifts or sag-swell.

The basic Building Blocks of the signal generator

The Microcontroller uses look-up tables to find the pre-calculated 8-bit integer values and writes them to the 8-bit output ports. The R-2R ladder DACs generate a voltage signal based on the digital values at the 8 nodes. This voltage signal is boosted, i.e. its current sourcing capability, which is limited by the microcontroller’s I/O ports is increased with the help of an OP-AMP based buffer circuit. The desired signals are then collected at the output of the buffer circuits.

 

Schematic of the Signal Generator

This particular board was used to generate three phase AC signals, with 120 Degree phase shifts to be fed to the PMU in order to further process it.

 

 

#include "JeeLib.h"
#include "avr/pgmspace.h"
#include "TimerOne.h"

//byte index;
int i = 0;

byte sineR256[] PROGMEM = {
  128, 131, 134, 137, 140, 143, 146, 149,
  152, 155, 158, 162, 165, 167, 170, 173,
  176, 179, 182, 185, 188, 190, 193, 196,
  198, 201, 203, 206, 208, 211, 213, 215,
  218, 220, 222, 224, 226, 228, 230, 232,
  234, 235, 237, 238, 240, 241, 243, 244,
  245, 246, 248, 249, 250, 250, 251, 252,
  253, 253, 254, 254, 254, 255, 255, 255,
  255, 255, 255, 255, 254, 254, 254, 253,
  253, 252, 251, 250, 250, 249, 248, 246,
  245, 244, 243, 241, 240, 238, 237, 235,
  234, 232, 230, 228, 226, 224, 222, 220,
  218, 215, 213, 211, 208, 206, 203, 201,
  198, 196, 193, 190, 188, 185, 182, 179,
  176, 173, 170, 167, 165, 162, 158, 155,
  152, 149, 146, 143, 140, 137, 134, 131,
  128, 124, 121, 118, 115, 112, 109, 106,
  103, 100, 97, 93, 90, 88, 85, 82,
  79, 76, 73, 70, 67, 65, 62, 59,
  57, 54, 52, 49, 47, 44, 42, 40,
  37, 35, 33, 31, 29, 27, 25, 23,
  21, 20, 18, 17, 15, 14, 12, 11,
  10, 9, 7, 6, 5, 5, 4, 3,
  2, 2, 1, 1, 1, 0, 0, 0,
  0, 0, 0, 0, 1, 1, 1, 2,
  2, 3, 4, 5, 5, 6, 7, 9,
  10, 11, 12, 14, 15, 17, 18, 20,
  21, 23, 25, 27, 29, 31, 33, 35,
  37, 40, 42, 44, 47, 49, 52, 54,
  57, 59, 62, 65, 67, 70, 73, 76,
  79, 82, 85, 88, 90, 93, 97, 100,
  103, 106, 109, 112, 115, 118, 121, 124
};

byte sineY256[] PROGMEM = {
  238, 237, 235,
  234, 232, 230, 228, 226, 224, 222, 220,
  218, 215, 213, 211, 208, 206, 203, 201,
  198, 196, 193, 190, 188, 185, 182, 179,
  176, 173, 170, 167, 165, 162, 158, 155,
  152, 149, 146, 143, 140, 137, 134, 131,
  128, 124, 121, 118, 115, 112, 109, 106,
  103, 100, 97, 93, 90, 88, 85, 82,
  79, 76, 73, 70, 67, 65, 62, 59,
  57, 54, 52, 49, 47, 44, 42, 40,
  37, 35, 33, 31, 29, 27, 25, 23,
  21, 20, 18, 17, 15, 14, 12, 11,
  10, 9, 7, 6, 5, 5, 4, 3,
  2, 2, 1, 1, 1, 0, 0, 0,
  0, 0, 0, 0, 1, 1, 1, 2,
  2, 3, 4, 5, 5, 6, 7, 9,
  10, 11, 12, 14, 15, 17, 18, 20,
  21, 23, 25, 27, 29, 31, 33, 35,
  37, 40, 42, 44, 47, 49, 52, 54,
  57, 59, 62, 65, 67, 70, 73, 76,
  79, 82, 85, 88, 90, 93, 97, 100,
  103, 106, 109, 112, 115, 118, 121, 124,
  128, 131, 134, 137, 140, 143, 146, 149,
  152, 155, 158, 162, 165, 167, 170, 173,
  176, 179, 182, 185, 188, 190, 193, 196,
  198, 201, 203, 206, 208, 211, 213, 215,
  218, 220, 222, 224, 226, 228, 230, 232,
  234, 235, 237, 238, 240, 241, 243, 244,
  245, 246, 248, 249, 250, 250, 251, 252,
  253, 253, 254, 254, 254, 255, 255, 255,
  255, 255, 255, 255, 254, 254, 254, 253,
  253, 252, 251, 250, 250, 249, 248, 246,
  245, 244, 243, 241, 240
};

byte sineB256[] PROGMEM = {
  18, 17, 15, 14, 12, 11,
  10, 9, 7, 6, 5, 5, 4, 3,
  2, 2, 1, 1, 1, 0, 0, 0,
  0, 0, 0, 0, 1, 1, 1, 2,
  2, 3, 4, 5, 5, 6, 7, 9,
  10, 11, 12, 14, 15, 17, 18, 20,
  21, 23, 25, 27, 29, 31, 33, 35,
  37, 40, 42, 44, 47, 49, 52, 54,
  57, 59, 62, 65, 67, 70, 73, 76,
  79, 82, 85, 88, 90, 93, 97, 100,
  103, 106, 109, 112, 115, 118, 121, 124,
  128, 131, 134, 137, 140, 143, 146, 149,
  152, 155, 158, 162, 165, 167, 170, 173,
  176, 179, 182, 185, 188, 190, 193, 196,
  198, 201, 203, 206, 208, 211, 213, 215,
  218, 220, 222, 224, 226, 228, 230, 232,
  234, 235, 237, 238, 240, 241, 243, 244,
  245, 246, 248, 249, 250, 250, 251, 252,
  253, 253, 254, 254, 254, 255, 255, 255,
  255, 255, 255, 255, 254, 254, 254, 253,
  253, 252, 251, 250, 250, 249, 248, 246,
  245, 244, 243, 241, 240, 238, 237, 235,
  234, 232, 230, 228, 226, 224, 222, 220,
  218, 215, 213, 211, 208, 206, 203, 201,
  198, 196, 193, 190, 188, 185, 182, 179,
  176, 173, 170, 167, 165, 162, 158, 155,
  152, 149, 146, 143, 140, 137, 134, 131,
  128, 124, 121, 118, 115, 112, 109, 106,
  103, 100, 97, 93, 90, 88, 85, 82,
  79, 76, 73, 70, 67, 65, 62, 59,
  57, 54, 52, 49, 47, 44, 42, 40,
  37, 35, 33, 31, 29, 27, 25, 23,
  21, 20
};

void setup () {
  for (int i = 22; i < 50; i++) //Define Port A,C and L as Output
  {
    pinMode(i, OUTPUT);
  }
  //Timer1.initialize(87);    //45 Hz
  //Timer1.initialize(86);  //45.6 Hz
  //Timer1.initialize(85);  //46.1 Hz
  //Timer1.initialize(84);  //46.6 Hz
  //Timer1.initialize(83);  //47.26 Hz
  //Timer1.initialize(82);  //47.8 Hz
  //Timer1.initialize(81);  //48.4 Hz
  //Timer1.initialize(80);  //49.02 Hz
  //Timer1.initialize(79);  //49.65 Hz
    Timer1.initialize(78);  //50.3 Hz
  //Timer1.initialize(77);  //51 Hz
  //Timer1.initialize(76);  //51.6 Hz
  //Timer1.initialize(75);  //52.3 Hz
  //Timer1.initialize(74);  //52.97 Hz
  //Timer1.initialize(73);  //53.7 Hz
  //Timer1.initialize(72);  //54.4 Hz
  //Timer1.initialize(71);//55.23hz

    Timer1.attachInterrupt(write_data);
}

void loop () {

}
void write_data() {
  PORTA = pgm_read_byte(sineR256 + i);//Red Phase
  PORTC = pgm_read_byte(sineY256 + i);//Blue Phase
  PORTL = pgm_read_byte(sineB256 + i);//Yellow Phase
  i++;
  if (i == 255)
  {
    i = 0;
  }
}

 

Three Phase Voltage Sensor Module

 

To calculate phasor, the voltage signal is acquired from a transmission line using current transformer (CT) and potential transformer (PT). The signal from CT and PT is provided to the microcontroller unit using Hall Effect voltage sensors (LEM-LV25P).

The schematic of using the voltage sensor for measuring the AC Supply

 

The output of the Hall Effect voltage sensor is bipolar in nature, i.e. it has both positive and negative voltage , which cannot be given to the ADC of the microcontroller, which only takes positive analog signals. Hence a level shifter is used to increase the signal level to all positive.

Schematic of Voltage Offset Adder circuit

 

 

 

Frequency Measurement


A sine to square wave converter circuit is used to convert the three phase sine waves into three square waves which are identical in phases and frequency as the sine waves. These square waves are given as interrupts to the microcontroller which calculates the frequencies.

Schematic of the sine to square wave converter

DC Power Supply Module

The Step Down transformers reduce the voltage level of the AC supply from 230 Volt to in around 17 volt AC. This AC is rectified using diodes or bridge rectifiers. This signal is smoothed out by some Bulk capacitors. Afterwards the voltage regulators generate the regulated DC voltages suitable for the electronics and microcontrollers.

Schematic of Positive DC Power Supply

Schematic of Dual Power Supply

 


 

Arduino code for Temperature Controlled Fan to be attached with the Power Supply Unit

#include "OneWire.h"
#include "LiquidCrystal.h"
#include "FreqMeasure.h"

OneWire  ds(3);  // on pin 10 (a 4.7K resistor is necessary)
LiquidCrystal lcd(A5, A4, A3, A2, A1, A0);
int pwm_fan = 6;
void setup(void) {
  lcd.begin(16, 2);
  Serial.begin(9600);
  FreqMeasure.begin();
  pinMode(pwm_fan, OUTPUT);
}

double sum = 0;
int count = 0;
int rpm = 0;
void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  Serial.print("ROM =");
  for ( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return;
  }
  Serial.println();

  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end

  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.

  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");

  lcd.setCursor(0, 0);
  lcd.print("Temp: ");
  lcd.print(celsius);
  lcd.print(" *C");

  if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
    if (count > 15) {
      float frequency = FreqMeasure.countToFrequency(sum / count);
      rpm = frequency * (60 / 2);
      sum = 0;
      count = 0;
      Serial.print("Fan Speed");
      Serial.print(rpm);
      Serial.println("RPM");
      //rpm = 0;
    }
  }

  int fan_speed = map(celsius, 20, 45, 0, 255);
  if (fan_speed < 0)
  {
    fan_speed = 0;
  }
  else if (fan_speed > 255)
  {
    fan_speed = 255;
  }
  else
  {}
  analogWrite(pwm_fan, fan_speed);

  int desired_rpm = map(fan_speed, 0, 255, 800, 2200);
  lcd.setCursor(0, 1);
  lcd.print("Speed: ");
  lcd.print(rpm);
  lcd.print(" RPM");

}

 

This program is used to generate 3200 pulses per second from the 1 PPS signal from the GPS Module after a successful satellite Fix

 

#include "TimerOne.h"
// use this library to handle the Timer functionality
const int sampling_clock_out_pin = 9;
// the sampling pulses will be generated at this pin
void setup()
{
  pinMode(2, INPUT);
  // This is the pin where the 1 PPS pulse from GPS module is connected
  attachInterrupt(0, pulsePPS, RISING);//watch out for interrupt (1PPS) on pin 2
  //Timer1.initialize(400);//for 2500 pulses per sec
  Timer1.initialize(312.5);//for 3200 pulses per sec
  Timer1.pwm(sampling_clock_out_pin, 100);//duty cycle of the pulse, i.e. about 100uS
}
void loop()
{ /* Since the microcontrollers timer operate independently without invoking the CPU, and the Interrupt handlers
  takes care of the ISR, there is nothing to do in the loop*/
}
void pulsePPS() //interrupt routine upon receiving PPS
{
  Timer1.restart();
  /*Just restart the timer, to keep it in sync with the GPS module's PPS pulses*/
}

 

 

The following code is used by the Phasor Processor (Arduino Due) to calculate the phasors and transmit them to the Communication Module

 

#include "Time.h"         // Time Library
#include "TinyGPS++.h"    // GPS Library
#include "math.h"         // Math functions library
static const uint32_t GPSBaud = 38400;
boolean Calculate_A_Phasor = false;
boolean get_time_on_pps = false;
// The TinyGPS++ object
TinyGPSPlus gps;

// Serial connection to the GPS device
#define Serial_GPS Serial3
#define SerialTx Serial2
//#define SerialTx Serial

time_t prevDisplay = 0; // Count for when time last displayed
int Year;
byte Month;
byte Day;
byte Hour;
byte Minute;
byte Second;

//Phasor Estimation Variable Declaration
#define WindowSize 64 //i.e. 64 samples per second
int N = WindowSize;   //Sampling frequency 3200 Hz
long double pi = 3.143;

long double adc_out_1[WindowSize], values_1[WindowSize];
long double adc_out_2[WindowSize], values_2[WindowSize];
long double adc_out_3[WindowSize], values_3[WindowSize];

long double Xi_1, Xr_1, Phasor_Magnitude_1, Phasor_Angle_1, Phasor_Angle_Degree_1;
long double Xi_2, Xr_2, Phasor_Magnitude_2, Phasor_Angle_2, Phasor_Angle_Degree_2;
long double Xi_3, Xr_3, Phasor_Magnitude_3, Phasor_Angle_3, Phasor_Angle_Degree_3;

unsigned long int calculation_start_millis;
unsigned long int calculation_finish_millis;

unsigned long int pps_time_millis;
unsigned long int phasor_stamp_millis;

//variables for frequency calculation
volatile long double P1_start_micros = 0, last_P1_start_micros = 0, P1_period = 0;
volatile long double P2_start_micros = 0, last_P2_start_micros = 0, P2_period = 0;
volatile long double P3_start_micros = 0, last_P3_start_micros = 0, P3_period = 0;

int P1_freq, P2_freq, P3_freq;
long double P1_freqf, P2_freqf, P3_freqf;
int P1_lf, P2_lf, P3_lf;// last frequencies
int P1_rocof, P2_rocof, P3_rocof;// rate of change of frequency df/dt
long double P1_rocoff, P2_rocoff, P3_rocoff;

void setup()
{ adc_setup();
  SerialTx.begin(921600);//for transmitting Phasors
  Serial_GPS.begin(GPSBaud); // Start GPS Serial Connection
  smartDelay(1000);
  delay(2000);
  analogReadResolution(12);
  attachInterrupt(22, aquire, RISING);// aquire voltage samples
  attachInterrupt(23, attach_pps_time, RISING);// get pulse per second time
  attachInterrupt(31, capture_P1_start, RISING);// get starting time of P1 waveform
  attachInterrupt(33, capture_P2_start, RISING);// """""""""""""""""""" P2 waveform
  attachInterrupt(29, capture_P3_start, RISING);// """""""""""""""""""" P3 waveform
}
// Get start Time of waves for calculation of frequency
void capture_P1_start() {
  P1_start_micros = micros();
  P1_period = P1_start_micros - last_P1_start_micros;
  last_P1_start_micros = P1_start_micros;
}
void capture_P2_start() {
  P2_start_micros = micros();
  P2_period = P2_start_micros - last_P2_start_micros;
  last_P2_start_micros = P2_start_micros;
}
void capture_P3_start() {
  P3_start_micros = micros();
  P3_period = P3_start_micros - last_P3_start_micros;
  last_P3_start_micros = P3_start_micros;
}
// Circular buffer, power of two.
#define BUFSIZE 0x40 //64 samples buffer
#define BUFMASK 0x3F
volatile int R [BUFSIZE] ;
volatile int Y [BUFSIZE] ;
volatile int B [BUFSIZE] ;
volatile int sptr = 0 ;
volatile int isr_count = 0 ;

void aquire() {
  ADC->ADC_CR |= 0b10; //start conversion
  while (!(ADC->ADC_ISR & 0b11100000)); //wait for conversion to end
  int Rval = ADC->ADC_CDR[7];
  int Yval = ADC->ADC_CDR[6];
  int Bval = ADC->ADC_CDR[5];

  R[sptr] = Rval;
  Y[sptr] = Yval;
  B[sptr] = Bval;
  sptr = (sptr + 1) & BUFMASK;
  isr_count ++ ;
}

void adc_setup()
{
  //ADC setup
  ADC->ADC_WPMR &= 0xFFFE; //disable write protect
  ADC->ADC_CHER = 0b11100000; //Enable AD7,AD6,AD5 or CH7,Ch6,Ch5 or PA16,PA24,PA23 or A0,A1 and A2     |
  ADC->ADC_MR &= 0b11111111000000000000011100000000;//Fast i.e. about 4mS for 2500 Conversions on three channels
  ADC->ADC_MR |= 0b00000000000100100000000000000000; //software trigger, hi res, no sleep, not free running
  ADC->ADC_IER = 0b11100000; //enable interrupt
  ADC->ADC_IMR = 0b11100000; //enable interrupt in mask
  ADC->ADC_CR |= 0b10; //start first conversion
}

void loop()
{
  if (get_time_on_pps == true)
  {
    pps_time_millis = millis();
    GPS_Timezone_Adjust();  // Call Time Adjust Function
    get_time_on_pps = false;
  }
  if (isr_count == 64)
  {
    Calculate_A_Phasor = true;
    isr_count = 0;
  }
  if (Calculate_A_Phasor == true)
  {
    calc_phasor();
    transmit_phasors_on_SerialTx();
    Calculate_A_Phasor = false;
  }
  smartDelay(0);
}

void attach_pps_time()
{
  get_time_on_pps = true;
}

void GPS_Timezone_Adjust() {

  Year = gps.date.year();
  Month = gps.date.month();
  Day = gps.date.day();
  Hour = gps.time.hour();
  Minute = gps.time.minute();
  Second = gps.time.second();

  // Set Time from GPS data string
  setTime(Hour, Minute, Second, Day, Month, Year);
  // Calc current Time Zone time by offset value

  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) {
      prevDisplay = now();
    }
  }
  smartDelay(0);
}

static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do
  {
    while (Serial_GPS.available())
      gps.encode(Serial_GPS.read());
  } while (millis() - start < ms);
}

//Phasor calculation function
void calc_phasor() {
  //copy buffer to SampleWindow for calculation
  for (int i = 0; i < 64; i++)
  {
    adc_out_1[i] = R[i];
    adc_out_2[i] = Y[i];
    adc_out_3[i] = B[i];
  }
  calculation_start_millis = millis();
  for (int i = 0; i < N; i++)
  {
    values_1[i] = map_double(adc_out_1[i], 1433, 2812, -347.25, 347.25);// Phase A
    values_2[i] = map_double(adc_out_2[i], 1498, 2859, -336.78, 336.78);// Phase B
    values_3[i] = map_double(adc_out_3[i], 1408, 2851, -344.70, 344.70);// Phase C
    smartDelay(0);
  }

  //Calculate 64-Point DFT
  Xr_1 = 0;    Xr_2 = 0;    Xr_3 = 0;
  Xi_1 = 0;    Xi_2 = 0;    Xi_3 = 0;
  Phasor_Magnitude_1 = 0;    Phasor_Magnitude_2 = 0;    Phasor_Magnitude_3 = 0;
  Phasor_Angle_1 = 0;    Phasor_Angle_2 = 0;    Phasor_Angle_3 = 0;
  for (int n = 0; n < N; n++)
  {
    Xr_1 = Xr_1 + values_1[n] * cos((2 * pi * n) / N);
    Xi_1 = Xi_1 + values_1[n] * sin((2 * pi * n) / N);

    Xr_2 = Xr_2 + values_2[n] * cos((2 * pi * n) / N);
    Xi_2 = Xi_2 + values_2[n] * sin((2 * pi * n) / N);

    Xr_3 = Xr_3 + values_3[n] * cos((2 * pi * n) / N);
    Xi_3 = Xi_3 + values_3[n] * sin((2 * pi * n) / N);
    smartDelay(0);
  }

  Xr_1 = (sqrt(2) / N) * Xr_1;
  Xr_2 = (sqrt(2) / N) * Xr_2;
  Xr_3 = (sqrt(2) / N) * Xr_3;

  Xi_1 = -(sqrt(2) / N) * Xi_1;
  Xi_2 = -(sqrt(2) / N) * Xi_2;
  Xi_3 = -(sqrt(2) / N) * Xi_3;

  Phasor_Magnitude_1 = sqrt(Xr_1 * Xr_1 + Xi_1 * Xi_1);
  Phasor_Magnitude_2 = sqrt(Xr_2 * Xr_2 + Xi_2 * Xi_2);
  Phasor_Magnitude_3 = sqrt(Xr_3 * Xr_3 + Xi_3 * Xi_3);

  Phasor_Angle_1 = atan2(Xi_1, Xr_1); //double atan2(double y, double x)
  Phasor_Angle_2 = atan2(Xi_2, Xr_2); //The atan2() function returns the arc tangent of y/x, in the range [-pi, +pi] radians.
  Phasor_Angle_3 = atan2(Xi_3, Xr_3);

  //Calculate Phasor Angle in Degree
  Phasor_Angle_Degree_1 = (Phasor_Angle_1 * 4068) / 71;
  Phasor_Angle_Degree_2 = (Phasor_Angle_2 * 4068) / 71;
  Phasor_Angle_Degree_3 = (Phasor_Angle_3 * 4068) / 71;
  //
  calculation_finish_millis = millis();
  phasor_stamp_millis = calculation_start_millis - pps_time_millis;

  // Calculate frequency
  //long int P1_period = P1_end_micros - P1_start_micros;
  P1_freqf = 1000000 / P1_period;
  P2_freqf = 1000000 / P2_period;
  P3_freqf = 1000000 / P3_period;

  P1_rocoff = sqrt((P1_freqf - 50.00) * (P1_freqf - 50.00)) * 50;
  P2_rocoff = sqrt((P2_freqf - 50.00) * (P2_freqf - 50.00)) * 50;
  P3_rocoff = sqrt((P3_freqf - 50.00) * (P3_freqf - 50.00)) * 50;

  smartDelay(0);
}

float map_double(double x, double in_min, double in_max, double out_min, double out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
  smartDelay(0);
}
void transmit_phasors_on_SerialTx() {
  SerialTx.write('!');
  SerialTx.print(int(Phasor_Magnitude_1 * 100));
  SerialTx.write('"');
  SerialTx.print(int(Phasor_Magnitude_2 * 100));
  SerialTx.write('#');
  SerialTx.print(int(Phasor_Magnitude_3 * 100));
  SerialTx.write('$');

  //Calculate angle i.e. 2pi's complement to be sent
  if (Phasor_Angle_1 < 0)
    Phasor_Angle_1 = Phasor_Angle_1 + 6.286;
  if (Phasor_Angle_2 < 0)
    Phasor_Angle_2 = Phasor_Angle_2 + 6.286;
  if (Phasor_Angle_3 < 0)
    Phasor_Angle_3 = Phasor_Angle_3 + 6.286;

  SerialTx.print(int(Phasor_Angle_1 * 1000));
  SerialTx.write('%');
  SerialTx.print(int(Phasor_Angle_2 * 1000));
  SerialTx.write('&');
  SerialTx.print(int(Phasor_Angle_3 * 1000));
  SerialTx.write('(');

  SerialTx.print(day());
  SerialTx.write(')');
  SerialTx.print(month());
  SerialTx.write('*');
  SerialTx.print(year());

  SerialTx.write('+');
  SerialTx.print(hour());
  SerialTx.write(',');
  SerialTx.print(minute());
  SerialTx.write('-');
  SerialTx.print(second());
  SerialTx.write('.');
  SerialTx.print(phasor_stamp_millis);
  SerialTx.write('/');

  float Latitude = (gps.location.lat());
  float Longitude = (gps.location.lng());
  SerialTx.print(int(Latitude * 1000));
  SerialTx.write(':');
  SerialTx.print(int(Longitude * 1000));
  SerialTx.write(';');

  //Transmit Frequencies
  SerialTx.print(int(P1_freqf * 100));
  SerialTx.write('@');
  SerialTx.print(int(P2_freqf * 100));
  SerialTx.write('^');
  SerialTx.print(int(P3_freqf * 100));
  SerialTx.write('?');

  //Transmit ROCOF
  SerialTx.print(int(P1_rocoff * 100));
  SerialTx.write('[');
  SerialTx.print(int(P2_rocoff * 100));
  SerialTx.write(']');
  SerialTx.print(int(P3_rocoff * 100));
  SerialTx.write('|');

}

 

The Communication Module with Arduino Due which Transmits the Data received from the Phasor Processor, to the Display Module (LCD) and to the Computer(Which has the Real-time GUI Running)

 

#define IDLE  0
#define RECEIVING1 1
#define RECEIVING2 2
#define RECEIVING3 3
#define RECEIVING4 4
#define RECEIVING5 5
#define RECEIVING6 6
#define RECEIVING7 7
#define RECEIVING8 8
#define RECEIVING9 9
#define RECEIVING10 10
#define RECEIVING11 11
#define RECEIVING12 12
#define RECEIVING13 13
#define RECEIVING14 14
#define RECEIVING15 15
#define RECEIVING16 16
#define RECEIVING17 17
#define RECEIVING18 18
#define RECEIVING19 19
#define RECEIVING20 20
#define RECEIVING21 21

int Year, Month, Day;
int Hour, Minute, Second, MilliSecond;

float Phasor_Magnitude_1, Phasor_Magnitude_2, Phasor_Magnitude_3;

float Phasor_Angle_1, Phasor_Angle_Degree_1;
float Phasor_Angle_2, Phasor_Angle_Degree_2;
float Phasor_Angle_3, Phasor_Angle_Degree_3;

float Latitude;
float Longitude;

float P1_freq, P2_freq, P3_freq;
float P1_rocof, P2_rocof, P3_rocof;

byte status = IDLE;
#define SerialRx Serial2
#define SerialLCD Serial3
void setup() {
  SerialLCD.begin(921600);
  SerialRx.begin(921600);
  Serial.begin(460800);
  //Serial.println("Ready");
}
int count = 0;
void loop() {

  if (SerialRx.available()) {
    int c = SerialRx.read();
    if (status == RECEIVING1 && c >= '0' && c <= '9') {
      Phasor_Magnitude_1 = Phasor_Magnitude_1 * 10 + (c - '0');
    } else if (status == RECEIVING2 && c >= '0' && c <= '9') {
      Phasor_Magnitude_2 = Phasor_Magnitude_2 * 10 + (c - '0');
    } else if (status == RECEIVING3 && c >= '0' && c <= '9') {
      Phasor_Magnitude_3 = Phasor_Magnitude_3 * 10 + (c - '0');
    } else if (status == RECEIVING4 && c >= '0' && c <= '9') {
      Phasor_Angle_1 = Phasor_Angle_1 * 10 + (c - '0');
    } else if (status == RECEIVING5 && c >= '0' && c <= '9') {
      Phasor_Angle_2 = Phasor_Angle_2 * 10 + (c - '0');
    } else if (status == RECEIVING6 && c >= '0' && c <= '9') {
      Phasor_Angle_3 = Phasor_Angle_3 * 10 + (c - '0');
    } else if (status == RECEIVING7 && c >= '0' && c <= '9') {
      Day = Day * 10 + (c - '0');
    } else if (status == RECEIVING8 && c >= '0' && c <= '9') {
      Month = Month * 10 + (c - '0');
    } else if (status == RECEIVING9 && c >= '0' && c <= '9') {
      Year = Year * 10 + (c - '0');
    } else if (status == RECEIVING10 && c >= '0' && c <= '9') {
      Hour = Hour * 10 + (c - '0');
    } else if (status == RECEIVING11 && c >= '0' && c <= '9') {
      Minute = Minute  * 10 + (c - '0');
    } else if (status == RECEIVING12 && c >= '0' && c <= '9') {
      Second = Second * 10 + (c - '0');
    }  else if (status == RECEIVING13 && c >= '0' && c <= '9') {
      MilliSecond = MilliSecond * 10 + (c - '0');
    }  else if (status == RECEIVING14 && c >= '0' && c <= '9') {
      Latitude = Latitude * 10 + (c - '0');
    }  else if (status == RECEIVING15 && c >= '0' && c <= '9') {
      Longitude = Longitude * 10 + (c - '0');
    }  else if (status == RECEIVING16 && c >= '0' && c <= '9') {
      P1_freq = P1_freq * 10 + (c - '0');
    }  else if (status == RECEIVING17 && c >= '0' && c <= '9') {
      P2_freq = P2_freq * 10 + (c - '0');
    }  else if (status == RECEIVING18 && c >= '0' && c <= '9') {
      P3_freq = P3_freq * 10 + (c - '0');
    } else if (status == RECEIVING19 && c >= '0' && c <= '9') {
      P1_rocof = P1_rocof * 10 + (c - '0');
    } else if (status == RECEIVING20 && c >= '0' && c <= '9') {
      P2_rocof = P2_rocof * 10 + (c - '0');
    } else if (status == RECEIVING21 && c >= '0' && c <= '9') {
      P3_rocof = P3_rocof * 10 + (c - '0');
    }

    else if (status == RECEIVING1 && c == '"') {
      status = RECEIVING2;
    } else if (status == RECEIVING2 && c == '#') {
      status = RECEIVING3;
    } else if (status == RECEIVING3 && c == '$') {
      status = RECEIVING4;
    } else if (status == RECEIVING4 && c == '%') {
      status = RECEIVING5;
    } else if (status == RECEIVING5 && c == '&') {
      status = RECEIVING6;
    } else if (status == RECEIVING6 && c == '(') {
      status = RECEIVING7;
    } else if (status == RECEIVING7 && c == ')') {
      status = RECEIVING8;
    } else if (status == RECEIVING8 && c == '*') {
      status = RECEIVING9;
    } else if (status == RECEIVING9 && c == '+') {
      status = RECEIVING10;
    } else if (status == RECEIVING10 && c == ',') {
      status = RECEIVING11;
    } else if (status == RECEIVING11 && c == '-') {
      status = RECEIVING12;
    } else if (status == RECEIVING12 && c == '.') {
      status = RECEIVING13;
    } else if (status == RECEIVING13 && c == '/') {
      status = RECEIVING14;
    } else if (status == RECEIVING14 && c == ':') {
      status = RECEIVING15;
    } else if (status == RECEIVING15 && c == ';') {
      status = RECEIVING16;
    } else if (status == RECEIVING16 && c == '@') {
      status = RECEIVING17;
    } else if (status == RECEIVING17 && c == '^') {
      status = RECEIVING18;
    } else if (status == RECEIVING18 && c == '?') {
      status = RECEIVING19;
    } else if (status == RECEIVING19 && c == '[') {
      status = RECEIVING20;
    } else if (status == RECEIVING20 && c == ']') {
      status = RECEIVING21;
    } else if (c == '|') {
      status = IDLE;

      //Remote value Received completely, Now display it

      //Calculate Phasor Angles into Float
      Phasor_Angle_1 = Phasor_Angle_1 / 1000;
      Phasor_Angle_2 = Phasor_Angle_2 / 1000;
      Phasor_Angle_3 = Phasor_Angle_3 / 1000;

      //Calculate Phasor Angle using reverse 2pi's complement
      if (Phasor_Angle_1 > 3.143)
        Phasor_Angle_1 = Phasor_Angle_1 - 6.286;
      if (Phasor_Angle_2 > 3.143)
        Phasor_Angle_2 = Phasor_Angle_2 - 6.286;
      if (Phasor_Angle_3 > 3.143)
        Phasor_Angle_3 = Phasor_Angle_3 - 6.286;

      //Calculate Angles in Degrees
      Phasor_Angle_Degree_1 = (Phasor_Angle_1 * 4068) / 71;
      Phasor_Angle_Degree_2 = (Phasor_Angle_2 * 4068) / 71;
      Phasor_Angle_Degree_3 = (Phasor_Angle_3 * 4068) / 71;

      Display_Phasors_on_Serial_Terminal();
      transmit_phasors_LCD();

    } else if (c == '!') {
      status = RECEIVING1;
      //Reset the variables to Zero
      Year = 0;
      Month = 0;
      Day = 0;
      Hour = 0;
      Minute = 0;
      Second = 0;
      MilliSecond = 0;
      Phasor_Angle_1 = 0;
      Phasor_Angle_2 = 0;
      Phasor_Angle_3 = 0;
      Phasor_Magnitude_1 = 0;
      Phasor_Magnitude_2 = 0;
      Phasor_Magnitude_3 = 0;
      Phasor_Angle_Degree_1 = 0;
      Phasor_Angle_Degree_2 = 0;
      Phasor_Angle_Degree_3 = 0;
      Latitude = 0;
      Longitude = 0;
      P1_freq = 0;
      P2_freq = 0;
      P3_freq = 0;
      P1_rocof = 0;
      P2_rocof = 0;
      P3_rocof = 0;
    }
  }
}
// Transmit the phasors to local PDC, where it can be plotted in real-time
void Display_Phasors_on_Serial_Terminal() {
  //Serial.print(millis());
  Serial.print(Day);
  Serial.print(" ");
  Serial.print(Month);
  Serial.print(" ");
  Serial.print(Year);
  Serial.print(" ");
  Serial.print(Hour);
  Serial.print(" ");
  Serial.print(Minute);
  Serial.print(" ");
  Serial.print(Second);
  Serial.print(" ");
  Serial.print(MilliSecond);
  Serial.print(" ");
  Serial.print(float(Phasor_Magnitude_1 / 100));
  Serial.print(" ");
  Serial.print(Phasor_Angle_Degree_1);
  Serial.print(" ");
  Serial.print(float(Phasor_Magnitude_2 / 100));
  Serial.print(" ");
  Serial.print(Phasor_Angle_Degree_2);
  Serial.print(" ");
  Serial.print(float(Phasor_Magnitude_3 / 100));
  Serial.print(" ");
  Serial.print(Phasor_Angle_Degree_3);
  Serial.print(" ");
  Serial.print(P1_freq / 100);
  Serial.print(" ");
  Serial.print(P2_freq / 100);
  Serial.print(" ");
  Serial.print(P3_freq / 100);
  Serial.print(" ");
  Serial.print(P1_rocof / 100);
  Serial.print(" ");
  Serial.print(P2_rocof / 100);
  Serial.print(" ");
  Serial.print(P3_rocof / 100);
  Serial.print("\n");
}

// Transmit the Phasor parameters to LCD Module
void transmit_phasors_LCD() {
  SerialLCD.write('!');
  SerialLCD.print(int(Phasor_Magnitude_1));
  SerialLCD.write('"');
  SerialLCD.print(int(Phasor_Magnitude_2));
  SerialLCD.write('#');
  SerialLCD.print(int(Phasor_Magnitude_3));
  SerialLCD.write('$');

  //Calculate angle i.e. 2pi's complement to be sent
  if (Phasor_Angle_1 < 0)
    Phasor_Angle_1 = Phasor_Angle_1 + 6.286;
  if (Phasor_Angle_2 < 0)
    Phasor_Angle_2 = Phasor_Angle_2 + 6.286;
  if (Phasor_Angle_3 < 0)
    Phasor_Angle_3 = Phasor_Angle_3 + 6.286;
  SerialLCD.print(int(Phasor_Angle_1 * 100));
  SerialLCD.write('%');
  SerialLCD.print(int(Phasor_Angle_2 * 100));
  SerialLCD.write('&');
  SerialLCD.print(int(Phasor_Angle_3 * 100));
  SerialLCD.write('(');

  SerialLCD.print(Day);
  SerialLCD.write(')');
  SerialLCD.print(Month);
  SerialLCD.write('*');
  SerialLCD.print(Year);

  SerialLCD.write('+');
  SerialLCD.print(Hour);
  SerialLCD.write(',');
  SerialLCD.print(Minute);
  SerialLCD.write('-');
  SerialLCD.print(Second);
  SerialLCD.write('.');
  SerialLCD.print(MilliSecond);
  SerialLCD.write('/');

  SerialLCD.print(int(Latitude));
  SerialLCD.write(':');
  SerialLCD.print(int(Longitude));
  SerialLCD.write(';');

  //Transmit Frequencies
  SerialLCD.print(P1_freq);
  SerialLCD.write('@');
  SerialLCD.print(P2_freq);
  SerialLCD.write('^');
  SerialLCD.print(P3_freq);
  SerialLCD.write('?');

  //Transmit ROCOF
  SerialLCD.print(P1_rocof);
  SerialLCD.write('[');
  SerialLCD.print(P2_rocof);
  SerialLCD.write(']');
  SerialLCD.print(P3_rocof);
  SerialLCD.write('|');
}

 

The LCD Displays the phasor Information locally. An Arduino Due with a 7 inch UTFT LCD Module is used as the Display Unit.

 

#include "UTFT.h"
extern uint8_t Grotesk32x64[];// Declare which fonts we will be using
extern uint8_t Ubuntubold[];
extern uint8_t Ubuntu[];
extern uint8_t franklingothic_normal[];
extern uint8_t Inconsola[];
extern uint8_t BigFont[];// Declare which fonts we will be using

UTFT PMU_LCD(CTE70, 25, 26, 27, 28);
bool display_now_on_lcd = false;

#define IDLE  0
#define RECEIVING1 1
#define RECEIVING2 2
#define RECEIVING3 3
#define RECEIVING4 4
#define RECEIVING5 5
#define RECEIVING6 6
#define RECEIVING7 7
#define RECEIVING8 8
#define RECEIVING9 9
#define RECEIVING10 10
#define RECEIVING11 11
#define RECEIVING12 12
#define RECEIVING13 13
#define RECEIVING14 14
#define RECEIVING15 15
#define RECEIVING16 16
#define RECEIVING17 17
#define RECEIVING18 18
#define RECEIVING19 19
#define RECEIVING20 20
#define RECEIVING21 21

int Year, Month, Day;
int Hour, Minute, Second, MilliSecond;

float Phasor_Magnitude_1;
float Phasor_Magnitude_2;
float Phasor_Magnitude_3;

float Phasor_Angle_1, Phasor_Angle_Degree_1;
float Phasor_Angle_2, Phasor_Angle_Degree_2;
float Phasor_Angle_3, Phasor_Angle_Degree_3;

float Latitude;
float Longitude;

float P1_freq, P2_freq, P3_freq;
float P1_rocof, P2_rocof, P3_rocof;

byte status = IDLE;

void setup() {
  // Setup the LCD
  PMU_LCD.InitLCD();

  init_LCD();
  Serial3.begin(921600);
  attachInterrupt(8, display_on_lcd, RISING);

}
int count = 0;
void loop() {
  // put your main code here, to run repeatedly:
  if (Serial3.available()) {
    int c = Serial3.read();
    if (status == RECEIVING1 && c >= '0' && c <= '9') {
      Phasor_Magnitude_1 = Phasor_Magnitude_1 * 10 + (c - '0');
    } else if (status == RECEIVING2 && c >= '0' && c <= '9') {
      Phasor_Magnitude_2 = Phasor_Magnitude_2 * 10 + (c - '0');
    } else if (status == RECEIVING3 && c >= '0' && c <= '9') {
      Phasor_Magnitude_3 = Phasor_Magnitude_3 * 10 + (c - '0');
    } else if (status == RECEIVING4 && c >= '0' && c <= '9') {
      Phasor_Angle_1 = Phasor_Angle_1 * 10 + (c - '0');
    } else if (status == RECEIVING5 && c >= '0' && c <= '9') {
      Phasor_Angle_2 = Phasor_Angle_2 * 10 + (c - '0');
    } else if (status == RECEIVING6 && c >= '0' && c <= '9') {
      Phasor_Angle_3 = Phasor_Angle_3 * 10 + (c - '0');
    } else if (status == RECEIVING7 && c >= '0' && c <= '9') {
      Day = Day * 10 + (c - '0');
    } else if (status == RECEIVING8 && c >= '0' && c <= '9') {
      Month = Month * 10 + (c - '0');
    } else if (status == RECEIVING9 && c >= '0' && c <= '9') {
      Year = Year * 10 + (c - '0');
    } else if (status == RECEIVING10 && c >= '0' && c <= '9') {
      Hour = Hour * 10 + (c - '0');
    } else if (status == RECEIVING11 && c >= '0' && c <= '9') {
      Minute = Minute  * 10 + (c - '0');
    } else if (status == RECEIVING12 && c >= '0' && c <= '9') {
      Second = Second * 10 + (c - '0');
    }  else if (status == RECEIVING13 && c >= '0' && c <= '9') {
      MilliSecond = MilliSecond * 10 + (c - '0');
    }  else if (status == RECEIVING14 && c >= '0' && c <= '9') {
      Latitude = Latitude * 10 + (c - '0');
    }  else if (status == RECEIVING15 && c >= '0' && c <= '9') {
      Longitude = Longitude * 10 + (c - '0');
    } else if (status == RECEIVING16 && c >= '0' && c <= '9') {
      P1_freq = P1_freq * 10 + (c - '0');
    }  else if (status == RECEIVING17 && c >= '0' && c <= '9') {
      P2_freq = P2_freq * 10 + (c - '0');
    }  else if (status == RECEIVING18 && c >= '0' && c <= '9') {
      P3_freq = P3_freq * 10 + (c - '0');
    } else if (status == RECEIVING19 && c >= '0' && c <= '9') {
      P1_rocof = P1_rocof * 10 + (c - '0');
    } else if (status == RECEIVING20 && c >= '0' && c <= '9') {
      P2_rocof = P2_rocof * 10 + (c - '0');
    } else if (status == RECEIVING21 && c >= '0' && c <= '9') {
      P3_rocof = P3_rocof * 10 + (c - '0');
    }
    else if (status == RECEIVING1 && c == '"') {
      status = RECEIVING2;
    } else if (status == RECEIVING2 && c == '#') {
      status = RECEIVING3;
    } else if (status == RECEIVING3 && c == '$') {
      status = RECEIVING4;
    } else if (status == RECEIVING4 && c == '%') {
      status = RECEIVING5;
    } else if (status == RECEIVING5 && c == '&') {
      status = RECEIVING6;
    } else if (status == RECEIVING6 && c == '(') {
      status = RECEIVING7;
    } else if (status == RECEIVING7 && c == ')') {
      status = RECEIVING8;
    } else if (status == RECEIVING8 && c == '*') {
      status = RECEIVING9;
    } else if (status == RECEIVING9 && c == '+') {
      status = RECEIVING10;
    } else if (status == RECEIVING10 && c == ',') {
      status = RECEIVING11;
    } else if (status == RECEIVING11 && c == '-') {
      status = RECEIVING12;
    } else if (status == RECEIVING12 && c == '.') {
      status = RECEIVING13;
    } else if (status == RECEIVING13 && c == '/') {
      status = RECEIVING14;
    } else if (status == RECEIVING14 && c == ':') {
      status = RECEIVING15;
    } else if (status == RECEIVING15 && c == ';') {
      status = RECEIVING16;
    } else if (status == RECEIVING16 && c == '@') {
      status = RECEIVING17;
    } else if (status == RECEIVING17 && c == '^') {
      status = RECEIVING18;
    } else if (status == RECEIVING18 && c == '?') {
      status = RECEIVING19;
    } else if (status == RECEIVING19 && c == '[') {
      status = RECEIVING20;
    } else if (status == RECEIVING20 && c == ']') {
      status = RECEIVING21;
    } else if (c == '|') {
      status = IDLE;

      //Remote value Received completely, Now display it

      //Calculate Phasor Angles into Float
      Phasor_Angle_1 = Phasor_Angle_1 / 100;
      Phasor_Angle_2 = Phasor_Angle_2 / 100;
      Phasor_Angle_3 = Phasor_Angle_3 / 100;

      //Calculate Phasor Angle using reverse 2pi's complement
      if (Phasor_Angle_1 > 3.143)
        Phasor_Angle_1 = Phasor_Angle_1 - 6.286;
      if (Phasor_Angle_2 > 3.143)
        Phasor_Angle_2 = Phasor_Angle_2 - 6.286;
      if (Phasor_Angle_3 > 3.143)
        Phasor_Angle_3 = Phasor_Angle_3 - 6.286;

      //Calculate Angles in Degrees
      Phasor_Angle_Degree_1 = (Phasor_Angle_1 * 4068) / 71;
      Phasor_Angle_Degree_2 = (Phasor_Angle_2 * 4068) / 71;
      Phasor_Angle_Degree_3 = (Phasor_Angle_3 * 4068) / 71;

      //Interrupt Driven LCD Display
      if (display_now_on_lcd == true) {
        Display_on_LCD();
        display_now_on_lcd = false;
      }

    } else if (c == '!') {
      status = RECEIVING1;

      //Reset the variables to Zero
      Year = 0;
      Month = 0;
      Day = 0;
      Hour = 0;
      Minute = 0;
      Second = 0;
      MilliSecond = 0;
      Phasor_Angle_1 = 0;
      Phasor_Angle_2 = 0;
      Phasor_Angle_3 = 0;
      Phasor_Magnitude_1 = 0;
      Phasor_Magnitude_2 = 0;
      Phasor_Magnitude_3 = 0;
      Phasor_Angle_Degree_1 = 0;
      Phasor_Angle_Degree_2 = 0;
      Phasor_Angle_Degree_3 = 0;
      Latitude = 0;
      Longitude = 0;
      P1_freq = 0;
      P2_freq = 0;
      P3_freq = 0;
      P1_rocof = 0;
      P2_rocof = 0;
      P3_rocof = 0;
    }
  }
}

void init_LCD() {
  //LCD Size 800:480
  //0,0   799,0
  //0,479 799,479
  PMU_LCD.setFont(BigFont);
  PMU_LCD.clrScr();
  PMU_LCD.setColor(0, 255, 0);
  PMU_LCD.print("* Phasor Measurement Unit Local Display *", CENTER, 1);
  PMU_LCD.setColor(255, 153, 51);
  PMU_LCD.print("!!! Developed by Debashish Mohapatra !!!", CENTER, 462);

  // Print out Phase 1 phase 2 and Phase 3
  String Header1 = String("Phasor ") + String(" Phase1 ") + String(" Phase2") + String("   Phase3");
  PMU_LCD.setColor(255, 0, 255);
  PMU_LCD.setFont(Inconsola);
  PMU_LCD.print(Header1, LEFT, 30);
  PMU_LCD.setColor(255, 0, 255);

  PMU_LCD.print("Magni:", LEFT, 80);
  PMU_LCD.print("Angle:", LEFT, 145);
  PMU_LCD.print("Frequ:", LEFT, 220);
  PMU_LCD.print("ROCOF:", LEFT, 295);

  PMU_LCD.setColor(0, 255, 0);
  PMU_LCD.setFont(BigFont);
  PMU_LCD.print(" (VOLT)", LEFT, 115);
  PMU_LCD.print("(DEGREE)", LEFT, 180);
  PMU_LCD.print("  (HZ)", LEFT, 255);
  PMU_LCD.print("(HZ/SEC)", LEFT, 330);
}

void Display_on_LCD() {
  // Print Phase1 Parameters
  PMU_LCD.setFont(Inconsola);
  PMU_LCD.setColor(255, 0, 0);// Red
  PMU_LCD.printNumF(Phasor_Magnitude_1 / 100, 2, 185, 85,  46, 5,48);
  PMU_LCD.print("       ", 185, 150);
  PMU_LCD.printNumF(Phasor_Angle_Degree_1, 2,  185, 150, 46, 6, 48);
  PMU_LCD.printNumF(P1_freq / 10000, 2,          185, 225, 46, 4,48);
  PMU_LCD.printNumF(P1_rocof / 10000, 2,         185, 300, 46, 4,48);

  PMU_LCD.setColor(255, 255, 0);// Yellow
  PMU_LCD.printNumF(Phasor_Magnitude_2 / 100, 2, 380, 85,  46, 5,48);
  PMU_LCD.print("       ", 380, 150);
  PMU_LCD.printNumF(Phasor_Angle_Degree_2, 2,  380, 150, 46, 6, 48);
  PMU_LCD.printNumF(P2_freq / 10000, 2,          380, 225, 46, 4,48);
  PMU_LCD.printNumF(P2_rocof / 10000, 2,         380, 300, 46, 4,48);

  PMU_LCD.setColor(127, 250, 250);// White-Blue
  PMU_LCD.printNumF(Phasor_Magnitude_3 / 100, 2, 600, 85,  46, 5,48);
  PMU_LCD.print("       ", 600, 150);
  PMU_LCD.printNumF(Phasor_Angle_Degree_3, 2,  600, 150, 46, 6, 48);
  PMU_LCD.printNumF(P3_freq / 10000, 2,          600, 225, 46, 4,48);
  PMU_LCD.printNumF(P3_rocof / 10000, 2,         600, 300, 46, 4,48);

  // Print GPS Information
  String Time = String("         UTC Time: ") + String(Hour) + ":" + String(Minute) + ":" + String(Second) + "  ";
  String Date = String("          Date: ") + String(Day) + "/" + String(Month) + "/" + String(Year);
  String Location = String(" Latitude: ") + String(Latitude / 1000) + String(" Longitude: ") + String(Longitude / 1000);

  PMU_LCD.setFont(Ubuntu);
  PMU_LCD.setColor(255, 255, 255);
  PMU_LCD.print(Time, LEFT, 360);
  PMU_LCD.print(Date, LEFT, 395);
  PMU_LCD .print(Location, LEFT, 430);
}
void display_on_lcd() { //ISR
  display_now_on_lcd = true;
}

The Graphical User Interface Developed for Plotting Real Time Data from PMU and logging data in a CSV file

 

from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
##import time
import numpy as np

import serial
ser = serial.Serial('com8', 460800, timeout=1)
# Connect to serial port at COM8, at 460800 bauds

pg.setConfigOptions(antialias=True)
# Enable antialiasing for prettier plots

app = QtGui.QApplication([])
win = pg.GraphicsWindow()

win.setWindowTitle('Realtime PMU Data Monitoring')
# Set the window title

#Define first graph to show the phasor magnitudes
p1 = win.addPlot(title="Phasor Magnitudes",colspan=2)
p1.setRange(yRange=[215, 250],xRange=[0, 1000])
p1.setLabel('left', "Phasor RMS Magnitude", units='Volts')
p1.setLabel('bottom', "Time ( x20 milli Seconds)")
p1.showGrid(x=1, y=1, alpha=.5)
p1.addLegend(offset=[-10,-10])

win.nextRow()
#Define second graph to show the phasor angles
p2 = win.addPlot(title="Phasor Angles")
p2.setRange(yRange=[-200, 200],xRange=[0, 1000])
p2.setLabel('left', "Phasor angles", units='Degree')
p2.setLabel('bottom', "Time ( x20 milli Seconds)")
p2.showGrid(x=1, y=1, alpha=.5)
p2.addLegend(offset=[-40,-10])

#Define third graph to show the phasor polar plot
v = win.addViewBox()
v.setAspectLocked()
v.setFixedWidth(500)
p3 = pg.PlotItem()
p3.setRange(xRange=[-250,250],yRange=[-250, 250])
curvePen = pg.mkPen(color=(255, 255, 255), style=QtCore.Qt.DotLine)
c1 = p3.plot(x=218*np.cos(np.linspace(0, 2*np.pi, 360)), y=218*np.sin(np.linspace(0, 2*np.pi, 360)),pen=curvePen,name="218V",)
c2 = p3.plot(x=50*np.cos(np.linspace(0, 2*np.pi, 360)), y=50*np.sin(np.linspace(0, 2*np.pi, 360)),pen=curvePen,name="50V")
c4 = p3.plot(x=150*np.cos(np.linspace(0, 2*np.pi, 360)), y=150*np.sin(np.linspace(0, 2*np.pi, 360)),pen=curvePen,name="150V")
c6 = p3.plot(x=250*np.cos(np.linspace(0, 2*np.pi, 360)), y=250*np.sin(np.linspace(0, 2*np.pi, 360)),pen=curvePen,name="250V")
c7 = p3.plot(x=np.linspace(-177, 177, 500), y=np.linspace(-177, 177, 500),pen=curvePen)
c8 = p3.plot(x=np.linspace(-177, 177, 500), y=np.linspace(177, -177, 500),pen=curvePen)
c9 = p3.plot(x=np.linspace(-250, 250, 500), y=np.linspace(0, 0, 500),pen=curvePen)
c10 = p3.plot(x=np.linspace(0, 0, 500), y=np.linspace(-250, 250, 500),pen=curvePen)
p3.addLegend(offset=[-1,-1])

g = pg.GraphItem()
v.addItem(g)
v.addItem(c1)
v.addItem(c2)
v.addItem(c4)
v.addItem(c6)
v.addItem(c7)
v.addItem(c8)
v.addItem(c9)
v.addItem(c10)

#plot the curves in the graph areas
curve1 = p1.plot(pen=(255, 0, 0),name="Phase 1(RMS Magnitude)")
curve2 = p1.plot(pen=(255, 255, 0),name="Phase 2(RMS Magnitude)")
curve3 = p1.plot(pen=(0, 0, 255),name="Phase 3(RMS Magnitude)")

curve4 = p2.plot(pen=(255, 0, 0),name="Phase 1")
curve5 = p2.plot(pen=(255, 255, 0),name="Phase 2")
curve6 = p2.plot(pen=(0, 0, 255),name="Phase 3")

# Read the serial data string coming in from the PMU
line1 = ser.readline()
# split the string and extract the phasor parameters
data1 = [float(val1) for val1 in line1.split()]

previous_minute = int(data1[4])

# define the log files, where the phasors will be stored
path_txt = 'pmu_data.txt'
path_txt_plot = 'pmu_data_plot.txt'
path_excel = 'pmu_data.csv'

now_min = "%s-%s-%s_%s-%s" %(int(data1[0]), int(data1[1]), int(data1[2]), int(data1[3]), int(data1[4]))
path_txt_n = '%s_%s' % (now_min, path_txt)
path_txt_plot_n = '%s_%s' % (now_min, path_txt_plot)
path_excel_n = '%s_%s' % (now_min, path_excel)

logfileExcel = open(path_excel_n, 'a')
logfileText = open(path_txt_n, 'a')
logfileTextPlot = open(path_txt_plot_n, 'a')

# define the read function to read the data stream and append the
# parameters to sepatrate arrays
def readfun():
    global data, current_minute, previous_minute, FORMAT, logfileText, logfileTextPlot, logfileExcel, path_txt, path_excel, path_txt_plot
    line = ser.readline()
    data = [float(val) for val in line.split()]

    current_minute = int(data[4])
    if current_minute == (previous_minute+1):
        now_m = "%s-%s-%s_%s-%s" %(int(data[0]), int(data[1]), int(data[2]), int(data[3]), int(data[4]))
        new_path_txt = '%s_%s' % (now_m, path_txt)
        new_path_txt_plot = '%s_%s' % (now_m, path_txt_plot)
        new_path_excel = '%s_%s' % (now_m, path_excel)

        logfileExcel.flush()
        logfileText.flush()
        logfileTextPlot.flush()
        logfileExcel.close()
        logfileText.close()
        logfileTextPlot.close()
        logfileExcel = open(new_path_excel, 'a')
        logfileText = open(new_path_txt, 'a')
        logfileTextPlot = open(new_path_txt_plot, 'a')
        previous_minute = current_minute

    a =  "%s-%s-%s, %s:%s:%s:%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s" % (int(data[0]), int(data[1]), int(data[2]), int(data[3]), int(data[4]), int(data[5]), int(data[6]), data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16], data[17], data[18],"\n")
    logfileExcel.write(a)
    logfileText.write(a)

    bs = int(data[5])
    bms = int(data[6])
    bmS = (bs*1000)+bms
    b =  "%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s %s" % (bmS, data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],data[16], data[17], data[18], "\n")
    logfileTextPlot.write(b)
    return data[6],data[7],data[8],data[9],data[10],data[11],data[12]

readData = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

y2=np.zeros(1000,dtype=float)
y3=np.zeros(1000,dtype=float)
y4=np.zeros(1000,dtype=float)
y5=np.zeros(1000,dtype=float)
y6=np.zeros(1000,dtype=float)
y7=np.zeros(1000,dtype=float)

indx = 0
# define the update function to update the plots with the parameter arrays
def update():
    global curve1, curve2, curve3, indx, y2, y3, y4, y5, y6, y7 #y1

    readData= readfun() #function that reads data from the sensor it returns a list of 6 elements as the y-coordinates for the updating plots

    y2[indx]=readData[1]
    y3[indx]=readData[2]
    y4[indx]=readData[3]
    y5[indx]=readData[4]
    y6[indx]=readData[5]
    y7[indx]=readData[6]

    Rx=y2[indx]*np.cos(np.deg2rad(y3[indx]))
    Ry=y2[indx]*np.sin(np.deg2rad(y3[indx]))
    Yx=y4[indx]*np.cos(np.deg2rad(y5[indx]))
    Yy=y4[indx]*np.sin(np.deg2rad(y5[indx]))
    Bx=y6[indx]*np.cos(np.deg2rad(y7[indx]))
    By=y6[indx]*np.sin(np.deg2rad(y7[indx]))

    pos = np.array([[0,0],[Rx,Ry],[Yx,Yy],[Bx,By]])
    adj = np.array([[0,1],[0,2],[0,3]])
    symbols = ['o','t','t','t']
    lines = np.array([(255,0,0,255,3),(255,255,0,255,3),(0,0,255,255,3)], dtype=[('red',np.ubyte),
          ('green',np.ubyte),('blue',np.ubyte),('alpha',np.ubyte),('width',float)])
    g.setData(pos=pos, adj=adj,pen=lines,size=1,symbol=symbols )

    if indx==999:
       y2=np.zeros(1000,dtype=float)
       y3=np.zeros(1000,dtype=float)
       y4=np.zeros(1000,dtype=float)
       y5=np.zeros(1000,dtype=float)
       y6=np.zeros(1000,dtype=float)
       y7=np.zeros(1000,dtype=float)
       indx = 0
    else:
       indx+=1

    curve1.setData(y2)# update magnitudes
    curve2.setData(y4)
    curve3.setData(y6)
    curve4.setData(y3)# update angles
    curve5.setData(y5)
    curve6.setData(y7)
    app.processEvents()

timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start()

if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_'):
        QtGui.QApplication.instance().exec_()

 

 

You don't have a pdf plugin, but you can download the pdf file.