// radio meteor counter  - agw 20121101

// requires processing version 1.5.1

// modified from

/**

* LiveSpectrogram_window

* Takes successive FFTs and renders them onto the screen as grayscale, scrolling left.

* This version includes variable window length.  Drag the mouse left and right to change the

* length of the Hanning window applied before the FFT is taken.

*

* Dan Ellis dpwe@ee.columbia.edu 2010-01-15

*/

import ddf.minim.analysis.*;

import ddf.minim.*;

Minim minim;

AudioInput in;

FFT fft;

// Configuration: spectrogram size (pixels)

int colmax = 256;

int rowmax = 256;

// Sample rate

//float sampleRate = 22050;

float sampleRate = 44100;

// buffer size (= FFT size, must be power of 2)

int bufferSize =4096;

// Variables

int[][] sgram = new int[rowmax][colmax];

int[][] sgramb = new int[rowmax][colmax];

byte[] mgram = new byte[256*256]; // audio meteor data

//

int[] phfreq = new int[256]; //peak hold freq

int[] phlvl = new int[256]; //peak hold lvl

int[] fhlasum = new int[256]; // sum level by freq

int[][] cgm = new int[32][25];  // colorgram counter

byte[] svcgm = new byte[24*31]; //color gram in bytes

int[] sgmtrc = new int[256] ; // sigmin value trace

int col;

int leftedge;

int window_len = bufferSize;

int count =0;

int sgmcount = -1 ;

int trgtime = 3000;

//

int fsens = 0;

int lsens = 0;

int ptfreq = 0;

int ptlvl =0;

int phldfreq =0;

int phldlvl =0;

int ptlavg = 0;

int palavg =0;

int phalavg = 0 ;

int palsum = 0;

int phalsum = 0;

int phalmin = 24001;

int tphalsum = 26000;

int phaldiff = 0;

int phalmax = 0;

int pavgtrg = 100;

int svphaldiff = 1000;

int lngdevnt = 0;

int sgmin = 0;

int freqalvl =0;

int freqtrk = 100;

int freqhalvl = 0;

//

String msfreq = "000.00";

float mfreq =  00.00;

float mtrstart = 0.0;

float mtrdura = 0.0;

String Mtrhist = " ";

float freqmp = 11.71 ;

// time stuff here

String YYYY = "2012" ;

String DMM = "12" ;

String DD = "31" ;

String LHH = "00" ;

String UHH = "00" ;

int TMZ = 10 ; // set time zone here for UTC

int DSV = 1 ; // set daylight saving offset

int dateln = -1; // set dateline

 String TMM = "00" ;

String SS = "00" ;

String YYYYMMDD = "20121022" ;

String LHHMMSS = "00:00:00" ;

String UHHMMSS = "00:00:00" ;

String Fname = "20121021_000000" ;

String lstFname = "20121021_000000";

//

//

PFont fontA = createFont("SanSerif", 12);

PFont fontB = createFont("SanSerif", 16);

//

void setup()

{

 size(colmax+60+300+300+300, rowmax, P3D);

 textMode(SCREEN);

//

//fontA = loadFont(fontA);

//fontB = loadFont(fontB);

 minim = new Minim(this);

 // setup audio input

 in = minim.getLineIn(Minim.MONO, bufferSize, sampleRate);

 fft = new FFT(in.bufferSize(), in.sampleRate());

 // suppress windowing inside FFT - we'll do it ourselves

 fft.window(FFT.NONE);

 //

 currentDT(); // get time now

 //

 

 //

 background(0);

}

void draw()

{

 //background(0);

 stroke(255);

 

 // grab the input samples

 float[] samples = in.mix.toArray();

 // apply windowing

 for (int i = 0; i < samples.length/2; ++i) {

   // Calculate & apply window symmetrically around center point

   // Hanning (raised cosine) window

   float winval = (float)(0.5+0.5*Math.cos(Math.PI*(float)i/(float)(window_len/2)));

   if (i > window_len/2)  winval = 0;

   samples[samples.length/2 + i] *= winval;

   samples[samples.length/2 - i] *= winval;

 }

 // zero out first point (not touched by odd-length window)

 samples[0] = 0;

 // perform a forward FFT on the samples in the input buffer

 fft.forward(samples);

 // fill in the new column of spectral values

 for(int i = 0; i < rowmax /* fft.specSize() */; i++) {

   sgram[i][col] = (int)Math.round(Math.max(0,2*20*Math.log10(1000*fft.getBand(i))));

   //

         palavg = (palavg+sgram[i][col])/2;

         palsum = palsum+sgram[i][col] ;

         fhlasum[col] = palsum ;

         if ( palavg >= phalavg ) { phalavg = palavg ;}

          freqalvl = (freqalvl+sgram[col][i])/2;  // sum all lvl at freq

          if ( fhlasum[i] <= freqalvl) {  fhlasum[i] = (fhlasum[i]+freqalvl)/2  ; }

         ptlvl = sgram[i][col];

         ptlavg = (ptlavg+ptlvl)/2 ;

         ptfreq = i;

         fsens = i;

         lsens = sgram[i][col];

         // counts++;

         if ( phlvl[i]<= sgram[i][col] ) { phlvl[i]=sgram[i][col] ; phfreq[i]+=1; }

         //

           mgram[i+(256*col)] =  byte(sgram[i][col]-128); //mgram in bytes

         //

           }

   phalsum = palsum;

 

   palsum = 0;

 

 // next time will be the next column

 col = col + 1;

 // wrap back to the first column when we get to the end

 if (col == colmax) { col = 0;}

 

 // Draw points.

 // leftedge is the column in the ring-filled array that is drawn at the extreme left

 // start from there, and draw to the end of the array

 for (int i = 0; i < colmax-leftedge; i++) {

   for (int j = 0; j < rowmax; j++) {

        stroke(0,sgram[j][i+leftedge],j);

         point(i,height-j);

   }

 }

 

 // Draw the rest of the image as the beginning of the array (up to leftedge)

 for (int i = 0; i < leftedge; i++) {

   for (int j = 0; j < rowmax; j++) {

        stroke(0,sgram[j][i],j);

         point(i+colmax-leftedge,height-j);

        

   }

 }

 //

 

 // Next time around, we move the left edge over by one, to have the whole thing

 // scroll left

 leftedge = leftedge + 1;

 // Make sure it wraps around

 if (leftedge == colmax) { leftedge = 0; }

 //

 // Add frequency axis labels

 stroke(255,255,255);

 fill(0,100,100);

 rect(colmax+2,0, 60-2, 256);

 fill(255,255,255);

 int x = colmax + 2; // to right of spectrogram display

 stroke(255);

 fill(255,255,255);

 line(x,0,x,height); // vertical line

 line(x+56,0,x+56,height); // vertical line

 // Make text appear centered relative to specified x,y point

 textAlign(LEFT,CENTER);

 for (float freq = 0.0; freq < in.sampleRate()/2; freq += 500.0) {

   int y = height - fft.freqToIndex(freq); // which bin holds this frequency?

   line(x,y,x+3,y); // add tick mark

   line(x+56,y,x+56+3,y); // add tick mark

   text(Math.round(freq)+" Hz", x+5, y); // add text label

        freqcntls();

         }

 //

  stroke(255);

  mfreq = fsens*freqmp  ;

  msfreq = nf(mfreq,4,2);

//

  if (millis()<=(mtrstart+trgtime)) // trigertime

         {

          copy((256-128), 0, 128, 256, (256+60), 0, 128, 256);

          copy(931,21, 160, 200, (256+63+128),21, 160, 200);

          mtrdura = millis()-mtrstart ;

          

          lstFname = Fname+"_F"+ptfreq+"_L"+ptlvl;

          int startui = 620 ;

          fill(1,1,1);

          rect(colmax+60, rowmax-15, 300, 15);

          fill(255,255,255);

          text(lstFname, colmax+60+20, rowmax-6);

   }

   //

 stroke(255,255,255);

 fill(0,100,100);

 line(colmax+60+(128),0,colmax+60+(128), height); // dots line

 line(colmax+60,0,colmax+60, height); // dots line

 rect(colmax+60,0, 300, 20);

 fill(255,255,255);

 text("METR:"+count+"  FREQ:"+msfreq+"  DURA:"+nf(mtrdura,2,2)+"  LvL  :"+lsens, colmax+65,10);

 //

 cgm[int(DD)][abs(1+int(UHH))] = count ; // current DD UHH counter position

 //

 userinfo() ;

 plotff();

 sigmad();

 //

 if (mtrdura >= 2900) {

           if ( lngdevnt == 0  ) {

           saveimg();

            }

 mtrdura=0.0 ; sgmcount = 0; count+=1; lngdevnt = 0;sgmin=0;}

 //

 freqalvl = 0 ;

 //

if ( minute()==59 ) { if ( second()==59 ) { }}

              //

if ( minute()==00 ) { if ( second()==00 ) { count=0  ; }} // reset meteor count to 0

//

//end draw here

}

void keyPressed() {

  //

   if (key =='x') { exit();}

   if (key =='c') {

         count = 0;

         mfreq = 0;

         fsens=0;

         mtrdura = 0;

         lsens =0;

         background(0);

           }

   if (key =='g') { colorgram() ; }

   if (key =='n') { count-=1 ; }

   if (key =='m') { count+=1 ; }

   if (key =='k') { freqtrk-=1 ; }

   if (key =='l') { freqtrk+=1 ; }

   if (key =='-') { tphalsum-=50 ; }

   if (key =='=') { tphalsum+=50 ; }

}

void userinfo() {

        //

        int startui = 620 ;

   fill(50,100,100);

   rect(startui, 25, 300, 150, 5,5,5,5);

   fill(255,255,255);

   textFont(fontB, 16);

   currentDT() ;

   text(YYYYMMDD+" UTC:"+UHHMMSS+" LCL:"+LHHMMSS+"\n-= RADIO METEOR COUNTER =-\n"+"[E(x)it]                [(C)lear counter] \n"+"[Sensitivity -(-)/+(=)]",  startui+10, 70 );

   //

   textFont(fontA, 12);

        text(lstFname,startui+10,128);

   //

}

void currentDT() {

 // current date here

  if (hour() <=10) { DD = nf(day()+dateln,2); }

         else { DD = nf(day(),2); }

          // Values from 1 - 31

        DMM = nf(month(),2);  // Values from 1 - 12

         YYYY = nf(year(),2);  // 2003, 2004, 2005, etc.

 // current time here

        if (hour() >=12) {UHH = nf((abs(hour()-(TMZ+DSV))),2) ; }

         else { UHH = nf((24-abs(hour()-(TMZ+DSV))),2) ; }  // Values from 0 - 23

        LHH = nf((abs(hour())),2) ;        // Values from 0 - 23

        TMM = nf(minute(),2) ; // Values from 0 - 59

        SS = nf(second(),2) ;  // Values from 0 - 59

         //

        YYYYMMDD = YYYY+DMM+DD ; // "20121022" ;

         UHHMMSS = UHH+":"+TMM+":"+SS ; // "00:00:00" ;

         LHHMMSS = LHH+":"+TMM+":"+SS ; // "00:00:00" ;

        Fname = YYYY+DMM+DD+"_"+UHH+TMM+SS ; // file name UTCtime stamp

        //

 }

 

void saveimg() {

          // save image to \data\meteor as filename_Fptfreq_Mdate_UTC.jpg

          // remove the // from next line to save an image of trigger event

          saveFrame("\\data\\meteor\\"+Fname+"_F"+ptfreq+"_L"+ptlvl+".png");

          saveFrame("\\data\\LastSave.jpg");

          print("\nSaved image "+(count+1)+" "+Fname)  ;        

        }

void plotff() {

            //

            int startff = 620 ;

            //

            fill(1,1,1);

             rect(startff+1, rowmax-1, 298, -75);

             fill(255,255,255);

            //

            for (int i = 0; i < 255; i+=1) {   // peak freq

           //

             stroke((1+phfreq[i]*2)*24,(1+phfreq[i]*2)*8,255-(1+phfreq[i]*2)*24);

             fill((1+phfreq[i])*24,(1+phfreq[i])*8,255-(1+phfreq[i])*24);

             //rect(startff+10+(i),rowmax-20,1,15) ;

             rect(startff+10+i, (rowmax-2), 1, -(phfreq[i]*10));

             fill(255,255,255);

            }

           

           

             for (int j = 1; j < 25; j+=1) {                // one day colorgram

              int i = int(DD);

             stroke(0,0,0);

             fill((1+cgm[i][j]/10)*24,(1+cgm[i][j]/10)*8,255-(1+cgm[i][j]/10)*24);

             rect(startff+10+(j*10),5,10,10) ;

             fill(255,255,255);

             //

            }

                phaldiff = abs(phalsum-tphalsum) ;

            //

            if (phalsum <= tphalsum) {

              currentDT();

                mtrstart = millis();sgmcount++; // count++; sgmin=1;

                //

                text("SIG-MIN", startff+200, rowmax-100);

                print("\n"+Fname+" "+phalsum+" "+sgmcount+" "+abs(phalsum-tphalsum)+" "+phaldiff);

                //

               if (sgmcount>255) { sgmcount = 1 ; sgmin=0; }

                sgmtrc[sgmcount] = abs(phalsum-tphalsum)/66;

                if (phalsum<=4000 ) {  // sig dropout has occured

                   stroke(255,255,255);

                   fill(100,25,0);

                   rect(startff+195, rowmax-110, 80, 20);

                   fill(0,250,100);

                   text(" DROP-OUT ", startff+200, rowmax-100);

                }

             }

           //

           text(tphalsum+" "+nf(freqtrk*11.71,4,2)+" "+(fhlasum[freqtrk])+"\n"+phalsum+" "+(phalsum-tphalsum), startff+10, rowmax-100);

}

 

void sigmad(){

 // Pingo Meter - draw visual of sig level

 //

  int startpgo = 930 ;

   stroke(255,255,255);

   fill(1,1,1);

   rect(startpgo, 1, 285, 250, 5,5,5,5);

   fill(255,255,255);

   stroke(255,255,255);

  //

   for (int i = 0; i < 255; i++)

  {

        if (sgmtrc[i]>=5000 ) { sgmtrc[i]=1; phfreq[i]=1;fhlasum[i]=1; } //

        if (phalsum<=4000 ) { sgmtrc[i]=1;phfreq[i]=1;fhlasum[i]=1;  } // sig dropout has occured

        if (sgmtrc[i]<=0 ) { sgmtrc[i]=1;phfreq[i]=1; fhlasum[i]=1;} //

   //

        stroke((1+sgmtrc[i]/2)*24,(1+sgmtrc[i]/2)*8,255-(1+sgmtrc[i]/2)*24);

        fill((1+sgmtrc[i]/2)*24,(1+sgmtrc[i]/2)*8,255-(1+sgmtrc[i]/2)*24);

        ellipse(startpgo+10+(i), 128, -5, -sgmtrc[i]);  // siglvl

   // rect(startpgo+10+(i), 128+sgmtrc[i], 1, -sgmtrc[i]*2);  // siglvl

   //

        fill((1+sgmtrc[3]/2)*24,(1+sgmtrc[3]/2)*8,255-(1+sgmtrc[3]/2)*24);

        text("PINGO METER", startpgo+10, 10);

        if(sgmin==1){

         if (sgmcount>=128 ) {  mtrstart = millis(); lngdevnt = 1; text("PINGO METER - LONG DURA EVENT ", startpgo+10, 10); }

        }

        if (sgmcount == 0) { sgmtrc[i]-=1; phfreq[i]-=1;  } //fade pingo signal  fhlasum[i]-=1;

        //

  }

   //

}

void freqcntls(){

  // freqncey controls

  //line(colmax+80+(trig/2),0,colmax+80+(trig/2),height); // trig line

   textAlign(RIGHT,CENTER);

   text("<[ftk]", colmax+28,rowmax-(freqtrk)); // freq notch cut value line

   textAlign(LEFT,CENTER);

   //

}

void colorgram(){

//  24hrx31day color gram model

for (int i = 1; i < 32 ; i++) {

   for (int j = 1; j < 25; j++) {

         //

           stroke(255,255,255);

           fill(1,1,1);

           rect(960, 1, 285, 250, 5,5,5,5);

             stroke(50,50,50);

            // make grid

             //fill current color gram data save to file later

             fill((1+cgm[i][j]/10)*24,(1+cgm[i][j]/10)*8,255-(1+cgm[i][j]/10)*24);

             rect(colmax+80+256+256+100+(i*5), 50+(j*5),5,5) ;

             fill(255,255,255);

             // color DD UHH grid point

             fill((1+count/10)*24,(1+count/10)*8,255-(1+count/10)*24);

             rect(colmax+80+256+256+100+(int(DD)*5), 50+((abs(1+int(UHH)))*5),5,5) ;

             fill(255,255,255);

             // make color bar

             fill((j*24),(j*8),255-(j*24));

             rect(colmax+80+256+256+100+325, 50+(j*5),5,5) ;

             fill(255,255,255);

             // label color bar

             textAlign(LEFT,RIGHT);

             text("10\n\n\n50\n\n\n\n100\n\n\n150\n\n\n\n200\n\n\n250", colmax+80+256+256+316+73, 20);

             textAlign(LEFT,CENTER);

         //

         

        

   }

 }

}

void stop()

{

 // always close Minim audio classes when you finish with them

 in.close();

 minim.stop();

 super.stop();

}