PeriodFitParameters.java
/**
* VStar: a statistical analysis tool for variable star data.
* Copyright (C) 2010 AAVSO (http://www.aavso.org/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.aavso.tools.vstar.util.model;
import org.aavso.tools.vstar.util.Tolerance;
import org.aavso.tools.vstar.util.prefs.NumericPrecisionPrefs;
/**
* This class represents a single period-based fit coefficient/parameter set
* that could be be used to re-create a model fit.
*/
public class PeriodFitParameters implements Comparable<PeriodFitParameters> {
private final static double DELTA = 1e-4;
private final static double TWOPI = 2 * Math.PI;
private Harmonic harmonic;
private double amplitude;
private double phase;
private double sineCoefficient;
private double cosineCoefficient;
private double constantCoefficient;
private double zeroPointOffset;
/**
* Constructor.
*
* TODO: consider computing amplitude locally rather than passing it in!
*
* @param harmonic The harmonic, from which can be obtained period
* and frequency.
* @param amplitude The amplitude parameter.
* @param cosineCoefficient The cosine Fourier coefficient.
* @param sineCoefficient The sine Fourier coefficient.
* @param constantCoefficient The constant (Y intercept?; check) coefficient.
* @param zeroPointOffset The zero point offset/term/subrahend to be
* subtracted from each time step for which the model
* is computed.
*/
public PeriodFitParameters(Harmonic harmonic, double amplitude, double cosineCoefficient, double sineCoefficient,
double constantCoefficient, double zeroPointOffset) {
this.harmonic = harmonic;
this.amplitude = amplitude;
this.phase = Math.atan2(-sineCoefficient, cosineCoefficient);
this.cosineCoefficient = cosineCoefficient;
this.sineCoefficient = sineCoefficient;
this.constantCoefficient = constantCoefficient;
this.zeroPointOffset = zeroPointOffset;
}
/**
* @return the harmonic
*/
public Harmonic getHarmonic() {
return harmonic;
}
/**
* @return the frequency
*/
public double getFrequency() {
return harmonic.getFrequency();
}
/**
* @return the harmonic number of the corresponding frequency
*/
public int getHarmonicNumber() {
return harmonic.getHarmonicNumber();
}
/**
* @return the period
*/
public double getPeriod() {
return harmonic.getPeriod();
}
/**
* @return the amplitude
*/
public double getAmplitude() {
return amplitude;
}
/**
* Get the relative amplitude given the first amplitude to which the current
* amplitude is relative.
*
* @param firstAmplitude The amplitude to which the current parameter's
* amplitude should be taken to be relative.
* @return The relative amplitude.
*/
public double getRelativeAmplitude(double firstAmplitude) {
return amplitude / firstAmplitude;
}
/**
* @return the phase in radians
*/
public double getPhase() {
return phase;
}
/**
* Get the relative phase in radians given the first phase to which the current
* phase is relative. The result is adjusted to be in the range 0..2PI.
*
* @param firstPhase The phase to which the current parameter's phase should be
* taken to be relative.
* @return The relative phase in radians.
*/
public double getRelativePhase(double firstPhase) {
double result = phase - harmonic.getHarmonicNumber() * firstPhase;
while (result < 0) {
result += TWOPI;
}
while (result > TWOPI) {
result -= TWOPI;
}
return result;
}
/**
* Get the relative phase in cycles (radians/2PI) given the first phase to which
* the current phase is relative.
*
* @param firstPhase The phase to which the current parameter's phase should be
* taken to be relative.
* @return The relative phase in cycles.
*/
public double getRelativePhaseInCycles(double firstPhase) {
return getRelativePhase(firstPhase) / TWOPI;
}
/**
* @return the sineCoefficient
*/
public double getSineCoefficient() {
return sineCoefficient;
}
/**
* @return the cosineCoefficient
*/
public double getCosineCoefficient() {
return cosineCoefficient;
}
/**
* @return the constantCoefficient
*/
public double getConstantCoefficient() {
return constantCoefficient;
}
/**
* @return the zeroPointOffset
*/
public double getZeroPointOffset() {
return zeroPointOffset;
}
/**
* @see java.lang.Object#equals(java.lang.Object) Equality of parameters to 4
* decimal places.
*/
@Override
public boolean equals(Object other) {
boolean equal = other instanceof PeriodFitParameters;
if (equal) {
PeriodFitParameters params = (PeriodFitParameters) other;
equal &= Tolerance.areClose(params.getFrequency(), getFrequency(), DELTA, true);
equal &= Tolerance.areClose(params.getPeriod(), getPeriod(), DELTA, true);
equal &= Tolerance.areClose(params.getAmplitude(), getAmplitude(), DELTA, true);
equal &= Tolerance.areClose(params.getCosineCoefficient(), getCosineCoefficient(), DELTA, true);
equal &= Tolerance.areClose(params.getSineCoefficient(), getSineCoefficient(), DELTA, true);
equal &= Tolerance.areClose(params.getConstantCoefficient(), getConstantCoefficient(), DELTA, true);
equal &= Tolerance.areClose(params.getZeroPointOffset(), getZeroPointOffset(), DELTA, true);
}
return equal;
}
public String toProsaicString() {
String str = "parameters: ";
str += "frequency=";
str += NumericPrecisionPrefs.formatOther(getFrequency());
str += ", ";
str += "period=";
str += NumericPrecisionPrefs.formatOther(getPeriod());
str += ", ";
str += "amplitude=";
str += NumericPrecisionPrefs.formatOther(amplitude);
str += ", ";
str += "cosine coefficient=";
str += NumericPrecisionPrefs.formatOther(cosineCoefficient);
str += ", ";
str += "sine coefficient=";
str += NumericPrecisionPrefs.formatOther(sineCoefficient);
str += ", ";
str += "constant coefficient=";
str += NumericPrecisionPrefs.formatOther(constantCoefficient);
str += "zero point offset=";
str += NumericPrecisionPrefs.formatTime(zeroPointOffset);
return str;
}
public String toString() {
String str = cosineCoefficient >= 0 ? "+" : "";
String sincosParam = "2*PI*" + harmonic + "*(t-zeroPoint" + ")";
str += NumericPrecisionPrefs.formatOther(cosineCoefficient) + " * cos(";
str += sincosParam;
str += ")";
str += sineCoefficient >= 0 ? " + " : "";
str += NumericPrecisionPrefs.formatOther(sineCoefficient) + " * sin(";
str += sincosParam;
str += ")";
return str;
}
public String toExcelString() {
String str = cosineCoefficient >= 0 ? "+" : "";
String sincosParam = "2*PI()*" + harmonic + "*(A1-" + NumericPrecisionPrefs.formatTime(zeroPointOffset) + ")";
str += NumericPrecisionPrefs.formatOther(cosineCoefficient) + " * COS(";
str += sincosParam;
str += ")";
str += sineCoefficient >= 0 ? " + " : "";
str += NumericPrecisionPrefs.formatOther(sineCoefficient) + " * SIN(";
str += sincosParam;
str += ")";
return str;
}
// toRString must be locale-independent!
public String toRString() {
String str = "+\n";
// harmonic.toString() is locale-dependent!
String sincosParam = "2*pi*" + NumericPrecisionPrefs.formatOtherLocaleIndependent(harmonic.getFrequency())
+ "*(t-zeroPoint" + ")";
str += NumericPrecisionPrefs.formatOtherLocaleIndependent(cosineCoefficient) + " * cos(";
str += sincosParam;
str += ")";
str += sineCoefficient >= 0 ? " + " : "";
str += NumericPrecisionPrefs.formatOtherLocaleIndependent(sineCoefficient) + " * sin(";
str += sincosParam;
str += ")";
return str;
}
public double toValue(double t) {
double sincosParam = 2 * Math.PI * harmonic.getFrequency() * (t - zeroPointOffset);
return cosineCoefficient * Math.cos(sincosParam) + sineCoefficient * Math.sin(sincosParam);
}
@Override
public int compareTo(PeriodFitParameters other) {
return getHarmonic().compareTo(other.getHarmonic());
}
}