SineFit.java
// @formatter:off
/*******************************************************************************
*
* This file is part of JMad.
*
* Copyright (c) 2008-2011, CERN. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
// @formatter:on
package cern.accsoft.steering.util.fit;
import java.util.ArrayList;
import java.util.List;
import cern.jdve.data.DataSet;
import cern.jdve.data.DefaultDataSet;
import cern.jdve.utils.DataRange;
import cern.jdve.utils.DisplayPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the sine-function used for fitting.
*
* @author Kajetan Fuchsberger (kajetan.fuchsberger at cern.ch)
*/
// public class SineFit implements FCNBase, DataViewerFit {
public class SineFit implements DataViewerFit {
/** the logger for the class */
private final static Logger logger = LoggerFactory.getLogger(SineFit.class);
/*
* the name of the used parameters
*/
public final static String PARAM_NAME_AMPLITUDE = "amplitude";
public final static String PARAM_NAME_FREQUENCY = "frequency";
public final static String PARAM_NAME_PHASE = "phase";
/*
* the indizes of the parameters
*/
public final static int PARAM_INDEX_AMPLITUDE = 0;
public final static int PARAM_INDEX_FREQUENCY = 1;
public final static int PARAM_INDEX_PHASE = 2;
/** the display points to which to fit the sine */
private List<DisplayPoint> displayPoints = new ArrayList<DisplayPoint>();
/** the last determined sine */
private SineFunction lastFit;
/**
* the default constructor. Initialize the points to where to fit to.
*
* @param displayPoints the points to fit the sine
*/
public SineFit(List<DisplayPoint> displayPoints) {
this.displayPoints = displayPoints;
}
// @Override
public double valueOf(double[] params) {
int nPoints = displayPoints.size();
SineFunction sine = new SineFunction(params[PARAM_INDEX_AMPLITUDE], params[PARAM_INDEX_FREQUENCY],
params[PARAM_INDEX_PHASE]);
double sum = 0.0;
for (DisplayPoint point : this.displayPoints) {
sum += Math.pow(point.getY() - sine.valueAt(point.getX()), 2);
}
if (nPoints > 1) {
sum /= (nPoints - 1);
}
/* return the chisquare */
return Math.sqrt(sum);
}
/**
* the sine function
*
* @author Kajetan Fuchsberger (kajetan.fuchsberger at cern.ch)
*/
private class SineFunction {
/** the amplitude of the sine function */
private double amplitude;
/** the frequency of the sine function */
private double frequency;
/** the phase of the sine function */
private double phase;
/**
* the constructor with the parameters
*
* @param amplitude
* @param frequency
* @param phase
*/
private SineFunction(double amplitude, double frequency, double phase) {
this.amplitude = amplitude;
this.frequency = frequency;
this.phase = phase;
}
/**
* calculates the value at the given point x
*
* @param x the value for which to calculate the value
* @return the value
*/
private double valueAt(double x) {
return (amplitude * Math.sin(2 * Math.PI * (frequency * x + phase)));
}
@Override
public String toString() {
return "amplitude: " + amplitude + "; frequency: " + frequency + "; phase: " + phase + ";";
}
}
@Override
public DataSet getResultDataSet(DataRange range, int nPoints) {
SineFunction sine = getLastFit();
if (sine == null) {
logger.warn("No fitted function available. Either the fit was not performed, or it did not give a valid result.");
return new DefaultDataSet("Sine fit", new double[] {}, new double[] {});
}
double step = range.getLength() / (nPoints - 1);
double[] xValues = new double[nPoints];
double[] yValues = new double[nPoints];
for (int i = 0; i < nPoints; i++) {
double xValue = range.getMin() + (step * i);
xValues[i] = xValue;
yValues[i] = sine.valueAt(xValue);
}
return new DefaultDataSet("Sine fit", xValues, yValues);
}
/**
* performs the fit and stores the fitted function for further usage.
*/
public void doFit() {
// MnUserParameters upar = new MnUserParameters();
// upar.add(PARAM_NAME_AMPLITUDE, 1.0, 0.01);
// upar.add(PARAM_NAME_FREQUENCY, 1.0, 0.01);
// upar.add(PARAM_NAME_PHASE, 0.0, 0.01);
//
// MnMigrad migrad = new MnMigrad(this, upar);
//
// FunctionMinimum min = migrad.minimize();
//
// if (min.isValid()) {
// MnUserParameters parameters = min.userParameters();
// this.lastFit = new SineFunction(parameters
// .value(PARAM_NAME_AMPLITUDE), parameters
// .value(PARAM_NAME_FREQUENCY), parameters
// .value(PARAM_NAME_PHASE));
// double chiSquared = valueOf(new double[] {
// parameters.value(PARAM_NAME_AMPLITUDE),
// parameters.value(PARAM_NAME_FREQUENCY),
// parameters.value(PARAM_NAME_PHASE) });
// logger.info("Successful fit. Result: " + this.lastFit.toString()
// + " chisquared: " + chiSquared + ";");
// } else {
// logger.warn("Fit was not successful.");
// this.lastFit = null;
// }
}
/**
* @return the lastFit
*/
public SineFunction getLastFit() {
return lastFit;
}
}