/**
* dsp: various digital signal processing algorithms
* <br>Copyright 2009 Ian Cameron Smith
*
* <p>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 (see COPYING).
*
* <p>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.
*/
package pl.llp.aircasting.sensor.builtin;
/**
* A power metering algorithm.
*/
public class SignalPower {
double MIN_DB = -100;
double MAX_DB = 0;
public Double calculatePowerDb(short[] data) {
double power = calculatePowerDb(data, 0, data.length);
if (power < MAX_DB && power > MIN_DB) {
return power;
} else {
// Faulty data
return null;
}
}
/**
* Calculate the power of the given input signal.
*
* @param sdata Buffer containing the input samples to process.
* @param off Offset in sdata of the data of interest.
* @param samples Number of data samples to process.
* @return The calculated power in dB relative to the maximum
* input level; hence 0dB represents maximum power,
* and minimum power is about -95dB. Particular
* cases of interest:
* <ul>
* <li>A non-clipping full-range sine wave input is
* about -2.41dB.
* <li>Saturated input (heavily clipped) approaches
* 0dB.
* <li>A low-frequency fully saturated input can
* get above 0dB, but this would be pretty
* artificial.
* <li>A really tiny signal, which only occasionally
* deviates from zero, can get below -100dB.
* <li>A completely zero input will produce an
* output of -Infinity.
* </ul>
* <b>You must be prepared to handle this infinite
* result and results greater than zero,</b> although
* clipping them off would be quite acceptable in
* most cases.
*/
private double calculatePowerDb(short[] sdata, int off, int samples) {
// Calculate the sum of the values, and the sum of the squared values.
// We need longs to avoid running out of bits.
double sum = 0;
double sqsum = 0;
for (int i = 0; i < samples; i++) {
final long v = sdata[off + i];
sum += v;
sqsum += v * v;
}
// sqsum is the sum of all (signal+bias)², so
// sqsum = sum(signal²) + samples * bias²
// hence
// sum(signal²) = sqsum - samples * bias²
// Bias is simply the average value, i.e.
// bias = sum / samples
// Since power = sum(signal²) / samples, we have
// power = (sqsum - samples * sum² / samples²) / samples
// so
// power = (sqsum - sum² / samples) / samples
double power = (sqsum - sum * sum / samples) / samples;
// Scale to the range 0 - 1.
power /= MAX_16_BIT * MAX_16_BIT;
// Convert to dB, with 0 being max power. Add a fudge factor to make
// a "real" fully saturated input come to 0 dB.
return Math.log10(power) * 10f + FUDGE;
}
// ******************************************************************** //
// Constants.
// ******************************************************************** //
// Maximum signal amplitude for 16-bit data.
private final float MAX_16_BIT = 32768;
// This fudge factor is added to the output to make a realistically
// fully-saturated signal come to 0dB. Without it, the signal would
// have to be solid samples of -32768 to read zero, which is not
// realistic. This really is a fudge, because the best value depends
// on the input frequency and sampling rate. We optimise here for
// a 1kHz signal at 16,000 samples/sec.
private final float FUDGE = 0.6f;
}