001    package edu.nrao.sss.model.source;
002    
003    import java.math.BigDecimal;
004    
005    import javax.xml.bind.annotation.XmlType;
006    
007    import edu.nrao.sss.astronomy.VelocityConvention;
008    import edu.nrao.sss.astronomy.VelocityFrame;
009    import edu.nrao.sss.measure.Frequency;
010    import edu.nrao.sss.measure.FrequencyRange;
011    import edu.nrao.sss.measure.LinearVelocity;
012    
013    /**
014     * The velocity of an astronomical source.
015     * <p>
016     * <b>Version Info:</b>
017     * <table style="margin-left:2em">
018     *   <tr><td>$Revision: 1710 $</td>
019     *   <tr><td>$Date: 2008-11-14 11:54:07 -0700 (Fri, 14 Nov 2008) $</td>
020     *   <tr><td>$Author: dharland $</td>
021     * </table></p>
022     *
023     * @author David M. Harland
024     * @since 2006-03-24
025     */
026    @XmlType
027    public class SourceVelocity
028      implements Cloneable, Comparable<SourceVelocity>
029    {
030      private VelocityConvention convention;
031      private VelocityFrame      restFrame;
032      private FrequencyRange     validFrequency;
033      private LinearVelocity     radialVelocity;
034    
035      /** Creates a new instance. */
036      public SourceVelocity()
037      {
038        validFrequency = new FrequencyRange();
039        radialVelocity = new LinearVelocity();
040        
041        initialize();
042      }
043      
044      private void initialize()
045      {
046        convention = VelocityConvention.getDefault();
047        restFrame  = VelocityFrame.getDefault();
048        
049        radialVelocity.set(BigDecimal.ZERO, convention.getDefaultUnits());
050      }
051      
052      /** Returns this velocity to its initial state. */
053      public void reset()
054      {
055        initialize();
056    
057        validFrequency.reset();
058      }
059      
060      /**
061       * Sets the convention used for this velocity.
062       * <p>
063       * If {@code newConvention} is <i>null</i>, it will be treated as
064       * a non-null default type.</p>
065       * 
066       * @param newConvention the measurement convention used by this velocity.
067       */
068      public void setConvention(VelocityConvention newConvention)
069      {
070        if (newConvention == null)
071          newConvention = VelocityConvention.getDefault();
072        
073        convention = newConvention;
074      }
075      
076      /**
077       * Returns the convention used for this velocity.
078       * 
079       * @return the velocity convention used by this source velocity.
080       */
081      public VelocityConvention getConvention()
082      {
083        return convention;
084      }
085      
086      /**
087       * Sets the frame of rest used for this velocity.
088       * 
089       * @param restFrame the frame of rest used for this velocity.
090       */
091      public void setRestFrame(VelocityFrame restFrame)
092      {
093        this.restFrame = restFrame;
094      }
095      
096      /**
097       * Returns the frame of rest used for this velocity.
098       * 
099       * @return the frame of rest used for this velocity.
100       */
101      public VelocityFrame getRestFrame()
102      {
103        return restFrame;
104      }
105    
106      /**
107       * Sets the speed of this source velocity.
108       * Note that {@code newSpeed} will be referenced internally by this object,
109       * so any changes made to it after this call will be reflected herein.
110       * <p>
111       * If {@code newSpeed} is <i>null</i>, this velocity's speed will be set
112       * to zero, with the units set according to the current 
113       * {@link #getConvention() convention}.</p>
114       * 
115       * @param newSpeed
116       *   the new radial velocity for this source velocity.
117       */
118      public void setRadialVelocity(LinearVelocity newSpeed)
119      {
120        if (newSpeed != null)
121          radialVelocity = newSpeed;
122        else
123          radialVelocity.set(BigDecimal.ZERO, convention.getDefaultUnits());
124      }
125      
126      /**
127       * Returns the speed of this source velocity.
128       * <p>
129       * The return value is guaranteed to be non-null.  It is
130       * also the speed that is held internally by this source
131       * velocity, so any changes made to the returned speed
132       * will be reflected in this object.</p>
133       * 
134       * @return
135       *   the radial velocity for this source velocity.
136       */
137      public LinearVelocity getRadialVelocity()
138      {
139        return radialVelocity;
140      }
141      
142      /**
143       * Sets the valid frequency range for this source velocity,
144       * assuming the source is at rest.
145       * <p>
146       * If {@code newRange} is <i>null</i>, this velocity's frequency
147       * range will be set to all (positive) frequencies.</p>
148       * 
149       * @param newRange the frequency range for this source velocity.
150       */
151      public void setValidFrequency(FrequencyRange newRange)
152      {
153        validFrequency = (newRange == null) ? new FrequencyRange() : newRange;
154      }
155      
156      /**
157       * Returns the frequency range for which this source velocity
158       * is valid.
159       * <p>
160       * The return value is guaranteed to be non-null.  It is
161       * also the range that is held internally by this source
162       * velocity, so any changes made to the returned range
163       * will be reflected in this object.</p>
164       * 
165       * @return the valid frequency range for this source velocity.
166       */
167      public FrequencyRange getValidFrequency()
168      {
169        return validFrequency;
170      }
171      
172      /**
173       * Returns an observed frequency based on a rest frequency and the
174       * convention and magnitude of this source velocity.
175       *      
176       * @param restFreq
177       *   a rest frequency that is to be transformed to
178       *   another value based on this velocity.
179       * 
180       * @return
181       *   an observed frequency based on {@code restFreq} and this velocity.
182       */
183      public Frequency calcShiftedFrequency(Frequency restFreq)
184      {
185        BigDecimal velocityInConventionalUnits =
186          radialVelocity.toUnits(convention.getDefaultUnits());
187    
188        BigDecimal factor =
189          convention.getFrequencyShiftFactor(velocityInConventionalUnits);
190    
191        return restFreq.clone().multiplyBy(factor);
192      }
193      
194      @Override
195      public String toString()
196      {
197        final String DELIM = ";";
198        StringBuilder buff = new StringBuilder();
199        
200        buff.append("velocity = ").append(radialVelocity).append(DELIM);
201        buff.append(" frame = ").append(restFrame).append(DELIM);
202        buff.append(" convention = ").append(convention).append(DELIM);
203        buff.append(" frequency = ").append(validFrequency).append(DELIM);
204        
205        return buff.toString();
206      }
207      
208      /**
209       *  Returns a source velocity that is equal to this one.
210       *  <p>
211       *  If anything goes wrong during the cloning procedure,
212       *  a {@code RuntimeException} will be thrown.</p>
213       */
214      @Override
215      public SourceVelocity clone()
216      {
217        SourceVelocity clone = null;
218    
219        try
220        {
221          //This line takes care of the primitive & immutable fields properly
222          clone = (SourceVelocity)super.clone();
223          
224          clone.setValidFrequency(this.getValidFrequency().clone());
225          clone.setRadialVelocity(this.radialVelocity.clone());
226        }
227        catch (Exception ex)
228        {
229          throw new RuntimeException(ex);
230        }
231        
232        return clone;
233      }
234      
235      /** Returns <i>true</i> if {@code o} is equal to this source velocity. */
236      @Override
237      public boolean equals(Object o)
238      {
239        //Quick exit if o is this
240        if (o == this)
241          return true;
242        
243        //Quick exit if o is null
244        if (o == null)
245          return false;
246        
247        //Quick exit if classes are different
248        if (!o.getClass().equals(this.getClass()))
249          return false;
250    
251        SourceVelocity other = (SourceVelocity)o;
252        
253        //Separated the tests for easier debugging
254        if (!other.restFrame.equals(this.restFrame))
255          return false;
256        
257        if (!other.validFrequency.equals(this.validFrequency))
258          return false;
259        
260        if (!other.getRadialVelocity().equals(this.getRadialVelocity()))
261          return false;
262        
263        return true;
264      }
265    
266      /** Returns a hash code value for this source velocity. */
267      @Override
268      public int hashCode()
269      {
270        //Taken from the Effective Java book by Joshua Bloch.
271        //The constants 17 & 37 are arbitrary & carry no meaning.
272        int result = 17;
273        
274        result = 37 * result + radialVelocity.hashCode();
275        result = 37 * result + restFrame.hashCode();
276        result = 37 * result + validFrequency.hashCode();
277        
278        return result;
279      }
280      
281      /**
282       * Compares this velocity to {@code other} for order.
283       * <p>
284       * One velocity is deemed to be "less than" the other if it has
285       * a valid frequency range that is less than that of the other.
286       * In the case that two velocities have the same valid frequency range,
287       * the rest frame is used as a tie-breaker.  If these are the
288       * same the velocity value, in km/s, is compared next.
289       * If no differences are found, the return value is zero.</p>
290       * 
291       * @param other the velocity to which this one is compared.
292       * 
293       * @return a negative integer, zero, or a positive integer as this velocity
294       *         is less than, equal to, or greater than the other velocity.
295       */
296      public int compareTo(SourceVelocity other)
297      {
298        //First compare the valid frequency ranges
299        int answer = this.validFrequency.compareTo(other.validFrequency);
300        if (answer != 0)
301          return answer;
302    
303        //Rest frame
304        answer = this.restFrame.compareTo(other.restFrame);
305        if (answer != 0)
306          return answer;
307    
308        //Velocity
309        answer = this.getRadialVelocity().compareTo(other.getRadialVelocity());
310        if (answer != 0)
311          return answer;
312    
313        //No differences found
314        return 0;
315      }
316    }