Project import
diff --git a/freq_analyzer-2.4.2/Makefile b/freq_analyzer-2.4.2/Makefile new file mode 100644 index 0000000..497c2e1 --- /dev/null +++ b/freq_analyzer-2.4.2/Makefile
@@ -0,0 +1,29 @@ +# Copyright (c) 2010-2015 Nest, Inc. +# All rights reserved. +# +# This document is the property of Nest. It is considered +# confidential and proprietary information. +# +# This document may not be reproduced or transmitted in any form, +# in whole or in part, without the express written permission of +# Nest. +# +# Description: +# This file is a make file for the frequency analyzer. +# + +include pre.mak + +TpsDir = sw/tps +fftwResultsDir = $(TpsDir)/fftw +fftwIncludeDir = usr/include +fftwLibraryDir = usr/lib +fftwNames = fftw3 +fftwIncludePath = $(call GenerateResultPaths,$(fftwResultsDir),$(fftwIncludeDir)) +fftwLibraryPaths = $(call GenerateResultPaths,$(fftwResultsDir),$(addprefix $(fftwLibraryDir)/,$(fftwNames))) +PROGRAMS = freq_analyzer +freq_analyzer_SOURCES = freq_analyzer.c +freq_analyzer_INCLUDES = $(fftwIncludePath) +freq_analyzer_LDLIBS = $(fftwLibraryPaths) m + +include post.mak
diff --git a/freq_analyzer-2.4.2/freq_analyzer.c b/freq_analyzer-2.4.2/freq_analyzer.c new file mode 100644 index 0000000..a7b1e56 --- /dev/null +++ b/freq_analyzer-2.4.2/freq_analyzer.c
@@ -0,0 +1,315 @@ +/* + * Code for frequency analysis + * + * Copyright (C) 2015 Nest Labs. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <math.h> +#include "fftw3.h" +#include "wav_header.h" + +#define SAMPLE_BYTES 2 +#define MAX_NUM_CHANNELS 2 + +#define RECTANGULAR 1 +#define HAMMING 2 +#define BLACKMAN_NUTTALL 3 +#define HANNING 4 +#define BLACKMAN_HARRIS 5 +#define WINDOW_TYPE HANNING + +#define SQUARE(a) ( (a) * (a) ) + +void apply_window(int16_t *input, double *windowed, int32_t num_samples); + +#if WINDOW_TYPE == RECTANGULAR +void apply_window(int16_t *input, double *windowed, int32_t num_samples) +{ + int32_t sample_index; + for(sample_index = 0; sample_index < num_samples; sample_index++) + windowed[sample_index] = (double)input[sample_index]/32767.0; +} +#elif WINDOW_TYPE == HAMMING +void apply_window(int16_t *input, double *windowed, int32_t num_samples) +{ + int32_t sample_index; + for(sample_index = 0; sample_index < num_samples; sample_index++) { + windowed[sample_index] = 0.54 - 0.46 * cos(2 * M_PI * sample_index/(num_samples -1)); + windowed[sample_index] *= (double)input[sample_index]/32767.0; + } +} +#elif WINDOW_TYPE == BLACKMAN_NUTTALL +void apply_window(int16_t *input, double *windowed, int32_t num_samples) +{ + int32_t sample_index; + for(sample_index = 0; sample_index < num_samples; sample_index++) { + windowed[sample_index] = 0.3635819; + windowed[sample_index] -= 0.4891775 * cos(2 * M_PI * sample_index/(num_samples -1)); + windowed[sample_index] += 0.1365995 * cos(4 * M_PI * sample_index/(num_samples -1)); + windowed[sample_index] -= 0.0106411 * cos(6 * M_PI * sample_index/(num_samples -1)); + windowed[sample_index] *= (double)input[sample_index]/32767.0; + } +} +#elif WINDOW_TYPE == HANNING +void apply_window(int16_t *input, double *windowed, int32_t num_samples) +{ + int32_t sample_index; + for(sample_index = 0; sample_index < num_samples; sample_index++) { + windowed[sample_index] = 0.5 - 0.5 * cos(2 * M_PI * sample_index/(num_samples -1)); + windowed[sample_index] *= (double)input[sample_index]/32767.0; + } +} +#elif WINDOW_TYPE == BLACKMAN_HARRIS +void apply_window(int16_t *input, double *windowed, int32_t num_samples) +{ + int32_t sample_index; + for(sample_index = 0; sample_index < num_samples; sample_index++) { + windowed[sample_index] = 0.35875; + windowed[sample_index] -= 0.48829 * cos(2 * M_PI * sample_index/(num_samples -1)); + windowed[sample_index] += 0.14128 * cos(4 * M_PI * sample_index/(num_samples -1)); + windowed[sample_index] -= 0.01168 * cos(6 * M_PI * sample_index/(num_samples -1)); + windowed[sample_index] *= (double)input[sample_index]/32767.0; + } +} +#endif + +typedef struct { + double *data_r; + double *mag; + fftw_complex *data_c; +} scratch_t; + +typedef struct { + double max_freq; + double max; +} output_t; + +void execute_fft(int16_t * input_buffer, + int32_t nfft, + int32_t offset, + int32_t num_input_samples, + int32_t sample_rate, + scratch_t *scratch, + output_t *fft_output) +{ + fftw_plan p; + int32_t max_freq_index, sample_index, block_index; + double *data_r = scratch->data_r; + double *mag = scratch->mag; + double max = -32768.0; /* Initialize to a very low dB */ + fftw_complex *data_c = scratch->data_c; + + /* This array will contain the average over multiple blocks*/ + for(sample_index = 0; sample_index < nfft/2+1; sample_index++) { + mag[sample_index] = 0; + } + /* Setup the plan to compute FFT */ + p = fftw_plan_dft_r2c_1d(nfft, data_r, data_c, FFTW_ESTIMATE); + /* Execute FFT */ + for(block_index = 0; block_index * offset + nfft < num_input_samples; block_index++) { + /* Windowing */ + apply_window(input_buffer + block_index * offset, data_r, nfft); + /* FFT */ + fftw_execute(p); + /* Accumulate for averaging. nfft is the normalization factor to + * account for the difference in forward and reverse DFT + */ + for(sample_index = 0; sample_index < nfft/2+1; sample_index++) { + mag[sample_index] += SQUARE(data_c[sample_index][0]/nfft) + SQUARE(data_c[sample_index][1]/nfft); + } + } + /* Find the tone with the highest frequency, ignore the DC bin */ + for(sample_index = 1; sample_index < nfft/2+1; sample_index++) { + mag[sample_index] = 10 * log10(mag[sample_index]/block_index); + if (mag[sample_index] > max) { + max = mag[sample_index]; + max_freq_index = sample_index; + } + } + fft_output->max = max; + fft_output->max_freq = (float)max_freq_index/nfft * sample_rate; + printf("Number of blocks for FFT %d\n", block_index); +#if 0 + /* Print the final output */ + for(sample_index = 0; sample_index < nfft/2+1; sample_index++) { + mag[sample_index] = 10 * log10(mag[sample_index]/block_index); + printf("%d: %lf(dB)\n", sample_index, mag[sample_index]); + } +#endif + fftw_destroy_plan(p); +} + +int check_boundary(output_t *fft_output, double input_freq, double freq_dev, + double input_vol, double vol_dev) +{ + double lower_limit, upper_limit; + + /* check if freq is within deviation */ + lower_limit = input_freq * (1 - freq_dev/100); + upper_limit = input_freq * (1 + freq_dev/100); + if ( (fft_output->max_freq < lower_limit) || (fft_output->max_freq > upper_limit) ) + return -1; + + /* Check if volume is within deviation */ + lower_limit = input_vol * (1 - vol_dev/100); + lower_limit = 20 * log10(lower_limit); + upper_limit = input_vol * (1 + vol_dev/100); + upper_limit = 20 * log10(upper_limit); + if ( (fft_output->max < lower_limit) || (fft_output->max > upper_limit) ) + return -1; + + return 0; +} + +int main(int argc, char **argv) +{ + FILE *fin = NULL; + int16_t *input_buffer_i = NULL; /* contains interleaved data */ + int16_t *input_buffer_n = NULL; /* contains one channel of non-interleaved data */ + wave_header_t hdr; + int ret = 0; + int32_t nfft, offset, channel_index, num_input_samples, sample_index; + scratch_t scratch = {NULL, NULL, NULL}; + output_t fft_output; + double input_freq[MAX_NUM_CHANNELS], freq_dev[MAX_NUM_CHANNELS]; + double input_vol[MAX_NUM_CHANNELS], vol_dev[MAX_NUM_CHANNELS]; + + /* Verify all required arguments are present */ + if (argc < 8) { + printf("Usage: freq_analyzer <num FFT points> <overlap percent> <input file> <input freq> <%% freq deviation> <volume (dB)> <\%% volume deviation>\n"); + printf(" channel 2: <input freq> <%% freq deviation> <volume (dB)> <\%% volume deviation>\n"); + printf(" ........... \n"); + ret = -1; + goto _done; + } + /* Number of FFT points */ + nfft = atoi(argv[1]); + /* overlap */ + offset = ( (100 - atoi(argv[2])) * nfft) / 100; + /* Read the samples from the input file into an array */ + fin = fopen(argv[3], "rb"); + if (!fin) { + printf("Unable to open input file %s\n", argv[3]); + ret = -1; + goto _done; + } + /* input frequency */ + input_freq[0] = atof(argv[4]); + /* Deviation in input frequency */ + freq_dev[0] = atof(argv[5]); + /* Input volume */ + input_vol[0] = pow(10,atof(argv[6])/20); + /* Deviation from input volume */ + vol_dev[0] = atof(argv[7]); + /* Read the header & input samples */ + fread(&hdr, sizeof(wave_header_t), 1, fin); + if (hdr.num_channels > MAX_NUM_CHANNELS) { + printf("Only upto stereo supported\n"); + ret = -1; + goto _done; + } + if ((hdr.num_channels == 2) && (argc < 12) ) { + printf("Not enough arguments\n"); + ret = -1; + goto _done; + } + if (hdr.num_channels == 2) { + /* input frequency */ + input_freq[1] = atof(argv[8]); + /* Deviation in input frequency */ + freq_dev[1] = atof(argv[9]); + /* Input volume */ + input_vol[1] = pow(10,atof(argv[10])/20); + /* Deviation from input volume */ + vol_dev[1] = atof(argv[11]); + } + num_input_samples = hdr.subchunk2_size/(hdr.num_channels * SAMPLE_BYTES); + input_buffer_i = malloc(hdr.subchunk2_size); + if (NULL == input_buffer_i) { + printf("Unable to allocate space for input buffer\n"); + ret = -1; + goto _done; + } + fread(input_buffer_i, 1, hdr.subchunk2_size, fin); + /* Make necessary memory allocations */ + scratch.data_r = fftw_alloc_real(nfft); + if (NULL == scratch.data_r) { + printf("Unable to allocate space for input buffer\n"); + ret = -1; + goto _done; + } + scratch.data_c = fftw_alloc_complex(nfft/2 + 1); + if (NULL == scratch.data_c) { + printf("Unable to allocate space for output buffer\n"); + ret = -1; + goto _done; + } + scratch.mag = malloc(sizeof(double) * (nfft/2 + 1)); + if (NULL == scratch.mag) { + printf("Unable to allocate space for output averaging buffer\n"); + ret = -1; + goto _done; + } + + /* FFT */ + if (1 == hdr.num_channels) { + printf("Channel 0: "); + /* Mono */ + execute_fft(input_buffer_i, nfft, offset, num_input_samples, hdr.sample_rate, &scratch, &fft_output); + /* Print the output data */ + printf("%f (hz): %lf(dB)\n", fft_output.max_freq, fft_output.max); + if (check_boundary(&fft_output, input_freq[0], freq_dev[0], input_vol[0], vol_dev[0]) == 0) + printf("PASS\n"); + else + printf("FAIL\n"); + } + else { + /* Processing for more than 1 channel */ + input_buffer_n = malloc(num_input_samples * SAMPLE_BYTES); + if (NULL == input_buffer_n) { + printf("Unable to allocate space for deinterleaving buffer\n"); + ret = -1; + goto _done; + } + for(channel_index = 0; channel_index < hdr.num_channels; channel_index++) { + /* Deinterleave and fft for one channel at a time */ + for(sample_index = 0; sample_index < num_input_samples; sample_index++) + input_buffer_n[sample_index] = input_buffer_i[hdr.num_channels * sample_index + channel_index]; + /* FFT */ + printf("Channel %d: ", channel_index); + execute_fft(input_buffer_n, nfft, offset, num_input_samples, hdr.sample_rate, &scratch, &fft_output); + /* Print the output data */ + printf("%f (hz): %lf(dB)\n", fft_output.max_freq, fft_output.max); + if (check_boundary(&fft_output, input_freq[channel_index], freq_dev[channel_index], input_vol[channel_index], vol_dev[channel_index]) == 0) + printf("PASS\n"); + else + printf("FAIL\n"); + } + } +_done: + if (fin) fclose(fin); + if (input_buffer_i) free(input_buffer_i); + if (input_buffer_n) free(input_buffer_n); + if (scratch.data_r) fftw_free(scratch.data_r); + if (scratch.data_c) fftw_free(scratch.data_c); + if (scratch.mag) free(scratch.mag); + return ret; +}