001    package edu.nrao.sss.astronomy;
002    
003    import java.math.BigDecimal;
004    
005    import edu.nrao.sss.measure.Frequency;
006    import edu.nrao.sss.measure.LinearVelocity;
007    import edu.nrao.sss.measure.LinearVelocityUnits;
008    import edu.nrao.sss.util.StringUtil;
009    
010    /**
011     * A spectral line caused by emission or absorption of a photon.
012     * <p>
013     * This class is based on the definition of a spectral line used by the
014     * <a href="http://www.splatalogue.net/">Splatalogue</a>. 
015     * <p>
016     * <b>Version Info:</b>
017     * <table style="margin-left:2em">
018     *   <tr><td>$Revision: 1951 $</td></tr>
019     *   <tr><td>$Date: 2009-02-03 16:26:19 -0700 (Tue, 03 Feb 2009) $</td></tr>
020     *   <tr><td>$Author: dharland $</td></tr>
021     * </table></p>
022     * 
023     * @author David M. Harland
024     * @since 2007-02-28
025     */
026    public class SpectralLine
027      implements Cloneable
028    {
029      private String    species;
030      private String    transition;
031      private Frequency frequency;
032      private Frequency uncertainty;
033      private String    sourceOfInformation;
034      //private Xxx energyOfLowerState;
035    
036      /** Creates a new instance. */
037      public SpectralLine()
038      {
039        species             = "";
040        transition          = "";
041        frequency           = new Frequency();
042        uncertainty         = new Frequency();
043        sourceOfInformation = "";
044      }
045      
046      //============================================================================
047      // SIMPLE GET/SET PROPERTIES
048      //============================================================================
049    
050      /**
051       * Sets the frequency for this line.
052       * 
053       * @param newFrequency the frequency for this line.  If this value is
054       *                     <i>null</i>, it will be replaced with 
055       *                     {@code new Frequency()}.
056       */
057      public void setFrequency(Frequency newFrequency)
058      {
059        frequency = (newFrequency == null) ? new Frequency() : newFrequency;
060      }
061    
062      /**
063       * Returns the frequency of this line.
064       * @return the frequency of this line.
065       */
066      public Frequency getFrequency()
067      {
068        return frequency;
069      }
070      
071      /**
072       * Sets the source of information for this line.
073       * In <i>Splatalogue</i> this corresponds to <i>Line List</i>.
074       * 
075       * @param newSource the source of information for this line.  If this value is
076       *                  <i>null</i>, it will be replaced with the empty string
077       *                  (<tt>""</tt>). 
078       */
079      public void setSourceOfInformation(String newSource)
080      {
081        sourceOfInformation = newSource;
082      }
083    
084      /**
085       * Returns the source of information of this line.
086       * @return the source of information of this line.
087       */
088      public String getSourceOfInformation()
089      {
090        return sourceOfInformation;
091      }
092      
093      /**
094       * Sets the species for this line.
095       * This is the atom or molecule that causes this line
096       * 
097       * @param newSpecies the source of this line.  If this value is
098       *                   <i>null</i>, it will be replaced with the empty string
099       *                   (<tt>""</tt>). 
100       */
101      public void setSpecies(String newSpecies)
102      {
103        species = (newSpecies == null) ? "" : newSpecies;
104      }
105      
106      /**
107       * Returns the atom or molecule that generates this line.
108       * @return the atom or molecule that generates this line.
109       */
110      public String getSpecies()
111      {
112        return species;
113      }
114      
115      /**
116       * Sets the transition for this line.
117       * 
118       * @param newTransition the transition that causes this line.
119       *                      If this value <i>null</i>,
120       *                      it will be replaced with the empty string
121       *                      (<tt>""</tt>). 
122       */
123      public void setTransition(String newTransition)
124      {
125        transition = (newTransition == null) ? "" : newTransition;
126      }
127      
128      /**
129       * Returns the transition that causes this line.
130       * @return the transition that causes this line.
131       */
132      public String getTransition()
133      {
134        return transition;
135      }
136      
137      /**
138       * Sets the uncertainty in the frequency of this line.
139       * 
140       * @param newValue the uncertainty in the frequency of this line.
141       *                 If this value is <i>null</i>, it will be replaced with 
142       *                 {@code new Frequency()}.
143       */
144      public void setUncertainty(Frequency newValue)
145      {
146        uncertainty = (newValue == null) ? new Frequency() : newValue;
147      }
148      
149      /**
150       * Returns the uncertainty in the frequency of this line.
151       * @return the uncertainty in the frequency of this line.
152       */
153      public Frequency getUncertainty()
154      {
155        return uncertainty;
156      }
157    
158      //============================================================================
159      // 
160      //============================================================================
161      
162      /**
163       * Returns the apparent frequency of this line if its source is moving
164       * relative to the observer.
165       * 
166       * @param radialVelocity
167       *   the velocity of the source of this line away from (positive values)
168       *   or toward (negative values) the observer.  Note that this is the
169       *   component of the source's velocity directly away from or toward
170       *   the observer.  If this value is <i>null</i> it will be treated as
171       *   a velocity of zero.
172       * 
173       * @param convention
174       *   the convention to use when using <tt>radialVelocity</tt> to calculate
175       *   the shifted frequency.  If this value is <i>null</i>, the redshift
176       *   convention will be used if the velocity is in units of Z and
177       *   the radio convention will be used if it is in any other units.
178       *   
179       * @return
180       *   the frequency of this line shifted for the given velocity.
181       */
182      public Frequency getShiftedFrequency(LinearVelocity radialVelocity,
183                                           VelocityConvention convention)
184      {
185        BigDecimal shiftFactor = BigDecimal.ONE;
186        
187        if (radialVelocity != null)
188        {
189          if (convention == null)
190            convention = radialVelocity.getUnits().equals(LinearVelocityUnits.Z) ?
191                         VelocityConvention.REDSHIFT : VelocityConvention.RADIO;
192    
193          shiftFactor = convention.getFrequencyShiftFactor(radialVelocity);
194        }
195        
196        return frequency.clone().multiplyBy(shiftFactor);
197      }
198      
199      //============================================================================
200      // 
201      //============================================================================
202    
203      /** Returns a text representation of this spectral line. */
204      @Override
205      public String toString()
206      {
207        StringBuilder buff = new StringBuilder();
208        
209        final String EOL = StringUtil.EOL;
210        
211        buff.append("Species:        ").append(species).append(EOL);
212        buff.append("Transition:     ").append(transition).append(EOL);
213        buff.append("Frequency:      ").append(frequency).append(EOL);
214        buff.append("Uncertainty:    ").append(uncertainty).append(EOL);
215        buff.append("Source of Info: ").append(sourceOfInformation).append(EOL);
216        
217        return buff.toString();
218      }
219      
220      /**
221       * Returns a spectral line that is a copy of this one.
222       * <p>
223       * If anything goes wrong during the cloning procedure,
224       * a {@code RuntimeException} will be thrown.</p>
225       */
226      @Override
227      public SpectralLine clone()
228      {
229        SpectralLine clone = null;
230        
231        try
232        {
233          clone = (SpectralLine)super.clone();
234          
235          clone.frequency   = this.frequency.clone();
236          clone.uncertainty = this.uncertainty.clone();
237        }
238        catch (Exception ex)
239        {
240          throw new RuntimeException(ex);
241        }
242    
243        return clone;
244      }
245      
246      /** Returns <i>true</i> if {@code o} is equal to this spectral line. */
247      @Override
248      public boolean equals(Object o)
249      {
250        //Quick exit if o is this
251        if (o == this)
252          return true;
253        
254        //Quick exit if o is null
255        if (o == null)
256          return false;
257       
258        //Quick exit if classes are different
259        if (!o.getClass().equals(this.getClass()))
260          return false;
261       
262        //A safe cast if we got this far
263        SpectralLine other = (SpectralLine)o;
264        
265        return other.species.equals            (this.species)             &&
266               other.transition.equals         (this.transition)          &&
267               other.sourceOfInformation.equals(this.sourceOfInformation) &&
268               other.frequency.equals          (this.frequency)           &&
269               other.uncertainty.equals        (this.uncertainty);
270      }
271    
272      /** Returns a hash code value for this spectral line. */
273      @Override
274      public int hashCode()
275      {
276        //Taken from the Effective Java book by Joshua Bloch.
277        //The constants 17 & 37 are arbitrary & carry no meaning.
278        int result = 17;
279        
280        //This method MUST be kept in synch w/ the equals method.
281        
282        result = 37 * result + species.hashCode();
283        result = 37 * result + transition.hashCode();
284        result = 37 * result + sourceOfInformation.hashCode();
285        result = 37 * result + frequency.hashCode();
286        result = 37 * result + uncertainty.hashCode();
287        
288        return result;
289      }
290    }