DSP tutorial: FSK decoder
Last modified: March 4th, 2012This example is a very basic (A)FSK decoder. It doesn’t look for a start byte, uses no bit synchronization. It oversamples 3 times (divides one bit length count of samples into 3 pieces called chunks) and decides between 0 and 1 by calculating the dominant frequency in these chunks. After the loop gathered 8 bits it reconstructs the sent ASCII byte and displays it. Operates poorly even with a little noise on the channel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | public void run() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // this will store sound data int oneBitSampleCount = (int)Math.round(SAMPLERATE/BITSPERSEC); int oneChunkSampleCount = (int)Math.round(oneBitSampleCount/3); System.out.println("One bit length: " + 1/BITSPERSEC + " seconds, " + oneBitSampleCount + " samples"); System.out.println("One chunk length: " + (1/BITSPERSEC)/3 + " seconds, " + oneChunkSampleCount + " samples"); byte[] abBuffer = new byte[oneChunkSampleCount*2]; double[] abBufferDouble = new double[oneChunkSampleCount]; int[] chunkResults = new int[3]; int chunkResultp = 0; int byteResult = 0; int byteResultp = 0; int numberOfInvalidResults = 0; fft = new DoubleFFT_1D(abBufferDouble.length); // we need to initialize a buffer where we store our samples as complex numbers. first value is the real part, second is the imaginary. double[] fftData = new double[abBuffer.length]; // size == samples * 2 int binOfFreq0 = (int)Math.round((FREQ0/(double)SAMPLERATE)*fftData.length); int binOfFreq1 = (int)Math.round((FREQ1/(double)SAMPLERATE)*fftData.length); System.out.println("Bin of freq. 0 (" + FREQ0 + "Hz): " + binOfFreq0); System.out.println("Bin of freq. 1 (" + FREQ1 + "Hz): " + binOfFreq1); tdl.start(); try { while (!Thread.interrupted()) { // waiting for the buffer to get filled while (tdl.available() < abBuffer.length) Thread.sleep(0, 1); // without this, the audio will be choppy int bytesRead = tdl.read(abBuffer, 0, abBuffer.length); // converting frames stored as bytes to double values int samplesRead = bytesRead / 2; for (int i = 0; i < samplesRead; i++) { abBufferDouble[i] = ((abBuffer[i * 2] & 0xFF) | (abBuffer[i * 2 + 1] << 8)) / 32768.0; } for (int i = 0; i < fftData.length; i++) fftData[i] = 0; for (int i = 0; i < samplesRead; i++) { // copying audio data to the fft data buffer, imaginary part is 0 fftData[2 * i] = abBufferDouble[i]; fftData[2 * i + 1] = 0; } // calculating the fft of the data, so we will have spectral power of each frequency component // fft resolution (number of bins) is samplesNum, because we initialized with that value fft.complexForward(fftData); double PWRFreq0 = Math.sqrt(fftData[binOfFreq0] * fftData[binOfFreq0] + fftData[binOfFreq0+1] * fftData[binOfFreq0+1]); double PWRFreq1 = Math.sqrt(fftData[binOfFreq1] * fftData[binOfFreq1] + fftData[binOfFreq1+1] * fftData[binOfFreq1+1]); if (PWRFreq0 > PWRTHRESHOLD || PWRFreq1 > PWRTHRESHOLD) { if (PWRFreq0*PWRFreq0-PWRFreq1*PWRFreq1 < 0) chunkResults[chunkResultp] = 1; else chunkResults[chunkResultp] = 0; //System.out.print("PWRFreq0: " + PWRFreq0 + " PWRFreq1: " + PWRFreq1 + " chunk[" + chunkResultp + "]=" + chunkResults[chunkResultp]); chunkResultp++; if (chunkResultp == 3) { // if we got 3 chunks chunkResultp = 0; int bitResult; if (chunkResults[0] + chunkResults[1] + chunkResults[2] >= 2) bitResult = 1; else bitResult = 0; //System.out.print(" resulting bit: " + bitResult); if (byteResultp == 0 && bitResult != 0) { // if the first bit of the byte is not 0 byteResultp = byteResult = chunkResultp = 0; } else { byteResult += bitResult << (7-byteResultp); byteResultp++; if (byteResultp == 8) { System.out.println(" *** char: " + (char)byteResult + "(" + byteResult + ")"); byteResultp = byteResult = 0; } } } //System.out.println(); numberOfInvalidResults = 0; } else { //System.out.println("invalid!"); numberOfInvalidResults++; } if (numberOfInvalidResults > 0) { //System.out.println("reset"); numberOfInvalidResults = byteResultp = byteResult = chunkResultp = 0; } //System.out.println("Samples read: " + samplesRead + " sampleAfterSamples: " + bitChunkLengthInSamples); abBufferDouble[0] = 1; baos.write(getBytesFromDoubles(abBufferDouble, samplesRead), 0, samplesRead * 2); } } catch (InterruptedException e) { } tdl.stop(); tdl.close(); writeWavFile(baos.toByteArray(), baos.size() / 2, "output.wav"); } |
download (424.4 kb)
About me
I'm Nonoo. This is my blog about music, sounds, filmmaking, amateur radio, computers, programming, electronics and other things I'm obsessed with.
... »
Trackback URL
No comments yet.
Trackback responses to this post