HarmonicPeriodPane.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.ui.dialog.model;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTextField;

import org.aavso.tools.vstar.util.locale.NumberParser;
import org.aavso.tools.vstar.util.model.Harmonic;
import org.aavso.tools.vstar.util.prefs.NumericPrecisionPrefs;
import org.aavso.tools.vstar.vela.VeLaEvalError;
import org.aavso.tools.vstar.vela.VeLaParseError;

/**
 * This component defines a pane that shows a period and a combo-box requesting
 * the number of harmonics (e.g. to be found and tested in some algorithm such
 * as CLEANest) for that period.
 */
@SuppressWarnings("serial")
public class HarmonicPeriodPane extends JPanel {

	private double frequency;

	private JTextField periodField;
	private String initialPeriodStr;

	private JComboBox harmonicSelector;

	/**
	 * Constructor.
	 * 
	 * @param frequency              The frequency to be displayed as a period.
	 * @param numHarmonics           The maximum number of harmonics that can be
	 *                               selected from.
	 * @param numDefaultNumHarmonics The default number of harmonics for the
	 *                               frequency.
	 */
	public HarmonicPeriodPane(double frequency, int numHarmonics, int defaultNumHarmonics) {

		this.frequency = frequency;

		double period = 1.0 / frequency;

		setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

		String periodStr = String.format("%g", period);
		initialPeriodStr = periodStr;

		periodField = new JTextField(periodStr);
		periodField.setEditable(true);
		periodField.setToolTipText("frequency=" + NumericPrecisionPrefs.formatOther(frequency));
		add(periodField);

		add(Box.createRigidArea(new Dimension(10, 10)));

		String[] harmonicNumbers = new String[numHarmonics];
		for (int i = 0; i < numHarmonics; i++) {
			harmonicNumbers[i] = i + 1 + "";
		}
		harmonicSelector = new JComboBox(harmonicNumbers);
		add(harmonicSelector);
	}

	/**
	 * Return the frequency or null if the field text is malformed.<br/>
	 * If the period text has not been edited, just return the initial frequency
	 * since the reciprocal of the period may have a different number of decimal
	 * places that does not actually correspond to a top hit, if being used in that
	 * context, for example, and context matters! Especially if you want to use a
	 * frequency value for a Fourier model uncertainty calculation which MUST
	 * correspond to an actual top hit that is at the peak of a local periodogram
	 * maximum.
	 * 
	 * @return the frequency
	 */
	public Double getFrequency() {
		Double freq = frequency;
		if (!periodField.getText().equals(initialPeriodStr)) {
			Double period = getPeriod();
			// Garbage may have been entered into the period text field which will yield
			// null from getPeriod().
			freq = period != null ? 1.0 / period : null;
		}
		return freq;
	}

	/**
	 * Return the period or null if the field text is malformed.
	 * 
	 * @return the period
	 */
	public Double getPeriod() {
		String periodText = periodField.getText();
		Double period = null;

		try {
			period = NumberParser.parseDouble(periodText);
		} catch (NumberFormatException e) {
			// Nothing to do; return null.
		} catch (VeLaParseError e) { // #PMAK#
			// Nothing to do; return null.
		} catch (VeLaEvalError e) { // #PMAK#
			// Nothing to do; return null.
		}

		return period;
	}

	/**
	 * Get the number of harmonics (e.g. to search for) for this period.
	 * 
	 * @return The number of harmonics.
	 */
	public int getNumberOfHarmonics() {
		return Integer.parseInt((String) harmonicSelector.getSelectedItem());
	}

	/**
	 * Create and return a harmonic object for this period and harmonic count
	 * selection.
	 * 
	 * @return A harmonic object corresponding to the frequency and harmonic count
	 *         selection, or null if the frequency is null.
	 */
	public Harmonic getHarmonic() {
		Double frequency = getFrequency();
		return frequency != null ? new Harmonic(frequency, getNumberOfHarmonics()) : null;
	}

	/**
	 * A list of Harmonic objects, each representing a frequency and harmonic number
	 * (up to user selection) with respect to some fundamental frequency.
	 * 
	 * @return A list of Harmonic objects or null if any frequency is null.
	 */
	public List<Harmonic> getHarmonicListForPeriod() {
		List<Harmonic> harmonics = new ArrayList<Harmonic>();
		for (int i = 1; i <= getNumberOfHarmonics(); i++) {
			Double frequency = getFrequency();
			if (frequency == null) {
				return Collections.EMPTY_LIST;
			}
			harmonics.add(new Harmonic(frequency * i, i));
		}

		return harmonics;
	}
}