package pl.edu.fuw.fid.signalanalysis.wavelet;
import org.apache.commons.math.complex.Complex;
import pl.edu.fuw.fid.signalanalysis.AsyncStatus;
import pl.edu.fuw.fid.signalanalysis.waveform.ImageRenderer;
import pl.edu.fuw.fid.signalanalysis.waveform.PreferencesWithAxes;
import pl.edu.fuw.fid.signalanalysis.SingleSignal;
import pl.edu.fuw.fid.signalanalysis.waveform.ImageResult;
import pl.edu.fuw.fid.signalanalysis.waveform.Waveform;
/**
* Computes Wavelet Transform coefficients for parameters selected by the user.
*
* @author ptr@mimuw.edu.pl
*/
public class ImageRendererForWavelet extends ImageRenderer<PreferencesForWavelet> {
private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(ImageRendererForWavelet.class);
private volatile MotherWavelet wavelet_ = new GaborWavelet();
private volatile boolean logScale_ = false;
public ImageRendererForWavelet(SingleSignal signal) {
super(signal);
}
@Override
public ImageResult compute(PreferencesWithAxes<PreferencesForWavelet> preferences, AsyncStatus status) throws Exception {
final PreferencesForWavelet prefs = preferences.prefs;
// długość okna zależy od maksymalnej długości falki
int windowLength = 16;
double fullWidth = 2 * prefs.wavelet.getHalfWidth();
double maxScale = 1.0 / Math.min(preferences.yMin, preferences.yMax);
while (maxScale * sampling * fullWidth > windowLength) {
windowLength *= 2;
}
final Complex[][] windows = new Complex[preferences.height][];
ImageResult result = new ImageResult(preferences.width, preferences.height, "Averaged wavelet transform ("+prefs.wavelet.getLabel()+")");
// prepare windows
Complex[] window = new Complex[windowLength];
for (int iy=0; iy<preferences.height; ++iy) {
if (status.isCancelled()) {
return null;
}
status.setProgress(0.25 * iy / preferences.height);
double f = prefs.logScale
? Math.exp( Math.log(preferences.yMin) + Math.log(preferences.yMax / preferences.yMin) * iy / (preferences.height - 1) )
: preferences.yMin + (preferences.yMax - preferences.yMin) * iy / (preferences.height - 1);
result.f[iy] = f;
Waveform scaled = prefs.wavelet.scale(f);
double norm = 0.0;
for (int ix=0; ix<windowLength; ++ix) {
double t = (ix - 0.5*(windowLength-1)) / sampling;
window[ix] = scaled.value(t);
double re = window[ix].getReal(), im = window[ix].getImaginary();
norm += re*re + im*im;
}
norm = 1.0 / Math.sqrt(norm * windowLength);
for (int ix=0; ix<windowLength; ++ix) {
window[ix] = window[ix].multiply(norm);
}
windows[iy] = window.clone();
}
double[] chunk = new double[windowLength];
for (int ix=0; ix<preferences.width; ++ix) {
if (status.isCancelled()) {
return null;
}
status.setProgress(0.25 + 0.75 * ix / preferences.width);
double t = preferences.xMin + (preferences.xMax - preferences.xMin) * ix / (preferences.width - 1);
result.t[ix] = t;
int i0 = (int) Math.floor(sampling * t) - windowLength / 2;
signal.getSamples(i0, windowLength, chunk);
for (int iy=0; iy<preferences.height; ++iy) {
Complex sum = Complex.ZERO;
for (int iw=0; iw<windowLength; ++iw) {
sum = sum.add(windows[iy][iw].multiply(chunk[iw]));
}
result.values[ix][iy] = sum.conjugate().multiply(windowLength);
}
}
return result;
}
@Override
protected PreferencesForWavelet getPreferences() {
PreferencesForWavelet prefs = new PreferencesForWavelet();
prefs.wavelet = wavelet_;
prefs.logScale = logScale_;
return prefs;
}
public MotherWavelet getWavelet() {
return wavelet_;
}
public void setWavelet(MotherWavelet wavelet) {
wavelet_ = wavelet;
}
public boolean isLogScale() {
return logScale_;
}
public void setLogScale(boolean logScale) {
logScale_ = logScale;
}
}