Extract specific frequency from Modulated signal using FFT - java

I've modulated a carrier frequency signal with my data using FSK like this:
double SAMPLING_TIME = 1.0 / 441000 // 44khz
int SAMPLES_PER_BIT = 136;
int ENCODING_SAMPLES_PER_BIT = SAMPLES_PER_BIT / 2;
int duration = ENCODING_SAMPLES_PER_BIT * SAMPLING_TIME;
public double[] encode(int[] bits) {
for (int i = 0; i < bits.length; i++) {
int freq = FREQUENCY_LOW;
if (bits[i] > 1)
freq = FREQUENCY_HIGH;
bitArray = generateTone(freq, duration);
message = bitArray;
}
return message;
}
private double[] generateTone(int frequency, double duration) {
int samplingRate = 1/SAMPLING_TIME; // Hz
int numberOfSamples = (int) (duration * samplingRate);
samplingTime = 2 * SAMPLING_TIME;
double[] tone = new double[numberOfSamples];
for (int i = 0; i < numberOfSamples; i++) {
double y = Math.sin(2 * Math.PI * frequency * i * SAMPLING_TIME);
tone[i] = y * CARRIER_AMPLITUDE;
}
return tone;
}
Clearly, I'm sending FREQUENCY_LOW for ZERO and FREQUENCY_HIGH for 1.
Now how do I demodulate it using FFT? I'm interested in sampling magnitudes (presence and absence) of FREQUENCY_LOW, FREQUENCY_HIGH throughout the time.
I only know basics of FFT, I was starting to write this but it doesn't make sense:
private void decode(byte[] tone, int length) {
float[] input = new float[FFT_SIZE*2]; // not sure what size? shouldn't this be buffer?
for(int i=0;i<length;i++){
input[i]=tone[i];
}
FloatFFT_1D fft = new FloatFFT_1D(FFT_SIZE);
fft.realForward(input);
}
Can someone help with code?

You can use overlapping sliding windows for your FFTs, with the window and FFT the same length as that of your data bits. Then look for magnitude peaks for your 1's and 0's in the appropriate FFT result bins across these windows. You will also need some synchronization logic for runs of 1's and 0's.
Another DSP techniques that may be less compute intensive is to do quadrature demodulation for your two frequencies and low-pass filter the result before feeding it to the synchronization logic and bit detector. Yet another possibility is two sliding Goertzel filters.

Related

Problems with formula to output a triangle waveform in Java

I got my wave file creator working and I split it into three classes, I also made a sinewave generator that inherits from an abstract class called waveform and I can export 8 and 16 bit mono or sterio sine waves. I am trying to make a class called TriangleWave Generator to output a triangle wave tone, but I can't get the algebra from https://en.wikipedia.org/wiki/Triangle_wave#, the first formula, to work. It will only export the highest harmonic stated and not blend them together with the fundamental
Sample length: length in seconds
point: individual sample
amp limit: the highest position possible
harmonics: the number of harmonics to use to make the waveform 1 = fundamental, 2 = 1st overtone, 3 = 2nd overtone.......
frequency: fundamental frequency (Middle C = 261.63)
sample rate = 44100; (CD quality)
triangle Samples array: sample data
This is my code
public class TriangleGenerator extends Waveform {
// constants
public static final int HARMONIC_COUNT = 16;
// instance variabls
int harmonics;
int[] triangleSample;
int addCount;
// constructor
public TriangleGenerator(double amplitude, double frequency, int bitRate, double duration, int harmonics) {
super(amplitude, frequency, bitRate, duration);
// sample data
triangleSample = new int[sampleLength];
calculateAmpLimit();
this.harmonics = harmonics;
}
// one arg cunstructor
public TriangleGenerator(double frequency) {
this(AMPLITUDE, frequency, BIT_RATE, DURATION, HARMONIC_COUNT);
}
// no args constructor
public TriangleGenerator() {
this(AMPLITUDE, FREQUENCY, BIT_RATE, DURATION, HARMONIC_COUNT);
}
#Override
public int[] generateWaveForm() {
// generate the actual waveform
for (int i = 0; i < sampleLength; i++) {
point = (int)(ampLimit * ((8 / Math.pow(Math.PI, 2)) * sumnate(harmonics - 1, Math.pow(-1, addCount))
* Math.pow(harmonics, -2) * Math.sin(2 * Math.PI * frequency * harmonics * i / SAMPLE_RATE)));
triangleSample[i] = point;
}
// return the sample data
return triangleSample;
}
public double sumnate(int n, double adder) {
double sum = 0;
for (addCount = 0; addCount <= n; addCount++) {
sum += adder;
}
return sum;
}
}
In the formula for the triangle wave:
the mode number n is dependent on the harmonic label i:
This means that it must also be summed over the components
which doesn't happen in the current implementation. One possible implementation is:
public int[] generateWaveForm() {
for (int t = 0; t < sampleLength; t++) {
triangleSample[t] = (int)(ampLimit * 8.0 / Math.pow(Math.PI, 2.0) * getDataPoint(t, N));
}
return triangleSample;
}
private double getDataPoint(int t, int N) {
double sum = 0;
for (int i = 0; i <= N - 1; i++) {
sum += getHarmonicShare(t, i);
}
return sum;
}
private double getHarmonicShare(int t, int i) {
double n = 2.0 * i + 1.0;
return Math.pow(-1.0, i) * Math.pow(n, -2.0) * Math.sin(2.0 * Math.PI * frequency * (t / SAMPLE_RATE) * n);
}
Here t, i, n and N correspond to the values from the formula. frequency denotes the frequency. The remaining values correspond to the parameters of the posted code.
The curve reaches up to the value sampleLength / SAMPLE_RATE. The period is 1 / frequency and there are (sampleLength / SAMPLE_RATE) * frequency periods in the displayed frame.
Example:
sampleLength: 500
ampLimit: 100.00
SAMPLE_RATE: 44100.00
frequency: 261.63
sampleLength / SAMPLE_RATE: 0.0113378685
1 / frequency: 0.0038221916
(sampleLength / SAMPLE_RATE) * frequency: 2.9663265306
In the following the corresponding curve is shown for different N using JFreeChart:
Is there a reason you want to use sin to do this rather than produce the straight lines directly by a linear formula? There is an advantage in using sin in that you don't get harmonic distortion from aliasing, but if that's an issue then you can oversample. The issue is that sin is much slower than basic arithmetic.

Get frequency from Android mic

I know that this question was asked, but it has no distinct answer.
So, what I've found is some example here : FFT spectrum analysis
Where I can transform my array of doubles with FFT class
RealDoubleFFT transformer;
int blockSize= */2048;
short[] buffer = new short[blockSize];
double[] toTransform = new double[blockSize];
bufferReadResult = audioRecord.read(buffer, 0, blockSize);
for (int i = 0; i < blockSize && i < bufferReadResult; i++) {
toTransform[i] = (double) buffer[i] / 32768.0; // signed 16 bit
}
transformer.ft(toTransform);
so now I don't know how to get a frequency
I wrote such method :
public static int calculateFFTFrequency(double[] audioData){
float sampleRate = 44100;
int numSamples = audioData.length;
double max = Double.MIN_VALUE;
int index = 0;
for (int i = 0; i< numSamples -1; i++){
if (audioData[i] > max) {
max = audioData[i];
index = i;
}
}
float freq = (sampleRate / (float) numSamples * (float) index) * 2F;
return (int)freq;
}
I try to implement a formula, but it doesn't return me anything good - some wild numbers
I tried zero passing as well :
public static int calculateFrequency(short [] audioData){
int sampleRate = 44100;
int numSamples = audioData.length;
int numCrossing = 0;
for (int p = 0; p < numSamples-1; p++)
{
if ((audioData[p] > 0 && audioData[p + 1] <= 0) ||
(audioData[p] < 0 && audioData[p + 1] >= 0))
{
numCrossing++;
}
}
float numSecondsRecorded = (float)numSamples/(float)sampleRate;
float numCycles = numCrossing/2;
float frequency = numCycles/numSecondsRecorded;
return (int)frequency;
}
But in zero passing method if I play "A" note on piano it shows me 430 for a moment (which is close to A) and then start to show some wild numbers when the sound fades - 800+ , 1000+ , etc.
Can somebody help me how to get more or less actual frequency from the mic?
You should test your solution using a generated stream rather than a mic, then testing if the frequency detected is what you expect. Then you can do real life tests with mic, you should analyze the data collected by mic by yourself in case of any issues. There could be non audible sounds in your environment that could cause some strange results. When the sound fades there could be some harmonical sounds and these harmonicals can become lauder than the base sound. There's a lot of things to be considered when processing sounds from real environment.
What you hear from a piano is a pitch, not just a spectral frequency. They are not the same thing. Pitch is a psycho-acoustic phenomena, depending more on periodicity, not just the spectral peak. A bare FFT reports spectral frequency magnitudes, which can be composed of overtones, harmonics, and other artifacts, and may or may not include the fundamental pitch frequency.
So what you may want to use instead of an FFT is a pitch detection/estimation algorithm, which is a bit more complicated than just picking a peak magnitude out of an FFT.

Frequency with fft.complexForward

I want to calculate the fundamental frequency with autocorrelation and I found this code:
public double calculateFFT(double[] signal)
{
final int mNumberOfFFTPoints =1024;
double[] magnitude = new double[mNumberOfFFTPoints/2];
DoubleFFT_1D fft = new DoubleFFT_1D(mNumberOfFFTPoints);
double[] fftData = new double[mNumberOfFFTPoints*2];
double max_index=-1;
double max_magnitude=-1;
final float sampleRate=44100;
double frequency;
for (int i=0;i<mNumberOfFFTPoints;i++){
fftData[2 * i] = signal[i];
fftData[2 * i + 1] = 0;
fft.complexForward(fftData);
}
for(int i = 0; i < mNumberOfFFTPoints/2; i++){
magnitude[i]=Math.sqrt((fftData[2*i] * fftData[2*i]) + (fftData[2*i + 1] * fftData[2*i + 1]));
if (max_magnitude<magnitude[i]){
max_magnitude=magnitude[i];
max_index=i;
}
}
return frequency=sampleRate*(double)max_index/(double)mNumberOfFFTPoints;
}
I analyzed that fftData before "fft.complexForward(fftData);" contains some values, but after this operation ("fft.complexForward(fftData)") contains NaN.
Why?
Can someone help me, please?
Thanks
Outputs of FFT are complex numbers. The method complex forward calculates the FFT of the fftData and puts it back in fftData. The even indices are real parts of the FFT while the odd indices are the imaginary parts. Since this code is calculating the magnitudes in the end, there's no need for a for loop. So instead of,
for (int i=0;i<mNumberOfFFTPoints;i++){
fftData[2 * i] = signal[i];
fftData[2 * i + 1] = 0;
fft.complexForward(fftData);
}
Just write :
fft.complexForward(fftData);

Using gdx Library and FFT to Calculate Frequency (Java)

I am currently using the gdx library com.badlogic.gdx.audio.analysis.FFT and the method:
private float[] fft(int N, int fs, float[] array) {
float[] fft_cpx, tmpr, tmpi;
float[] res = new float[N / 2];
// float[] mod_spec =new float[array.length/2];
float[] real_mod = new float[N];
float[] imag_mod = new float[N];
double[] real = new double[N];
double[] imag = new double[N];
double[] mag = new double[N];
double[] phase = new double[N];
float[] new_array = new float[N];
// Zero Pad signal
for (int i = 0; i < N; i++) {
if (i < array.length) {
new_array[i] = array[i];
}
else {
new_array[i] = 0;
}
}
FFT fft = new FFT(N, 8000);
fft.forward(new_array);
fft_cpx = fft.getSpectrum();
tmpi = fft.getImaginaryPart();
tmpr = fft.getRealPart();
for (int i = 0; i < new_array.length; i++) {
real[i] = (double) tmpr[i];
imag[i] = (double) tmpi[i];
mag[i] = Math.sqrt((real[i] * real[i]) + (imag[i] * imag[i]));
phase[i] = Math.atan2(imag[i], real[i]);
/**** Reconstruction ****/
real_mod[i] = (float) (mag[i] * Math.cos(phase[i]));
imag_mod[i] = (float) (mag[i] * Math.sin(phase[i]));
}
fft.inverse(real_mod, imag_mod, res);
return res;
}
How then do I use this method to find the frequency (and then note) of sound recorded from the microphone?
Your goal is to take all magnitudes of individual frequencies in mag[i] and to find the largest one. For start, you can just loop over them and find the maximum mag[i]. Then you have to recalculate it's corresponding frequency from i index.
Frequency is determined by this equation:
freq = i * Fs / N;
Where Fs is sampling frequency of your time domain data (input wave data), N - number of samples you did compute FFT from. i is the index of your frequency domain data (computed magnitudes and phases)
In your case you can add line like into your for cycle to debug it:
double freq = (double)i*(double)fs/(double)N;
System.out.println("Frequency: "+ Double.toString(freq) + "Magnitude: "+ Double.toString(mag[i]));
Check this link for more information:
How to get frequency from fft result?
Nyquist theorem
... states that you can perfectly reconstruct frequencies only if you have twice the samples.... for reconstructing 1000Hz, you have to have at least 2000 samples per second. (Still this wave will be very distorted.).
If you have samplerate of 22000Hz, you would be able to somehow measure frequencies up to 11000Hz. Your data in mag and phase will be meaningful to the first half of array 0..N/2, then, you'll see just a mirror image of previous data (see the link to wikipedia page for a picture.)
If you want to determine your N check this answer or google more. Try to start with arbitrary numbers like one tenth of samplerate fs. The larger N, the slower will be your algorithm.
Table of note frequencies
Simplest way is to make a table of all frequencies you will detect and then just compare your frequency with maximum magnitude to all frequencie values in table. With a small tolerance, for example +-2% of the value in table. Be sure those tolerances do not overlap for two consecutive notes.
Microphone input
Google up keywords like java microphone input library tutorial, or check this answer.

Android audio FFT to retrieve specific frequency magnitude using audiorecord

I am currently trying to implement some code using Android to detect when a number of specific audio frequency ranges are played through the phone's microphone. I have set up the class using the AudioRecord class:
int channel_config = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int format = AudioFormat.ENCODING_PCM_16BIT;
int sampleSize = 8000;
int bufferSize = AudioRecord.getMinBufferSize(sampleSize, channel_config, format);
AudioRecord audioInput = new AudioRecord(AudioSource.MIC, sampleSize, channel_config, format, bufferSize);
The audio is then read in:
short[] audioBuffer = new short[bufferSize];
audioInput.startRecording();
audioInput.read(audioBuffer, 0, bufferSize);
Performing an FFT is where I become stuck, as I have very little experience in this area. I have been trying to use this class:
FFT in Java and Complex class to go with it
I am then sending the following values:
Complex[] fftTempArray = new Complex[bufferSize];
for (int i=0; i<bufferSize; i++)
{
fftTempArray[i] = new Complex(audio[i], 0);
}
Complex[] fftArray = fft(fftTempArray);
This could easily be me misunderstanding how this class is meant to work, but the values returned jump all over the place and aren't representative of a consistent frequency even in silence. Is anyone aware of a way to perform this task, or am I overcomplicating matters to try and grab only a small number of frequency ranges rather than to draw it as a graphical representation?
First you need to ensure that the result you are getting is correctly converted to a float/double. I'm not sure how the short[] version works, but the byte[] version only returns the raw byte version. This byte array then needs to be properly converted to a floating point number. The code for the conversion should look something like this:
double[] micBufferData = new double[<insert-proper-size>];
final int bytesPerSample = 2; // As it is 16bit PCM
final double amplification = 100.0; // choose a number as you like
for (int index = 0, floatIndex = 0; index < bytesRecorded - bytesPerSample + 1; index += bytesPerSample, floatIndex++) {
double sample = 0;
for (int b = 0; b < bytesPerSample; b++) {
int v = bufferData[index + b];
if (b < bytesPerSample - 1 || bytesPerSample == 1) {
v &= 0xFF;
}
sample += v << (b * 8);
}
double sample32 = amplification * (sample / 32768.0);
micBufferData[floatIndex] = sample32;
}
Then you use micBufferData[] to create your input complex array.
Once you get the results, use the magnitudes of the complex numbers in the results. Most of the magnitudes should be close to zero except the frequencies that have actual values.
You need the sampling frequency to convert the array indices to such magnitudes to frequencies:
private double ComputeFrequency(int arrayIndex) {
return ((1.0 * sampleRate) / (1.0 * fftOutWindowSize)) * arrayIndex;
}

Categories

Resources