The result of audio processing using the Goertzel algorithm

I made a small signal processing application. It processes the audio signal (Morse code) at a specific frequency using the Goerztel algorithm. The application saves the temporary file to the file system and, after recording is complete, begins to detect signals. Now I got a result with many quantities.

I do not know what to read from these quantities. How can I decipher the morse code from these values? How can I read them? I tried to find links, but nowhere is it explained what is the result and how to read it.

EDIT:

My morse code application runs with Delphi and uses the Windows Beep function to send signals at a specific frequency. I use 1200 Hz for signals. Also, pauses between signals and words and Morse sounds are similar to Wikipedia descriptions. All right.

Goertzel.java:

public class Goertzel { private float samplingRate; private float targetFrequency; private int n; private double coeff, Q1, Q2; private double sine, cosine; public Goertzel(float samplingRate, float targetFrequency, int inN) { this.samplingRate = samplingRate; this.targetFrequency = targetFrequency; n = inN; sine = Math.sin(2 * Math.PI * (targetFrequency / samplingRate)); cosine = Math.cos(2 * Math.PI * (targetFrequency / samplingRate)); coeff = 2 * cosine; } public void resetGoertzel() { Q1 = 0; Q2 = 0; } public void initGoertzel() { int k; float floatN; double omega; floatN = (float) n; k = (int) (0.5 + ((floatN * targetFrequency) / samplingRate)); omega = (2.0 * Math.PI * k) / floatN; sine = Math.sin(omega); cosine = Math.cos(omega); coeff = 2.0 * cosine; resetGoertzel(); } public void processSample(double sample) { double Q0; Q0 = coeff * Q1 - Q2 + sample; Q2 = Q1; Q1 = Q0; } public double[] getRealImag(double[] parts) { parts[0] = (Q1 - Q2 * cosine); parts[1] = (Q2 * sine); return parts; } public double getMagnitudeSquared() { return (Q1 * Q1 + Q2 * Q2 - Q1 * Q2 * coeff); } } 

SoundCompareActivity.java

 import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.app.Activity; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class SoundCompareActivity extends Activity { private static final int RECORDER_SAMPLE_RATE = 8000; // at least 2 times // higher than sound // frequency, private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_CONFIGURATION_MONO; private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT; private AudioRecord recorder = null; private int bufferSize = 0; private Thread recordingThread = null; private boolean isRecording = false; private Button startRecBtn; private Button stopRecBtn; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startRecBtn = (Button) findViewById(R.id.button1); stopRecBtn = (Button) findViewById(R.id.button2); startRecBtn.setEnabled(true); stopRecBtn.setEnabled(false); bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLE_RATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING); startRecBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d("SOUNDCOMPARE", "Start Recording"); startRecBtn.setEnabled(false); stopRecBtn.setEnabled(true); stopRecBtn.requestFocus(); startRecording(); } }); stopRecBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d("SOUNDCOMPARE", "Stop recording"); startRecBtn.setEnabled(true); stopRecBtn.setEnabled(false); startRecBtn.requestFocus(); stopRecording(); } }); } private void startRecording() { recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, RECORDER_SAMPLE_RATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSize); recorder.startRecording(); isRecording = true; recordingThread = new Thread(new Runnable() { @Override public void run() { writeAudioDataToTempFile(); } }, "AudioRecorder Thread"); recordingThread.start(); } private String getTempFilename() { File file = new File(getFilesDir(), "tempaudio"); if (!file.exists()) { file.mkdirs(); } File tempFile = new File(getFilesDir(), "signal.raw"); if (tempFile.exists()) tempFile.delete(); return (file.getAbsolutePath() + "/" + "signal.raw"); } private void writeAudioDataToTempFile() { byte data[] = new byte[bufferSize]; String filename = getTempFilename(); FileOutputStream os = null; try { os = new FileOutputStream(filename); } catch (FileNotFoundException e) { e.printStackTrace(); } int read = 0; if (os != null) { while (isRecording) { read = recorder.read(data, 0, bufferSize); if (read != AudioRecord.ERROR_INVALID_OPERATION) { try { os.write(data); } catch (IOException e) { e.printStackTrace(); } } } try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } private void deleteTempFile() { File file = new File(getTempFilename()); file.delete(); } private void stopRecording() { if (recorder != null) { isRecording = false; recorder.stop(); recorder.release(); recorder = null; recordingThread = null; } new MorseDecoder().execute(new File(getTempFilename())); } } 

MorseDecoder.java:

 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ShortBuffer; import android.media.AudioFormat; import android.media.AudioRecord; import android.os.AsyncTask; import android.util.Log; public class MorseDecoder extends AsyncTask<File, Void, Void> { private FileInputStream is = null; @Override protected Void doInBackground(File... files) { int index; //double magnitudeSquared; double magnitude; int bufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); Goertzel g = new Goertzel(8000, 1200, bufferSize); g.initGoertzel(); for (int i = 0; i < files.length; i++) { byte[] data = new byte[bufferSize]; try { is = new FileInputStream(files[i]); while(is.read(data) != -1) { ShortBuffer sbuf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); short[] audioShorts = new short[sbuf.capacity()]; sbuf.get(audioShorts); float[] audioFloats = new float[audioShorts.length]; for (int j = 0; j < audioShorts.length; j++) { audioFloats[j] = ((float)audioShorts[j]) / 0x8000; } for (index = 0; index < audioFloats.length; index++) { g.processSample(data[index]); } magnitude = Math.sqrt(g.getMagnitudeSquared()); Log.d("SoundCompare", "Relative magnitude = " + magnitude); g.resetGoertzel(); } is.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return null; } } 

EDIT2:

Notifications of errors in sample processing. Changed the code in the while loop.

 while(is.read(data) != -1) { ShortBuffer sbuf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); short[] audioShorts = new short[sbuf.capacity()]; sbuf.get(audioShorts); float[] audioFloats = new float[audioShorts.length]; for (int j = 0; j < audioShorts.length; j++) { audioFloats[j] = ((float)audioShorts[j]) / 0x8000; } for (index = 0; index < audioFloats.length; index++) { g.processSample(audioFloats[index]); magnitude = Math.sqrt(g.getMagnitudeSquared()); Log.d("SoundCompare", "Relative magnitude = " + magnitude); } //magnitude = Math.sqrt(g.getMagnitudeSquared()); //Log.d("SoundCompare", "Relative magnitude = " + magnitude); g.resetGoertzel(); } 

Regards, evilone

+4
source share
2 answers

The output of your Goertzel filter will increase if there is a tone in its passband, and then decrease when the tone is removed. To detect tone pulses, for example. Morse code, you need some kind of threshold detector at the filter output, which simply gives a logical value for the “presence tone” / “tone not present” in the sample. Try building output values, and this should be obvious if you see it in graphical form.

+7
source

Build the signal values ​​on the graph versus time (some applications for decoding CW for PC do this in real time). Now figure out what the graph should look like for each character in the Morse code. Then learn some pattern matching algorithms. If there is sufficient noise, you can try some statistical pattern matching methods.

Here is the Wikipedia link for the correct definition of Morse time .

0
source

All Articles