001    package edu.nrao.sss.astronomy;
002    
003    import static java.math.BigDecimal.ONE;
004    import static java.math.BigDecimal.ZERO;
005    
006    import java.math.BigDecimal;
007    
008    import static edu.nrao.sss.math.MathUtil.MC_INTERM_CALCS;
009    import static edu.nrao.sss.math.MathUtil.POSITIVE_INFINITY;
010    import static edu.nrao.sss.measure.Wave.LIGHT_SPEED_VACUUM_KM_PER_SEC;
011    
012    import edu.nrao.sss.math.MathUtil;
013    import edu.nrao.sss.measure.LinearVelocity;
014    import edu.nrao.sss.measure.LinearVelocityUnits;
015    import edu.nrao.sss.util.EnumerationUtility;
016    
017    /**
018     * A convention used to convert a rest frequency to a sky frequency.
019     * <p>
020     * A discussion of these conventions may be found at
021     * <a href="http://iram.fr/IRAMFR/ARN/may95/node4.html">
022     * http://iram.fr/IRAMFR/ARN/may95/node4.html</a> and
023     * <a href="http://www.gb.nrao.edu/~fghigo/gbtdoc/doppler.html">
024     * http://www.gb.nrao.edu/~fghigo/gbtdoc/doppler.html</a>.</p>
025     * <p>
026     * <b>Version Info:</b>
027     * <table style="margin-left:2em">
028     *   <tr><td>$Revision: 1572 $</td></tr>
029     *   <tr><td>$Date: 2008-09-22 16:52:39 -0600 (Mon, 22 Sep 2008) $</td></tr>
030     *   <tr><td>$Author: dharland $</td></tr>
031     * </table></p>
032     *  
033     * @author David M. Harland
034     * @since 2006-03-31
035     */
036    public enum VelocityConvention
037    {
038      /** 
039       * The optical astronomy
040       * convention for converting a rest frequency to a sky frequency. 
041       * <p>
042       * <u>Convention:</u>
043       * <blockquote>
044       * f<sub>sky</sub> = f<sub>rest</sub> * (1 + v/c)<sup>-1</sup>
045       * </blockquote></p>
046       */
047      OPTICAL(LinearVelocityUnits.KILOMETERS_PER_SECOND)
048      {
049        public BigDecimal getFrequencyShiftFactor(BigDecimal kmPerSec)
050        {
051          //f.sky / f.rest = 1 / (1 + v/c)
052          
053          BigDecimal denominator =
054            ONE.add(kmPerSec.divide(LIGHT_SPEED_VACUUM_KM_PER_SEC,
055                                    MC_INTERM_CALCS));
056    
057          if (denominator.signum() <= 0)
058            return MathUtil.POSITIVE_INFINITY;
059          else
060            return ONE.divide(denominator, MC_INTERM_CALCS);
061        }
062        
063        public LinearVelocity getVelocity(BigDecimal f)
064        {
065          //v = [(1-f)/f] * c
066          
067          BigDecimal kms;
068          
069          if (f.signum() == 0)
070            kms = MathUtil.POSITIVE_INFINITY;
071          else
072            kms = ONE.subtract(f).divide(f, MC_INTERM_CALCS)
073                                 .multiply(LIGHT_SPEED_VACUUM_KM_PER_SEC);
074          
075          return new LinearVelocity(kms, getDefaultUnits());
076        }
077      },
078      
079      /** 
080       * The radio astronomy
081       * convention for converting a rest frequency to a sky frequency. 
082       * <p>
083       * <u>Convention:</u>
084       * <blockquote>
085       * f<sub>sky</sub> = f<sub>rest</sub> * (1 - v/c)
086       * </blockquote></p>
087       */
088      RADIO(LinearVelocityUnits.KILOMETERS_PER_SECOND)
089      {
090        public BigDecimal getFrequencyShiftFactor(BigDecimal kmPerSec)
091        {
092          //f.sky / f.rest = 1 - v/c
093          
094          if (kmPerSec.compareTo(LIGHT_SPEED_VACUUM_KM_PER_SEC) >= 0)
095            return BigDecimal.ZERO;
096          else
097            return ONE.subtract(kmPerSec.divide(LIGHT_SPEED_VACUUM_KM_PER_SEC,
098                                                MC_INTERM_CALCS));
099        }
100        
101        public LinearVelocity getVelocity(BigDecimal f)
102        {
103          //v = (1-f) * c
104    
105          BigDecimal kms = ONE.subtract(f).multiply(LIGHT_SPEED_VACUUM_KM_PER_SEC);
106          
107          return new LinearVelocity(kms, getDefaultUnits());
108        }
109      },
110      
111      /**
112       * The red shift
113       * convention for converting a rest frequency to a sky frequency. 
114       * <p>
115       * <u>Convention:</u>
116       * <blockquote>
117       * f<sub>sky</sub> = f<sub>rest</sub> * (1 + z)<sup>-1</sup>
118       * </blockquote></p>
119       */
120      REDSHIFT(LinearVelocityUnits.Z)
121      {
122        public BigDecimal getFrequencyShiftFactor(BigDecimal z)
123        {
124          //f.sky / f.rest = 1 / (1+z)
125    
126          if (z.compareTo(ONE.negate()) <= 0)
127            return ZERO;
128          else
129            return ONE.divide(ONE.add(z), MC_INTERM_CALCS);
130        }
131        
132        public LinearVelocity getVelocity(BigDecimal f)
133        {
134          //z = (1-f) / f
135          
136          BigDecimal z;
137          
138          if (f.signum() == 0)
139            z = POSITIVE_INFINITY;
140          else
141            z = ONE.subtract(f).divide(f, MC_INTERM_CALCS);
142          
143          return new LinearVelocity(z, getDefaultUnits());
144        }
145      },
146      
147      /**
148       * The relativistic
149       * convention for converting a rest frequency to a sky frequency. 
150       * <p>
151       * <u>Convention:</u>
152       * <blockquote>
153       * f<sub>sky</sub> = f<sub>rest</sub> *
154       * (1 - (v/c)<sup>2</sup>)<sup>&#x00BD;</sup> / (1 + v/c)
155       * </blockquote></p>
156       */
157      RELATIVISTIC(LinearVelocityUnits.KILOMETERS_PER_SECOND)
158      {
159        public BigDecimal getFrequencyShiftFactor(BigDecimal kmPerSec)
160        {
161          //f.sky / f.rest = sqrt[1 - (v/c)*(v/c)] / (1 + v/c)
162    
163          BigDecimal answer;
164          
165          BigDecimal c = LIGHT_SPEED_VACUUM_KM_PER_SEC;
166          
167          if (kmPerSec.compareTo(c) >= 0)
168          {
169            answer = BigDecimal.ZERO;
170          }
171          else if (kmPerSec.compareTo(c.negate()) <= 0)
172          {
173            answer = MathUtil.POSITIVE_INFINITY;
174          }
175          else
176          {
177            BigDecimal vOverC      = kmPerSec.divide(c, MC_INTERM_CALCS);
178            BigDecimal numSq       = ONE.subtract(vOverC.multiply(vOverC));
179            BigDecimal numerator   = BigDecimal.valueOf(Math.sqrt(numSq.doubleValue()));
180            BigDecimal denominator = ONE.add(vOverC);
181              
182            answer = numerator.divide(denominator, MC_INTERM_CALCS);
183          }
184          
185          return answer;
186        }
187        
188        public LinearVelocity getVelocity(BigDecimal f)
189        {
190          //v = [(1-f*f) / (1+f*f)] * c
191          
192          BigDecimal fSq    = f.multiply(f);
193          BigDecimal fNum   = ONE.subtract(fSq);
194          BigDecimal fDenom = ONE.add(fSq);
195          BigDecimal kms    = fNum.divide(fDenom, MC_INTERM_CALCS)
196                                  .multiply(LIGHT_SPEED_VACUUM_KM_PER_SEC);
197          
198          return new LinearVelocity(kms, getDefaultUnits());
199        }
200      };
201      
202      //============================================================================
203      // 
204      //============================================================================
205    
206      private LinearVelocityUnits defaultUnits;
207      
208      private VelocityConvention(LinearVelocityUnits defaultUnits)
209      {
210        this.defaultUnits = defaultUnits;
211      }
212      
213      /**
214       * Returns the default velocity units used by this convention.
215       * 
216       * @return the default velocity units used by this convention.
217       */
218      public LinearVelocityUnits getDefaultUnits()
219      {
220        return defaultUnits;
221      }
222      
223      /**
224       * Returns a factor that can be used to shift a rest frequency to one
225       * appropriate for the given velocity.
226       * The velocity value sent to this method will be assumed to be in
227       * the default units of this convention.
228       * <p>
229       * See the descriptions of the individual elements of this enumeration
230       * for the formula used by a given convention.</p>
231       *  
232       * @param velocityInDefaultUnits the velocity, in the default units of this
233       *                               convention, for which a frequency-shifting
234       *                               factor is desired.
235       */
236      public abstract BigDecimal
237        getFrequencyShiftFactor(BigDecimal velocityInDefaultUnits);
238      
239      /**
240       * Returns the velocity for the given shift factor.
241       * Positive values represent motion away from the observer.
242       * 
243       * @param shiftFactor the ratio between the observed frequency and the
244       *                    rest frequency.
245       *                    
246       * @return the velocity for the given shift factor.
247       */
248      public abstract LinearVelocity getVelocity(BigDecimal shiftFactor);
249      
250      /**
251       * Returns a factor that can be used to shift a rest frequency to one
252       * appropriate for the given velocity.
253       * 
254       * @param velocity the velocity for which a frequency-shifting factor
255       *                 is desired.
256       *                 
257       * @return a factor that can be used to shift a rest frequency to one
258       *         appropriate for the given velocity.
259       *         If {@code velocity} is <i>null</i>, a factor of one will be
260       *         returned.
261       */
262      public BigDecimal getFrequencyShiftFactor(LinearVelocity velocity)
263      {
264        return (velocity == null) ? ONE :
265          getFrequencyShiftFactor(velocity.toUnits(getDefaultUnits()));
266      }
267      
268      /**
269       * Returns a default velocity convention.
270       * @return a default velocity convention.
271       */
272      public static VelocityConvention getDefault()
273      {
274        return RADIO;
275      }
276      
277      /**
278       * Returns a text representation of this enumeration constant.
279       * @return a text representation of this enumeration constant.
280       */
281      public String toString()
282      {
283        return EnumerationUtility.getSharedInstance().enumToString(this);
284      }
285      
286      /**
287       * Returns the velocity convention represented by {@code text}.
288       * <p>
289       * For details about the transformation, see
290       * {@link EnumerationUtility#enumFromString(Class, String)}.</p>
291       * 
292       * @param text a text representation of a velocity convention.
293       * 
294       * @return the velocity convention represented by {@code text}.
295       */
296      public static VelocityConvention fromString(String text)
297      {
298        return EnumerationUtility.getSharedInstance()
299                                 .enumFromString(VelocityConvention.class, text);
300      }
301    }