package uk.co.mmscomputing.dsp.phone; import java.io.*; public class FSKInputStream extends FilterInputStream{ static public final int QUIET = 0; static public final int MARK = 1; // 1300Hz static public final int SPACE = 2; // 2100Hz static private final int max = 7; static private final double[][] retab = new double[max][max]; static private final double[][] imtab = new double[max][max]; protected byte[] pcm=new byte[2]; // can only read even number of bytes at a time from PCM AudioInputStream. protected double[] au=new double[max]; // buffer audio private byte[] bytes = new byte[256]; // buffer data private int len,index; public FSKInputStream(InputStream in){ super(in); } protected boolean readSample()throws IOException{ if(in.read(pcm)!=2){return false;} // read one pcm sample for(int i=au.length-1;i>0;i--){au[i]=au[i-1];} // first in first out au[0]=(pcm[0]&0x00FF)|(pcm[1]<<8); return true; } private int getFrequency(){ // [1] p.363 double maxmag=0,rev,imv,mag; int maxfrq=0,lastfrq=max>>1; for(int i=0;i<=lastfrq;i++){ // get frequency with largest spectral magnitude rev=0;imv=0; for(int k=0;k<au.length;k++){ rev+=retab[i][k]*au[k]; imv-=imtab[i][k]*au[k]; } rev = (Math.abs(rev)<0.00001)?0.0:rev; imv = (Math.abs(imv)< 0.001)?0.0:imv; mag = rev*rev+imv*imv; if(mag>maxmag){ maxmag=mag; maxfrq=i; } } if(maxmag<1e6){ return QUIET;} // Only noise if(maxfrq==3){ return QUIET;} return maxfrq; // QUIET(0Hz),MARK(1300Hz),SPACE(2100Hz) } // 1200 baud = 1200 bits/sec; 8000 Hz; public int readQuiet()throws IOException{ int count=0; while(true){ if(!readSample()){ if(count==0){return -1;} // EOF break; } if(getFrequency()==QUIET){ // FSK modulation not up and running yet count++; }else{ // change of symbol break; } } return count; } public int readBits(int symbol)throws IOException{ int freq,count=0; while(true){ if(!readSample()){ if(count==0){return -1;} // EOF break; } freq=getFrequency(); if(freq==symbol){ count++; }else if(count==0){ // change of symbol, but too early count++; }else if(freq==QUIET){ // FSK modulation not up and running yet break; }else{ // change of symbol break; } } count++; // add one for change of symbol in last readBits invocation return (int)(((double)count)*0.15+0.5); // 1200/8000 bits = 0.15 bits } public boolean readMarkSignal(int minlen)throws IOException{ int b,i,bits; do{ i=readQuiet(); if(i==-1){return false;} bits=readBits(MARK); // minlen MARK bits if(bits==-1){return false;} }while(bits<minlen); return true; } protected int readBuffer()throws IOException{ int len=0; while(len<bytes.length){ int bits=readBits(SPACE); // System.err.println("SPACE "+bits); if(bits==-1){return (len==0)?-1:len;} if(bits== 0){throw new IOException(getClass().getName()+".readBuffer:\n\tCorrupt message.Not enough bits per byte. Missing START bit.");} bits--; // minus start bit int b=0; int i=bits; // first 'bits' bit are 0 while(true){ bits=readBits(MARK); // System.err.println("MARK "+bits); if(bits==-1){throw new IOException(getClass().getName()+".readBuffer:\n\tCorrupt message.Not enough bits per byte. Missing MARK bits.");} if((i+bits)<8){ b|=(~(-1<<bits))<<i; }else if((i+bits)==9){ bits--; // minus stop bit b|=(~(-1<<bits))<<i; break; }else{ // missing start bit, assume end of FSK coding bits=8-i; // fill rest with 1s; After last STOP(1) bit follow 1..10 MARK(1) bits b|=(~(-1<<bits))<<i; // System.err.println("\t\t\tBYTE["+len+"] = 0x"+Integer.toHexString(b)+" "+Integer.toBinaryString(b)); bytes[len++]=(byte)b; return len; } i+=bits; bits=readBits(SPACE); // System.err.println("SPACE "+bits); if(bits==-1){throw new IOException(getClass().getName()+".readBuffer:\n\tCorrupt message.Not enough bits per byte. Missing SPACE bits.");} i+=bits; if(i>8){throw new IOException(getClass().getName()+".readBuffer:\n\tCorrupt message.Too many bits per byte. Missing STOP bit.");} } // System.err.println("\t\t\tBYTE["+len+"] = 0x"+Integer.toHexString(b)+" "+Integer.toBinaryString(b)); bytes[len++]=(byte)b; } return len; } public int available()throws IOException{ return len-index; } public int read()throws IOException{ while(!(index<len)){ len=readBuffer(); if(len==-1){ index=-1; return -1; } index=0; } return bytes[index++]&0x00FF; } public int read(byte[] buf, int off, int len)throws IOException{ if(buf==null){ throw new NullPointerException(getClass().getName()+".read(byte[] buf, int off, int len): buf is null"); } if((off<0)||(len<0)||(buf.length<(off+len))){ throw new IndexOutOfBoundsException(getClass().getName()+".read(byte[] buf, int off, int len): index off ["+off+"] or len ["+len+"] out of bounds ["+buf.length+"]."); } for(int i=0;i<len;i++){ int b=read(); if(b==-1){return (i==0)?-1:i;} buf[off++]=(byte)b; } return len; } static{ for(int i=0;i<max;i++){ for(int k=0;k<max;k++){ double tmp=2.0*Math.PI*((double)i)*((double)k)/((double)max); retab[i][k]=(1.0/(double)max)*Math.cos(tmp); imtab[i][k]=(1.0/(double)max)*Math.sin(tmp); } } } public static void main(String[] argv){ try{ String file="uk/co/mmscomputing/dsp/phone/fsk.raw"; FSKInputStream in=new FSKInputStream(new FileInputStream(file)); int b,i=0; if(in.readMarkSignal(55)){ while((b=in.read())!=-1){ System.out.println(" "+i+" , "+Integer.toHexString(b)+" , "+Integer.toBinaryString(b)+" , "+((char)b)); i++; } } in.close(); }catch(Exception e){ e.printStackTrace(); } } } // [1] Paul A. Lynn, Wolfgang Fuerst; Introductory Digital Signal Processing 2nd; Wiley ISBN 0-471-97631-8 // ETSI ES 201 912 v 1.2.1 (2004-06) // ETS 300 659-1/2 (1997-02)