001    package edu.nrao.sss.model.project.scan;
002    
003    import java.util.ArrayList;
004    import java.util.Collections;
005    import java.util.List;
006    
007    import javax.xml.bind.annotation.XmlElement;
008    import javax.xml.bind.annotation.XmlElementWrapper;
009    import javax.xml.bind.annotation.XmlRootElement;
010    
011    import edu.nrao.sss.measure.TimeDuration;
012    
013    /**
014     * A scan that is used to determine the timing delays needed to synchronize
015     * the incoming signals for antennas in an array.
016     * <p>
017     * <b>Version Info:</b>
018     * <table style="margin-left:2em">
019     *   <tr><td>$Revision: 931 $</td></tr>
020     *   <tr><td>$Date: 2007-09-28 14:41:38 -0600 (Fri, 28 Sep 2007) $</td></tr>
021     *   <tr><td>$Author: dharland $</td></tr>
022     * </table></p>
023     *  
024     * @author David M. Harland
025     * @since 2006-07-18
026     */
027    @XmlRootElement
028    public class DelayScan
029      extends Scan
030    {
031      private List<DelaySetting> delays;
032      
033      /** Creates a new instance. */
034      DelayScan()
035      {
036        super();
037        
038        delays = new ArrayList<DelaySetting>();
039      }
040    
041      /**
042       * Sets the collection of delay settings held by this scan.
043       * A <i>null</i> {@code replacementSet} will be interpreted
044       * as a new empty list.
045       * <p>
046       * This scan will hold a reference to {@code replacementSet}
047       * (unless it is <i>null</i>), so any changes made to the list
048       * after calling this method will be reflected in this object.</p>
049       * 
050       * @param replacementSet a set of delay settings to be held by this scan.
051       */
052      public void setDelays(List<DelaySetting> replacementSet)
053      {
054        delays = (replacementSet == null) ? new ArrayList<DelaySetting>()
055                                          : replacementSet;
056      }
057    
058      /**
059       * Returns a set of delay settings.
060       * <p>
061       * The returned set, which is guaranteed to be non-null,
062       * is the actual set held by this scan,
063       * so changes made to the set will be reflected in this
064       * object.</p>
065       * 
066       * @return a set of delay settings.
067       */
068      @XmlElementWrapper
069      @XmlElement(name="delaySetting")
070      public List<DelaySetting> getDelays()
071      {
072        return delays;
073      }
074    
075      /**
076       * Sorts the internal list of delays and returns it.
077       * <p>
078       * The returned list is the actual list held by this scan,
079       * so changes made to the list will be reflected in this
080       * object.</p>
081       * 
082       * @return the list of delays settings held by this scan, after they have
083       *         been sorted.
084       */
085      public List<DelaySetting> sortDelays()
086      {
087        Collections.sort(delays);
088        return delays;
089      }
090    
091      /**
092       * Adds a series of delays to this scan's list.
093       * The number of new delays added is equal to
094       * <tt>1 + 2 * numberOfPositiveDelays</tt>.
095       * If we call the <tt>numberOfPositiveDelays</tt> "<tt>n</tt>"
096       * and the <tt>incrementalNanosecondDelay</tt> "<tt>t</tt>", delays
097       * of the following durations will be added to this scan:
098       * <p>
099       * <tt>&nbsp;&nbsp;-nt,...,-2t,-t,0,+t,+2t,...,+nt</tt>.</p>
100       * <p>
101       * Note that these delays are relative to the current value of the delay
102       * and are, therefore, allowed to be negative.</p>
103       * 
104       * @param numberOfPositiveDelays
105       *   the number of positive delays to be added.  The total number of delays
106       *   added is equal to one plus two times this amount.  An {@code
107       *   IllegalArgumentException} will be thrown if this value is negative.
108       *   
109       * @param incrementalNanosecondDelay
110       *   the spacing between the delays added by this method.
111       *   An {@code IllegalArgumentException} will be
112       *   thrown if this value less than or equal to zero.
113       *   
114       * @param timeAtSetting
115       *   the amount of time the delay should be placed at this setting.
116       *   
117       * @throws IllegalArgumentException
118       *   if any of the parameters violate the rules listed above.
119       */
120      public void addDelays(int          numberOfPositiveDelays,
121                            double       incrementalNanosecondDelay,
122                            TimeDuration timeAtSetting)
123      {
124        validateParameters(numberOfPositiveDelays,
125                           incrementalNanosecondDelay, timeAtSetting);
126        
127        double ns = -numberOfPositiveDelays * incrementalNanosecondDelay;
128    
129        int offsetCount = 1 + 2 * numberOfPositiveDelays;
130    
131        for (int i=1; i <= offsetCount; i++)
132        {
133          DelaySetting setting = new DelaySetting();
134          
135          setting.setNanoseconds(ns);
136          setting.getTimeAtSetting().set(timeAtSetting);
137          
138          delays.add(setting);
139          
140          ns += incrementalNanosecondDelay;
141        }
142      }
143      
144      /**
145       * Adds a series of delays to this scan's list.
146       * The number of new delays added is {@code numberOfDelays}.
147       * The smallest new delay is {@code minNanosecondDelay}, the largest is
148       * {@code maxNanosecondDelay}.  The other delays are spread evenly between
149       * these two endpoints.
150       * <p>
151       * Note that these delays are relative to the current value of the delay
152       * and are, therefore, allowed to be negative.</p>
153       *  
154       * @param minNanosecondDelay
155       *   the smallest delay.  This value is often negative,
156       *   
157       * @param maxNanosecondDelay
158       *   the largest delay.  An {@code IllegalArgumentException} will be thrown if
159       *   this value is less than or equal to the {@code minNanosecondDelay}.
160       *   
161       * @param numberOfDelays
162       *    the number of new delays to be added.
163       *    Special cases:<ul>
164       *      <li>numberOfDelays < 1: No new delays will be added.</li>
165       *      <li>numberOfDelays == 1: Only the {@code minNanosecondDelay}
166       *                               will be added.</li>
167       *      <li>numberOfDelays == 2: Only the {@code minNanosecondDelay}
168       *                               and {@code maxNanosecondDelay}
169       *                               will be added.</li>
170       *    </ul>
171       *   
172       * @param timeAtSetting
173       *   the amount of time the delay should be placed at this setting.
174       *   
175       * @throws IllegalArgumentException
176       *   if any of the parameters violate the rules listed above.
177       */
178      public void addDelays(double minNanosecondDelay, double maxNanosecondDelay,
179                            int numberOfDelays, TimeDuration timeAtSetting)
180      {
181        validateParameters(minNanosecondDelay, maxNanosecondDelay,
182                           numberOfDelays,     timeAtSetting);
183        
184        //Special cases
185        if (numberOfDelays < 1)               //No action
186        {
187          return;
188        }
189        else if (numberOfDelays <= 2)         //Add only min (& possibly max)
190        {
191          DelaySetting minDelay = new DelaySetting();
192          minDelay.setNanoseconds(minNanosecondDelay);
193          minDelay.getTimeAtSetting().set(timeAtSetting);
194          delays.add(minDelay);
195          
196          if (numberOfDelays == 2)            //Add only min & max delays
197          {
198            DelaySetting maxDelay = new DelaySetting();
199            maxDelay.setNanoseconds(maxNanosecondDelay);
200            maxDelay.getTimeAtSetting().set(timeAtSetting);
201            delays.add(maxDelay);
202          }
203        }
204        //Normal logic
205        else
206        {
207          double ns        = minNanosecondDelay;
208          double increment = (maxNanosecondDelay - minNanosecondDelay) /
209                             (numberOfDelays - 1);
210          
211          for (int i=1; i <= numberOfDelays; i++)
212          {
213            DelaySetting setting = new DelaySetting();
214            
215            setting.getTimeAtSetting().set(timeAtSetting);
216            setting.setNanoseconds(ns);
217            
218            delays.add(setting);
219            
220            ns += increment;
221          }
222        }
223      }
224      
225      /** Helps addOffsets(...). */
226      private void validateParameters(int          numberOfPositiveDelays,
227                                      double       incrementalNanosecondDelay,
228                                      TimeDuration timeAtSetting)
229      {
230        final String errMsgStart = "Illegal value for ";
231        
232        if (numberOfPositiveDelays < 0)
233        {
234          StringBuilder errMsg = new StringBuilder(errMsgStart);
235          errMsg.append("numberOfPositiveDelays (")
236                .append(numberOfPositiveDelays)
237                .append("): value may not be negative.");
238          throw new IllegalArgumentException(errMsg.toString());
239        }
240        
241        if (incrementalNanosecondDelay <= 0.0)
242        {
243          StringBuilder errMsg = new StringBuilder(errMsgStart);
244          errMsg.append("incrementalNanosecondDelay (")
245                .append(incrementalNanosecondDelay)
246                .append("): value must be positive.");
247          throw new IllegalArgumentException(errMsg.toString());
248        }
249      }
250    
251      /** Helps addOffsets(...). */
252      private void validateParameters(double       minNanosecondDelay,
253                                      double       maxNanosecondDelay,
254                                      int          numberOfOffsets,
255                                      TimeDuration timeAtOffset)
256      {
257        if (minNanosecondDelay >= maxNanosecondDelay)
258        {
259          StringBuilder errMsg = new StringBuilder("The minimum delay (");
260          errMsg.append(minNanosecondDelay)
261                .append(") must be less than the maximum delay (")
262                .append(maxNanosecondDelay).append(").");
263          throw new IllegalArgumentException(errMsg.toString());
264        }
265      }
266    
267      //============================================================================
268      // TEXT
269      //============================================================================
270    
271      /* (non-Javadoc)
272       * @see edu.nrao.sss.model.project.scan.ScanLoopElement#toString()
273       */
274      public String toSummaryString()
275      {
276        StringBuilder buff = new StringBuilder(super.toSummaryString());
277        
278        buff.append(", delayCnt=").append(delays.size());
279        
280        return buff.toString();
281      }
282    
283      //============================================================================
284      // 
285      //============================================================================
286    
287      /**
288       *  Returns a delay scan that is a copy of this one.
289       *  <p>
290       *  The returned scan is, for the most part, a deep copy of this one.
291       *  However, there are a few exceptions noted in the
292       *  {@link ScanLoop#clone() clone method} of this class's parent.</p>
293       *  <p>
294       *  If anything goes wrong during the cloning procedure,
295       *  a {@code RuntimeException} will be thrown.</p>
296       */
297      public DelayScan clone()
298      {
299        DelayScan clone = null;
300    
301        try
302        {
303          clone = (DelayScan)super.clone();
304          
305          //Need to clone set AND contained elements.
306          clone.delays = new ArrayList<DelaySetting>();
307          for (DelaySetting ds : this.delays)
308            clone.delays.add(ds.clone());
309        }
310        catch (Exception ex)
311        {
312          throw new RuntimeException(ex);
313        }
314        
315        return clone;
316      }
317    
318      /**
319       * Returns <i>true</i> if {@code o} is equal to this delay scan.
320       * <p>
321       * In order for {@code o} to be equal to this scan, it must have
322       * equal delay settings in the same order as those of this scan.
323       * It must also follow the rules set forth in the
324       * {@link ScanLoop#equals(Object) equals method} of this class's parent.</p>
325       */
326      public boolean equals(Object o)
327      {
328        //Quick exit if o is this object
329        if (o == this)
330          return true;
331        
332        //Not equal if super class says not equal
333        if (!super.equals(o))
334          return false;
335        
336        //Super class tested for Class equality, so cast is safe
337        DelayScan other = (DelayScan)o;
338        
339        //Compare attributes
340        return other.delays.equals(this.delays);
341      }
342    
343      /* (non-Javadoc)
344       * @see edu.nrao.sss.model.project.scan.Scan#hashCode()
345       */
346      public int hashCode()
347      {
348        //Taken from the Effective Java book by Joshua Bloch.
349        //The constant 37 is arbitrary & carries no meaning.
350        int result = 37 * super.hashCode();
351        
352        result = 37 * result + delays.hashCode();
353        
354        return result;
355      }
356    
357      //============================================================================
358      // 
359      //============================================================================
360      /*
361      public static void main(String[] args) throws Exception
362      {
363        DelayScan scan1 = new DelayScan();
364        DelayScan scan2 = new DelayScan();
365        
366        scan1.addDelays(5,
367                        1.5,
368                        new TimeDuration(5.0, edu.nrao.sss.measure.TimeUnits.MINUTE));
369        
370        scan2.addDelays(-7.5,
371                        +7.5,
372                        11,
373                        new TimeDuration(5.0, edu.nrao.sss.measure.TimeUnits.MINUTE));
374        
375        System.out.println("SCAN 1:");
376        for (DelaySetting setting : scan1.getDelays())
377          System.out.println("  " + setting);
378        
379        System.out.println();
380        
381        System.out.println("SCAN 2:");
382        for (DelaySetting setting : scan2.getDelays())
383          System.out.println("  " + setting);
384      }
385      */
386    }