2 * Copyright (C) 2013-2015 Intel Corporation
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
29 #include "bat-signal.h"
31 static void check_amplitude(struct bat *bat, float *buf)
33 float sum, average, amplitude;
36 /* calculate average value */
37 for (i = 0, sum = 0.0, average = 0.0; i < bat->frames; i++)
39 average = sum / bat->frames;
41 /* calculate peak-to-average amplitude */
42 for (i = 0, sum = 0.0; i < bat->frames; i++)
43 sum += fabsf(buf[i] - average);
44 amplitude = sum / bat->frames * M_PI / 2.0;
46 /* calculate amplitude percentage against full range */
47 percent = amplitude * 100 / ((1 << ((bat->sample_size << 3) - 1)) - 1);
49 fprintf(bat->log, _("Amplitude: %.1f; Percentage: [%d]\n"),
52 fprintf(bat->err, _("ERROR: Amplitude can't be negative!\n"));
54 fprintf(bat->err, _("WARNING: Signal too weak!\n"));
55 else if (percent > 100)
56 fprintf(bat->err, _("WARNING: Signal overflow!\n"));
61 * @return 0 if peak detected at right frequency,
62 * 1 if peak detected somewhere else
65 int check_peak(struct bat *bat, struct analyze *a, int end, int peak, float hz,
66 float mean, float p, int channel, int start)
69 float hz_peak = (float) (peak) * hz;
70 float delta_rate = DELTA_RATE * bat->target_freq[channel];
71 float delta_HZ = DELTA_HZ;
72 float tolerance = (delta_rate > delta_HZ) ? delta_rate : delta_HZ;
74 fprintf(bat->log, _("Detected peak at %2.2f Hz of %2.2f dB\n"), hz_peak,
75 10.0 * log10f(a->mag[peak] / mean));
76 fprintf(bat->log, _(" Total %3.1f dB from %2.2f to %2.2f Hz\n"),
77 10.0 * log10f(p / mean), start * hz, end * hz);
79 if (hz_peak < DC_THRESHOLD) {
80 fprintf(bat->err, _(" WARNING: Found low peak %2.2f Hz,"),
82 fprintf(bat->err, _(" very close to DC\n"));
84 } else if (hz_peak < bat->target_freq[channel] - tolerance) {
85 fprintf(bat->err, _(" FAIL: Peak freq too low %2.2f Hz\n"),
87 err = FOUND_WRONG_PEAK;
88 } else if (hz_peak > bat->target_freq[channel] + tolerance) {
89 fprintf(bat->err, _(" FAIL: Peak freq too high %2.2f Hz\n"),
91 err = FOUND_WRONG_PEAK;
93 fprintf(bat->log, _(" PASS: Peak detected"));
94 fprintf(bat->log, _(" at target frequency\n"));
102 * Search for main frequencies in fft results and compare it to target
104 static int check(struct bat *bat, struct analyze *a, int channel)
106 float hz = 1.0 / ((float) bat->frames / (float) bat->rate);
107 float mean = 0.0, t, sigma = 0.0, p = 0.0;
108 int i, start = -1, end = -1, peak = 0, signals = 0;
109 int err = 0, N = bat->frames / 2;
112 for (i = 0; i < N; i++)
116 /* calculate standard deviation */
117 for (i = 0; i < N; i++) {
118 t = a->mag[i] - mean;
123 sigma = sqrtf(sigma);
125 /* clip any data less than k sigma + mean */
126 for (i = 0; i < N; i++) {
127 if (a->mag[i] > mean + bat->sigma_k * sigma) {
129 /* find peak start points */
131 start = peak = end = i;
134 if (a->mag[i] > a->mag[peak])
139 } else if (start != -1) {
140 /* Check if peak is as expected */
141 err |= check_peak(bat, a, end, peak, hz, mean,
144 if (signals == MAX_PEAKS)
149 err = -ENOPEAK; /* No peak detected */
150 else if ((err == FOUND_DC) && (signals == 1))
151 err = -EONLYDC; /* Only DC detected */
152 else if ((err & FOUND_WRONG_PEAK) == FOUND_WRONG_PEAK)
153 err = -EBADPEAK; /* Bad peak detected */
155 err = 0; /* Correct peak detected */
157 fprintf(bat->log, _("Detected at least %d signal(s) in total\n"),
163 static void calc_magnitude(struct bat *bat, struct analyze *a, int N)
168 for (i = 1; i < N / 2; i++) {
169 r2 = a->out[i] * a->out[i];
170 i2 = a->out[N - i] * a->out[N - i];
172 a->mag[i] = sqrtf(r2 + i2);
177 static int find_and_check_harmonics(struct bat *bat, struct analyze *a,
181 int err = -ENOMEM, N = bat->frames;
183 /* Allocate FFT buffers */
184 a->in = (float *) fftwf_malloc(sizeof(float) * bat->frames);
188 a->out = (float *) fftwf_malloc(sizeof(float) * bat->frames);
192 a->mag = (float *) fftwf_malloc(sizeof(float) * bat->frames);
196 /* create FFT plan */
197 p = fftwf_plan_r2r_1d(N, a->in, a->out, FFTW_R2HC,
198 FFTW_MEASURE | FFTW_PRESERVE_INPUT);
202 /* convert source PCM to floats */
203 bat->convert_sample_to_float(a->buf, a->in, bat->frames);
205 /* check amplitude */
206 check_amplitude(bat, a->in);
211 /* FFT out is real and imaginary numbers - calc magnitude for each */
212 calc_magnitude(bat, a, N);
215 err = check(bat, a, channel);
217 fftwf_destroy_plan(p);
229 static int calculate_noise_one_period(struct bat *bat,
230 struct noise_analyzer *na, float *src,
231 int length, int channel)
234 float tmp, rms, gain, residual;
235 float a = 0.0, b = 1.0;
237 /* step 1. phase compensation */
239 if (length < 2 * na->nsamples)
242 /* search for the beginning of a sine period */
243 for (i = 0, tmp = 0.0, shift = -1; i < na->nsamples; i++) {
244 /* find i where src[i] >= 0 && src[i+1] < 0 */
247 if (src[i + 1] < 0.0) {
248 tmp = src[i] - src[i + 1];
250 b = -src[i + 1] / tmp;
256 /* didn't find the beginning of a sine period */
260 /* shift sine waveform to source[0] = 0.0 */
261 for (i = 0; i < na->nsamples; i++)
262 na->source[i] = a * src[i + shift + 1] + b * src[i + shift];
264 /* step 2. gain compensation */
266 /* calculate rms of signal amplitude */
267 for (i = 0, tmp = 0.0; i < na->nsamples; i++)
268 tmp += na->source[i] * na->source[i];
269 rms = sqrtf(tmp / na->nsamples);
271 gain = na->rms_tgt / rms;
273 for (i = 0; i < na->nsamples; i++)
274 na->source[i] *= gain;
276 /* step 3. calculate snr in dB */
278 for (i = 0, tmp = 0.0, residual = 0.0; i < na->nsamples; i++) {
279 tmp = fabsf(na->target[i] - na->source[i]);
280 residual += tmp * tmp;
283 tmp = na->rms_tgt / sqrtf(residual / na->nsamples);
284 na->snr_db = 20.0 * log10f(tmp);
289 static int calculate_noise(struct bat *bat, float *src, int channel)
292 struct noise_analyzer na;
293 float freq = bat->target_freq[channel];
294 float tmp, sum_snr_pc, avg_snr_pc, avg_snr_db;
295 int offset, i, cnt_noise, cnt_clean;
296 /* num of samples in each sine period */
297 int nsamples = (int) ceilf(bat->rate / freq);
298 /* each section has 2 sine periods, the first one for locating
299 * and the second one for noise calculating */
300 int nsamples_per_section = nsamples * 2;
301 /* all sine periods will be calculated except the first one */
302 int nsection = bat->frames / nsamples - 1;
304 fprintf(bat->log, _("samples per period: %d\n"), nsamples);
305 fprintf(bat->log, _("total sections to detect: %d\n"), nsection);
306 na.source = (float *)malloc(sizeof(float) * nsamples);
312 na.target = (float *)malloc(sizeof(float) * nsamples);
318 /* generate standard single-tone signal */
319 err = generate_sine_wave_raw_mono(bat, na.target, freq, nsamples);
323 na.nsamples = nsamples;
325 /* calculate rms of standard signal */
326 for (i = 0, tmp = 0.0; i < nsamples; i++)
327 tmp += na.target[i] * na.target[i];
328 na.rms_tgt = sqrtf(tmp / nsamples);
330 /* calculate average noise level */
332 cnt_clean = cnt_noise = 0;
333 for (i = 0, offset = 0; i < nsection; i++) {
334 na.snr_db = SNR_DB_INVALID;
336 err = calculate_noise_one_period(bat, &na, src + offset,
337 nsamples_per_section, channel);
341 if (na.snr_db > bat->snr_thd_db) {
343 sum_snr_pc += 100.0 / powf(10.0, na.snr_db / 20.0);
351 fprintf(bat->err, _("Noise detected at %d points.\n"),
357 fprintf(bat->log, _("No noise detected.\n"));
360 avg_snr_pc = sum_snr_pc / cnt_clean;
361 avg_snr_db = 20.0 * log10f(100.0 / avg_snr_pc);
362 fprintf(bat->log, _("Average SNR is %.2f dB (%.2f %%) at %d points.\n"),
363 avg_snr_db, avg_snr_pc, cnt_clean);
373 static int find_and_check_noise(struct bat *bat, void *buf, int channel)
378 source = (float *)malloc(sizeof(float) * bat->frames);
382 /* convert source PCM to floats */
383 bat->convert_sample_to_float(buf, source, bat->frames);
385 /* adjust waveform and calculate noise */
386 err = calculate_noise(bat, source, channel);
393 * Convert interleaved samples from channels in samples from a single channel
395 static int reorder_data(struct bat *bat)
397 char *p, *new_bat_buf;
400 if (bat->channels == 1)
401 return 0; /* No need for reordering */
403 p = malloc(bat->frames * bat->frame_size);
408 for (ch = 0; ch < bat->channels; ch++) {
409 for (j = 0; j < bat->frames; j++) {
410 for (i = 0; i < bat->sample_size; i++) {
411 *p++ = ((char *) (bat->buf))[j * bat->frame_size
412 + ch * bat->sample_size + i];
418 bat->buf = new_bat_buf;
423 /* truncate sample frames for faster FFT analysis process */
424 static int truncate_frames(struct bat *bat)
426 int shift = SHIFT_MAX;
428 for (; shift > SHIFT_MIN; shift--)
429 if (bat->frames & (1 << shift)) {
430 bat->frames = 1 << shift;
437 int analyze_capture(struct bat *bat)
444 err = truncate_frames(bat);
446 fprintf(bat->err, _("Invalid frame number for analysis: %d\n"),
451 fprintf(bat->log, _("\nBAT analysis: signal has %d frames at %d Hz,"),
452 bat->frames, bat->rate);
453 fprintf(bat->log, _(" %d channels, %d bytes per sample.\n"),
454 bat->channels, bat->sample_size);
456 bat->buf = malloc(bat->frames * bat->frame_size);
457 if (bat->buf == NULL)
460 bat->fp = fopen(bat->capture.file, "rb");
462 if (bat->fp == NULL) {
463 fprintf(bat->err, _("Cannot open file: %s %d\n"),
464 bat->capture.file, err);
469 err = read_wav_header(bat, bat->capture.file, bat->fp, true);
473 items = fread(bat->buf, bat->frame_size, bat->frames, bat->fp);
474 if (items != bat->frames) {
479 err = reorder_data(bat);
483 for (c = 0; c < bat->channels; c++) {
484 fprintf(bat->log, _("\nChannel %i - "), c + 1);
485 fprintf(bat->log, _("Checking for target frequency %2.2f Hz\n"),
486 bat->target_freq[c]);
488 c * bat->frames * bat->frame_size
490 if (!bat->standalone) {
491 err = find_and_check_harmonics(bat, &a, c);
496 if (snr_is_valid(bat->snr_thd_db)) {
497 fprintf(bat->log, _("\nChecking for SNR: "));
498 fprintf(bat->log, _("Threshold is %.2f dB (%.2f%%)\n"),
499 bat->snr_thd_db, 100.0
500 / powf(10.0, bat->snr_thd_db / 20.0));
501 err = find_and_check_noise(bat, a.buf, c);