001    package edu.nrao.sss.astronomy;
002    
003    import java.util.Date;
004    
005    import edu.nrao.sss.geom.EarthPosition;
006    import edu.nrao.sss.measure.Frequency;
007    import edu.nrao.sss.measure.LinearVelocity;
008    import edu.nrao.sss.measure.LinearVelocityUnits;
009    
010    /**
011     * A calculator of frequency shifts due to the motion of a point on
012     * earth relative to a celestial source.
013     * <p>
014     * <b>Version Info:</b>
015     * <table style="margin-left:2em">
016     *   <tr><td>$Revision: 1994 $</td></tr>
017     *   <tr><td>$Date: 2009-02-23 09:03:44 -0700 (Mon, 23 Feb 2009) $</td></tr>
018     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
019     * </table></p>
020     * 
021     * @author David M. Harland
022     * @since 2008-09-09
023     */
024    public class DopplerTracker
025    {
026      private EarthPosition      observer;
027      private SkyPosition        skyPosition;
028      private LinearVelocity     sourceVelocity;
029      private VelocityFrame      sourceFrame;
030      private VelocityConvention velocityConvention;
031      
032      private SpectralLine helper;
033      
034      /**
035       * Creates a new Doppler tracker with the given parameters.
036       * 
037       * @param observer
038       *   a point on earth whose velocity relative to a celestial source
039       *   will be used for calculating Doppler frequency shifts.
040       *   See {@link #setObserver(EarthPosition)} for handling of
041       *   a <i>null</i> parameter value.
042       *   
043       * @param skyPosition
044       *   a point on the celestial sphere where a signal emitting source sits.
045       *   See {@link #setSkyPosition(SkyPosition)} for handling of
046       *   a <i>null</i> parameter value.
047       * 
048       * @param sourceVelocity
049       *   the velocity of the source at {@code skyPosition} as measured
050       *   against the rest frame given by {@code sourceFrame}.
051       *   See {@link #setSourceVelocity(LinearVelocity)} for handling of
052       *   a <i>null</i> parameter value.
053       * 
054       * @param sourceFrame
055       *   the rest frame against which the {@code sourceVelocity} is measured.
056       *   See {@link #setVelocityFrame(VelocityFrame)} for handling of
057       *   a <i>null</i> parameter value.
058       * 
059       * @param velocityConvention
060       *   the velocity convention used for calculating shifted frequencies.
061       *   See {@link #setVelocityConvention(VelocityConvention)} for handling of
062       *   a <i>null</i> parameter value.
063       */
064      public DopplerTracker(EarthPosition      observer,       SkyPosition   skyPosition,
065                            LinearVelocity     sourceVelocity, VelocityFrame sourceFrame,
066                            VelocityConvention velocityConvention)
067      {
068        setObserver(observer);
069        setSkyPosition(skyPosition);
070        setSourceVelocity(sourceVelocity);
071        setVelocityFrame(sourceFrame);
072        setVelocityConvention(velocityConvention);
073        
074        helper = new SpectralLine();
075      }
076      
077      /**
078       * Creates a new Doppler tracker where the observer, sky position, and
079       * velocity are all non-null default values.
080       */
081      public DopplerTracker()
082      {
083        this(null, null, null, null, null);
084      }
085      
086      /**
087       * Creates a new Doppler tracker for the given earth-bound observer.
088       * The sky position will be a non-null default value, and the
089       * velocity of the source will be zero.
090       * 
091       * @param observer
092       *   a point on earth whose velocity relative to a celestial source
093       *   will be used for calculating Doppler frequency shifts.
094       *   See {@link #setObserver(EarthPosition)} for handling of
095       *   a <i>null</i> parameter value.
096       */
097      public DopplerTracker(EarthPosition observer)
098      {
099        this(observer, null, null, null, null);
100      }
101      
102      /**
103       * Creates a new Doppler tracker for the given observer and celestial
104       * sky position.  The velocity of the source at that position will
105       * be zero.
106       * 
107       * @param observer
108       *   a point on earth whose velocity relative to a celestial source
109       *   will be used for calculating Doppler frequency shifts.
110       *   See {@link #setObserver(EarthPosition)} for handling of
111       *   a <i>null</i> parameter value.
112       *   
113       * @param skyPosition
114       *   a point on the celestial sphere where a signal emitting source sits.
115       *   See {@link #setSkyPosition(SkyPosition)} for handling of
116       *   a <i>null</i> parameter value.
117       */
118      public DopplerTracker(EarthPosition observer, SkyPosition skyPosition)
119      {
120        this(observer, skyPosition, null, null, null);
121      }
122    
123      //============================================================================
124      // CONFIGURATION SETTING
125      //============================================================================
126    
127      /**
128       * Sets the position on earth whose velocity will be measured relative
129       * to a celestial source.
130       * 
131       * @param newObserver
132       *   a new point on earth.
133       *   If this value is <i>null</i> it will be replaced by
134       *   a newly created <tt>EarthPosition</tt> object.
135       */
136      public final void setObserver(EarthPosition newObserver)
137      {
138        observer = (newObserver == null) ? new EarthPosition() : newObserver;
139      }
140    
141      /**
142       * Sets the point on the celestial sphere against which the observer's
143       * velocity will be measured.
144       * 
145       * @param newPosition
146       *   a new point on the celestial sphere.
147       *   If this value is <i>null</i> it will be replaced by
148       *   a newly created <tt>SimpleSkyPosition</tt> object.
149       */
150      public final void setSkyPosition(SkyPosition newPosition)
151      {
152        skyPosition = (newPosition == null) ? new SimpleSkyPosition() : newPosition;
153      }
154    
155      /**
156       * Sets the rest frame against which velocity values will be calculated.
157       * 
158       * @param newFrame
159       *   a new rest frame.
160       *   If this value is <i>null</i> it will be replaced by
161       *   <tt>VelocityFrame.TOPOCENTRIC</tt>.
162       */
163      public final void setVelocityFrame(VelocityFrame newFrame)
164      {
165        sourceFrame = (newFrame == null) ? VelocityFrame.TOPOCENTRIC : newFrame;
166      }
167    
168      /**
169       * Sets the velocity convention to use when shifting frequencies.
170       * 
171       * @param newConvention
172       *   a new velocity convention.
173       *   This value is permitted to be <i>null</i>.  To see how this
174       *   affects calculations see
175       *   {@link SpectralLine#getShiftedFrequency(LinearVelocity, VelocityConvention)}.
176       */
177      public final void setVelocityConvention(VelocityConvention newConvention)
178      {
179        //OK for this to be null
180        velocityConvention = newConvention;
181      }
182    
183      /**
184       * Sets the velocity of the celestial source, relative to the rest frame
185       * held by this object.
186       *  
187       * @param newVelocity
188       *   a new velocity for a celestial source.
189       *   If this value is <i>null</i> it will be replaced by
190       *   a newly created <tt>LinearVelocity</tt> object.
191       */
192      public final void setSourceVelocity(LinearVelocity newVelocity)
193      {
194        sourceVelocity = (newVelocity == null) ? new LinearVelocity() : newVelocity;
195      }
196      
197      /**
198       * A convenience method for setting the source velocity and frame.
199       * 
200       * @param newVelocity
201       *   see {@link #setSourceVelocity(LinearVelocity)}.
202       *   
203       * @param newFrame
204       *   see {@link #setVelocityFrame(VelocityFrame)}.
205       * 
206       * @param newConvention
207       *   see {@link #setVelocityConvention(VelocityConvention)}.
208       */
209      public void setSourceVelocity(LinearVelocity     newVelocity,
210                                    VelocityFrame      newFrame,
211                                    VelocityConvention newConvention)
212      {
213        setSourceVelocity(newVelocity);
214        setVelocityFrame(newFrame);
215        setVelocityConvention(newConvention);
216      }
217      
218      /**
219       * A convenience method for setting the position and velocity of a celestial
220       * source.
221       * 
222       * @param newPosition
223       *   see {@link #setSkyPosition(SkyPosition)}.
224       *   
225       * @param newVelocity
226       *   see {@link #setSourceVelocity(LinearVelocity)}.
227       *   
228       * @param newFrame
229       *   see {@link #setVelocityFrame(VelocityFrame)}.
230       * 
231       * @param newConvention
232       *   see {@link #setVelocityConvention(VelocityConvention)}.
233       */
234      public void setSource(SkyPosition   newPosition, LinearVelocity     newVelocity,
235                            VelocityFrame newFrame,    VelocityConvention newConvention)
236      {
237        setSkyPosition(newPosition);
238        setSourceVelocity(newVelocity);
239        setVelocityFrame(newFrame);
240        setVelocityConvention(newConvention);
241      }
242      
243      //============================================================================
244      // CONFIGURATION QUERIES
245      //============================================================================
246    
247      /**
248       * Returns the position on earth whose velocity will be measured relative
249       * to a celestial source.
250       * This is either the position provided by a client object
251       * or a default position if none was provided.
252       * 
253       * @return
254       *   the earth position used in the Doppler calculations.
255       */
256      public EarthPosition getObserver()
257      {
258        return observer;
259      }
260    
261      /**
262       * Returns the point on the celestial sphere against which the observer's
263       * velocity will be measured.
264       * This is either the position provided by a client object
265       * or a default position if none was provided.
266       * 
267       * @return
268       *   the sky position used in the Doppler calculations.
269       */
270      public SkyPosition getSkyPosition()
271      {
272        return skyPosition;
273      }
274    
275      /**
276       * Returns the rest frame against which velocity values will be calculated.
277       * This is either the frame provided by a client object
278       * or a default frame if none was provided.
279       * 
280       * @return
281       *   the frame of rest used in the Doppler calculations.
282       */
283      public VelocityFrame getVelocityFrame()
284      {
285        return sourceFrame;
286      }
287    
288      /**
289       * Returns the velocity convention used when shifting frequencies.
290       * This is either the convention provided by a client object
291       * or is based on the units of the velocity if none was provided.
292       * 
293       * @return
294       *   the velocity convention used in the Doppler calculations.
295       */
296      public VelocityConvention getVelocityConvention()
297      {
298        if (velocityConvention != null)
299          return velocityConvention;
300                                          
301        return
302          sourceVelocity.getUnits().equals(LinearVelocityUnits.Z) ?
303            VelocityConvention.REDSHIFT : VelocityConvention.RADIO;
304      }
305    
306      /**
307       * Returns the velocity of the celestial source, relative to the rest frame
308       * held by this object.
309       * This is either the velocity provided by a client object
310       * or a default velocity if none was provided.
311       *  
312       * @return
313       *   the source velocity used in the Doppler calculations.
314       * 
315       * @see #calculateRelativeVelocity(Date)
316       */
317      public LinearVelocity getSourceVelocity()
318      {
319        return sourceVelocity;
320      }
321    
322      //============================================================================
323      // CALCULATIONS
324      //============================================================================
325      
326      /**
327       * Calculates and returns the relative velocity between the earth-bound
328       * observer and celestial source held by this tracker.
329       * A positive velocity means the observer and signal source are moving
330       * <i>away</i> from each other.
331       * 
332       * @param dateTime
333       *   the point in time for which the calculation is performed.
334       *   
335       * @return
336       *   the relative velocity between observer and signal source.
337       *   
338       * @throws CoordinateConversionException
339       *   
340       */
341      public LinearVelocity calculateRelativeVelocity(Date dateTime)
342        throws CoordinateConversionException
343      {
344        LinearVelocity observerAwayFromSkyPos =
345          observer.calcVelocityRelativeTo(skyPosition, dateTime, sourceFrame);
346        
347        return observerAwayFromSkyPos.add(sourceVelocity);
348      }
349    
350      /**
351       * Returns a shifted frequency based on {@code restFreq} and the relative
352       * velocity of the observer to the emitter at {@code dateTime}.
353       * 
354       * @param restFreq
355       *   a frequency as emitted by a source for which a shifted received
356       *   frequency is desired.
357       *   
358       * @param dateTime
359       *   the point in time for which the relative velocity calculation is
360       *   performed.
361       *   
362       * @return
363       *   a Doppler shifted frequency.
364       *   
365       * @throws CoordinateConversionException
366       *   
367       */
368      public Frequency calculateShiftedFrequency(Frequency restFreq, Date dateTime)
369        throws CoordinateConversionException
370      {
371        helper.setFrequency(restFreq);
372        
373        return helper.getShiftedFrequency(calculateRelativeVelocity(dateTime),
374                                          getVelocityConvention());
375      }
376    }