// 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();
}