// Power Line 3rd Harmonic Meter
// Hardware: PIC18F2550 PIC project board
// Fuse settings: 4MHz, no divide CPU clock, 4MHz input
// The hex file is ready to be loaded by PICkit II
// Ananlog input 0-5Vpeak fullwave 50Hz AC signal
// Alarm when distortion >3%
// source code was compiled with Mikro-C compiler
// Copyright 2006-9 Wichit Sirichote, kswichit@kmitl.ac.th

#define ADC_CS1 PORTC.F0   // output bit
#define ADC_SDO PORTC.F1  // input bit
#define ADC_SCK PORTC.F2  // output bit

#define protect1 PORTC.F7 // test indicator
#define protect PORTC.F6 // output protect

#define on 0
#define off 1

// change the maximum distortion here
// IEEE 519-1992 for low voltage THDV is 5%
#define max 5.0

char *text = "Power Line 3rd";
char *text2 = "Harmonic Meter";
short alarm=0;
short overload=0;


const float sine[] = {
0,
0.098016374,
0.195088811,
0.290282466,
0.382680585,
0.47139334,
0.55556639,
0.634389115,
0.707102423,
0.773006055,
0.831465332,
0.881917269,
0.923875995,
0.956937428,
0.980783176,
0.995183594,
1,
0.99518601,
0.980787986,
0.956944585,
0.923885429,
0.881928891,
0.831479029,
0.773021695,
0.707119856,
0.634408173,
0.555586888,
0.471415082,
0.382703362,
0.290306057,
0.195112991,
0.098040908};


const float cosine[]={
1,
0.995184802,
0.980785581,
0.956941007,
0.923880712,
0.88192308,
0.83147218,
0.773013875,
0.707111139,
0.634398644,
0.555576639,
0.471404211,
0.382691974,
0.290294261,
0.195100901,
0.098028641,
1.23268E-05,
-0.098004106,
-0.195076721,
-0.290270669,
-0.382669197,
-0.471382468,
-0.55555614,
-0.634379586,
-0.707093707,
-0.772998234,
-0.831458483,
-0.881911458,
-0.923871277,
-0.95693385,
-0.980780771,
-0.995182385};


const float sinex3[]={
0,
0.290282466,
0.55556639,
0.773006055,
0.923875995,
0.995183594,
0.980787986,
0.881928891,
0.707119856,
0.471415082,
0.195112991,
-0.097991839,
-0.382657808,
-0.634370058,
-0.831451635,
-0.956930271,
-0.999999999,
-0.956951741,
-0.831492725,
-0.63442723 ,
-0.382726139,
-0.098065443,
0.195040451,
0.471349854,
0.707067556,
0.881894025,
0.980773555,
0.995190841,
0.923904296,
0.773052973,
0.555627884,
0.290353241};

const float cosinex3[]={
1,
0.956941007,
0.83147218,
0.634398644,
0.382691974,
0.098028641,
-0.195076721,
-0.471382468,
-0.707093707,
-0.881911458,
-0.980780771,
-0.995187218,
-0.923890146,
-0.773029514,
-0.555597137,
-0.290317853,
-3.69804E-05,
0.290247077 ,
0.555535641,
0.772982594,
0.923861842,
0.995179968,
0.980795199,
0.881946322,
0.707146004,
0.471447695,
0.19514926,
-0.097955036,
-0.382623642,
-0.63434147,
-0.831431088,
-0.956919535};

const float inputsine[]={
0,
30.38507582,
60.47753134,
89.98756431,
118.6309814,
146.1319353,
172.2255807,
196.6606258,
219.2017511,
239.6318769,
257.7542529,
273.3943535,
286.4015583,
296.6506028,
304.0427846,
308.5069141,
310,
308.5076632,
304.0442756,
296.6528213,
286.404483,
273.3979562,
257.7584989,
239.6367253,
219.2071552,
196.6665335,
172.2319353,
146.1386754,
118.6380423,
89.99487781,
60.48502709,
30.39268162,
};

const float inputsquare[]={
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
9 ,
8,
8,
8,
8,
7,
7,
7,
};

unsigned int read_ADC()
{
     unsigned int temp;
     temp =0;
     ADCON0 = 0x03; // start ADC

     while(ADCON0&0x02) // wait unitl done
     continue;
     temp = ADRESH;
     temp <<=8;
     return (temp|ADRESL);
}
     
     


float xin[32];

void capture_input()
{
    int i;
    char buffer[32];
    
    if(alarm)
    {Lcd_Out(1,15,"><");
    }
    else Lcd_Out(1,15,"--");
    
    Delay_ms(200);
    
    if(overload) Lcd_Out(1,10,"ov");
    else Lcd_Out(1,10,"  ");
    
   // finding start with cycle limit

    for(i=0; read_ADC()>3 && i<64; i++)
    continue;

    for(i=0; i<32; i++)
    {
     xin[i] = read_ADC();
     //Delay_us(104); //50); //312); //(312);   // for 10ms/32
     }
     // tested delay value for 0 to 180 degrees with 32 samples

     overload=0;
     
     for(i=0; i<32; i++)
      {
         if(xin[i]>1010) overload=1;

      }


     /*
     for(i=0; i<32; i++)
     {
     sprintf(buffer,"ADC(%d)= %f",i,xin[i]);
     Lcd_Cmd(LCD_FIRST_ROW);
     Lcd_Out_CP(buffer);
     Delay_ms(3000);

     }
     
     */
     

     




     
}

void compute_THD()
{
    char i;
    char buffer[32];
    float n=0;
    float k=0;
    float nx3=0;
    float kx3=0;
    float THD;
    
    Lcd_Out(1,15,"  ");
    Delay_ms(100);
    
       for(i=0; i<32; i++)
    {
        n+= sine[i]*xin[i]*0.31;    // input * fundamental
        k+= cosine[i]*xin[i]*0.31;   // 1000 = 310V

        nx3+= sinex3[i]*xin[i]*0.31;  // input * 3rd harmonic
        kx3+= cosinex3[i]*xin[i]*0.31;

    }
    n = sqrt(n*n+k*k);
    n=(4*0.0003125/0.02)*n;  // compute amplitude of fundamental frequency
    nx3 = sqrt(nx3*nx3+kx3*kx3);
    nx3=(4*0.0003125/0.02)*nx3;  // compute amplitude of 3rd harmonic
    THD = nx3*100/n; // compute %THD 3rd/fundamental

  // if %THD >10 or 1st harmonic amplitude <20 then shutdown
    if (THD>max || n <20)
    {
     protect = protect1= on;
     alarm=1;
     }
    else
    {
    protect = protect1=off;
    alarm=0;
    }
    

  sprintf(buffer,"Fund=%.0f",n);
  Lcd_Cmd(LCD_FIRST_ROW);
  Lcd_Out_CP(buffer);

  sprintf(buffer,"3rd=%.0f",nx3);
  Lcd_Cmd(LCD_SECOND_ROW);
  Lcd_Out_CP(buffer);

  sprintf(buffer,"HD=%.1f%% ",THD);
  Lcd_Out(2,9,buffer);

}

void main() {

   Delay_ms(200);
  ADCON1 = 0x0E;  // ADC0 = analog input
  ADCON0 = 0x00; // select channel 0
  ADCON2 = 0x92; // ADC frequency = FOSC/32
  TRISA  = 0xFF;  // PORTA is input

  TRISB = 0;                // PORTB is output
  PORTC = 0xFF;
  TRISC = 0x02;             // PORTC is output port
  protect = protect1= on;
   

   
  protect = protect1=off;

  Lcd_Init(&PORTB);         // Initialize LCD connected to PORTB
  Lcd_Cmd(Lcd_CLEAR);       // Clear display
  Lcd_Cmd(Lcd_CURSOR_OFF);  // Turn cursor off
  
  Lcd_Out(1, 1,text);
  Lcd_Out(2, 1,text2);
  Delay_ms(3000);
  Lcd_Cmd(Lcd_Clear);Lcd_Cmd(Lcd_Clear);


  while(1)
  {
  capture_input();
  compute_THD();
   }
  
}