001    package edu.nrao.sss.model.resource.evla;
002    
003    import static edu.nrao.sss.measure.TimeUnits.MICROSECOND;
004    import static edu.nrao.sss.measure.TimeUnits.SECOND;
005    
006    import java.math.BigDecimal;
007    import java.math.RoundingMode;
008    
009    import javax.xml.bind.annotation.XmlAttribute;
010    
011    import ca.nrc.widar.jaxb.vci.BlbProdIntegration;
012    import edu.nrao.sss.math.MathUtil;
013    import edu.nrao.sss.measure.TimeDuration;
014    
015    /**
016     * Components of integration time for WIDAR correlation products.
017     * <p>
018     * The VCI element to which this class is mapped is
019     * {@link ca.nrc.widar.jaxb.vci.BlbProdIntegration}.
020     * </p>
021     * <p>
022     * <b>Version Info:</b>
023     * <table style="margin-left:2em">
024     *   <tr><td>$Revision: 2133 $</td></tr>
025     *   <tr><td>$Date: 2009-03-20 14:24:28 -0600 (Fri, 20 Mar 2009) $</td></tr>
026     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
027     * </table></p>
028     * 
029     * @author David M. Harland
030     * @since 2008-11-26
031     */
032    public class WidarIntegrationTime
033    {
034      private static final BigDecimal HW_NO_RECIRC_IDEAL_uS = new BigDecimal("400.0");
035      private static final BigDecimal HW_RECIRC_IDEAL_uS    = new BigDecimal("200.0");
036      private static final BigDecimal LTA_IDEAL_uS          = new BigDecimal("1000000.0"); //one second
037    
038      private WidarCorrelationProductGroup group;
039      
040      private TimeDuration minHwIntTime;
041      private int          multCorrChip;
042      private int          multLTA;
043      private int          multCBE;
044      
045      WidarIntegrationTime(WidarCorrelationProductGroup container)
046      {
047        if (container == null)
048          throw new IllegalArgumentException(
049            "WidarIntegrationTime may not be created for NULL group.");
050    
051        group = container;
052    
053        init();
054        setTotalIntegrationTime(new TimeDuration("1.0", SECOND));
055      }
056      
057      //Used by copy mechanism, and also by things like jaxb & hibernate
058      private WidarIntegrationTime()  { init(); }
059      
060      private void init()
061      {
062        minHwIntTime = new TimeDuration("200.0", MICROSECOND);
063      }
064    
065      //============================================================================
066      // MINIMUM HARDWARE INTEGRATION TIME
067      //============================================================================
068    
069      /**
070       * Returns the smallest legal value for the concept called
071       * the <i>minimum hardware integration time</i>.
072       * 
073       * @return
074       *   the smallest legal value for the minimum hardware integration time.
075       */
076      public TimeDuration getSmallestMinimumHardwareIntegrationTime()
077      {
078        //TODO temp code below. See table 4-18, VCI ver 3.3, p74.
079        //     Missing from below is determination of # of CCCs dumped
080        //     per CC per LTA frame.
081        int ccc = 16;
082        
083        BigDecimal microSec = new BigDecimal("2.5");
084        if (ccc <= 1)
085        {
086          if (multLTA > 1)
087            microSec = new BigDecimal("15");
088        }
089        else //ccc > 1
090        {
091          if (multLTA > 1)
092            microSec = new BigDecimal("200");
093          else
094            microSec = microSec.multiply(new BigDecimal(ccc));
095        }
096    
097        return new TimeDuration(microSec, MICROSECOND);
098      }
099    
100      /**
101       * Returns the largest legal value for the concept called
102       * the <i>minimum hardware integration time</i>.
103       * 
104       * @return
105       *   the largest legal value for the minimum hardware integration time.
106       */
107      public TimeDuration getLargestMinimumHardwareIntegrationTime()
108      {
109        return new TimeDuration("400.0", MICROSECOND);
110      }
111    
112      private static BigDecimal TIME_MULT =
113        new BigDecimal("64000000.0", MathUtil.MC_INTERM_CALCS);
114    
115      /**
116       * Sets the minimum hardware integration time for this group of
117       * correlation products.
118       * <p>
119       * The minimum hardware integration time can take on only certain
120       * values.  The maximum value is 400&micro;s.  The minimum value
121       * depends on the number of correlator chip cells per correlator
122       * chip that are being dumped, and also depends on the value of
123       * the LTA multiplier.  Values in between these limits must be
124       * equal to N / 64MHz, where N is some integer.</p> 
125       * 
126       * @param minHwTime
127       *   the new minimum hardware integration time for this group of
128       *   correlation products.
129       *   
130       * @see #getLargestMinimumHardwareIntegrationTime()
131       * @see #getSmallestMinimumHardwareIntegrationTime()
132       */
133      public void setMinimumHardwareIntegrationTime(TimeDuration minHwTime)
134      {
135        //Time must be N/64MHz, where N is an integer
136        BigDecimal N = minHwTime.toUnits(SECOND).multiply(TIME_MULT);
137        N = N.setScale(0, RoundingMode.HALF_UP);
138        BigDecimal seconds = N.divide(TIME_MULT, MathUtil.MC_FINAL_CALC);
139    
140        minHwIntTime.set(seconds, SECOND);
141    
142        //Test against min/max
143        TimeDuration min = getSmallestMinimumHardwareIntegrationTime();
144        if (minHwIntTime.compareTo(min) < 0)
145        {
146          minHwIntTime = min;
147        }
148        else
149        {
150          TimeDuration max = getLargestMinimumHardwareIntegrationTime();
151          if (minHwIntTime.compareTo(max) > 0)
152            minHwIntTime = max;
153          else
154            minHwIntTime.convertTo(MICROSECOND);
155        }
156      }
157    
158      /**
159       * Returns the minimum hardware integration time setting for this group of
160       * correlation products.
161       * The returned value is a copy of the one held internally, so changes
162       * made to it will not be reflected in this object.
163       * 
164       * @return
165       *   the minimum hardware integration time setting for this group of
166       *   correlation products.
167       */
168      public TimeDuration getMinimumHardwareIntegrationTime()
169      {
170        return minHwIntTime.clone();
171      }
172      
173      //============================================================================
174      // MULTIPLIERS
175      //============================================================================
176    
177      /**
178       * Sets the correlator chip multiplier component of this integration time.
179       * @param newMult
180       *   the new correlator chip multiplier value.
181       */
182      @XmlAttribute(name="ccMult", required=true)
183      public void setCorrelatorChipMultiplier(int newMult)
184      {
185        if (newMult < 1)
186          throw new IllegalArgumentException("Illegal corr chip mult value: " +
187                                             newMult + ".  Must be >= 1.");
188        //TODO make sure LTA time is legal. Suggested=1.0s; max ~ 16.7s
189        
190        multCorrChip = newMult;
191      }
192      
193      
194      /**
195       * Returns the correlator chip multiplier component of this integration time.
196       * @return the correlator chip multiplier component of this integration time.
197       * @see #getTotalIntegrationTime()
198       */
199      public int getCorrelatorChipMultiplier()
200      {
201        return multCorrChip;
202      }
203    
204      /**
205       * Sets the correlator back end multiplier component of this integration time.
206       * @param newMult
207       *   the new CBE multiplier value.
208       */
209      @XmlAttribute(name="cbeMult", required=true)
210      public void setBackEndMultiplier(int newMult)
211      {
212        if (newMult < 1)
213          throw new IllegalArgumentException("Illegal CBE mult value: " +
214                                             newMult + ".  Must be >= 1.");
215        multCBE = newMult;
216      }
217      
218      /**
219       * Returns the correlator back end multiplier component of this integration time.
220       * @return the CBE multiplier component of this integration time.
221       * @see #getTotalIntegrationTime()
222       */
223      public int getBackEndMultiplier()
224      {
225        return multCBE;
226      }
227    
228      /**
229       * Sets the long term accumulator multiplier component of this integration time.
230       * @param newMult
231       *   the new LTA multiplier value.
232       */
233      @XmlAttribute(name="ltaMult", required=true)
234      public void setLongTermAccumulatorMultiplier(int newMult)
235      {
236        if (newMult < 1)
237          throw new IllegalArgumentException("Illegal LTA mult value: " +
238                                             newMult + ".  Must be >= 1.");
239        //TODO make sure LTA time is legal. Suggested=1.0s; max ~ 16.7s
240        
241        multLTA = newMult;
242      }
243      
244      /**
245       * Returns the long term accumulator multiplier component of this integration time.
246       * @return the LTA multiplier component of this integration time.
247       * @see #getTotalIntegrationTime()
248       */
249      public int getLongTermAccumulatorMultiplier()
250      {
251        return multLTA;
252      }
253      
254      //============================================================================
255      // TOTAL INTEGRATION TIME
256      //============================================================================
257    
258      /**
259       * Returns the smallest total integration time that may be used for the
260       * containing group of correlation products.
261       * 
262       * @return
263       *   smallest allowable total integration time.
264       */
265      public TimeDuration getMinimumTotalIntegrationTime()
266      {
267        return getSmallestMinimumHardwareIntegrationTime();
268      }
269    
270      /**
271       * Returns the total integration time for the
272       * containing group of correlation products.
273       * <p>
274       * The total integration time equals:</p>
275       * <pre>
276       *   {@link #getMinimumHardwareIntegrationTime()} *
277       *   {@link #getCorrelatorChipMultiplier()} *
278       *   {@link #getLongTermAccumulatorMultiplier()} *
279       *   {@link #getBackEndMultiplier()}
280       * </pre>
281       * <p>
282       * The returned duration is <i>not</i> held internally by this object,
283       * so changes made to it will not be reflected herein.</p>
284       * 
285       * @return
286       *   total integration time.
287       */
288      public TimeDuration getTotalIntegrationTime()
289      {
290        TimeDuration intTime = getMinimumHardwareIntegrationTime(); //a clone
291        
292        intTime.multiplyBy(new BigDecimal(multCorrChip));
293        intTime.multiplyBy(new BigDecimal(multLTA));
294        intTime.multiplyBy(new BigDecimal(multCBE));
295        
296        return intTime;
297      }
298      
299      /**
300       * Sets the total integration time for the
301       * containing group of correlation products.
302       * 
303       * @param totalDuration
304       *   the new total integration time.
305       *   If this value is <i>null</i> this method does nothing.
306       *   
307       * @see #getTotalIntegrationTime()
308       */
309      void setTotalIntegrationTime(TimeDuration totalDuration)
310      {
311        if (totalDuration == null)
312          return;
313        
314        //We assume here that we are NOT free to change the group's
315        //minimum hardware integration time.
316        //TODO examine above assumption
317        
318        BigDecimal desiredTotalMicroseconds = totalDuration.toUnits(MICROSECOND);
319        
320        BigDecimal minHwMs = getMinimumHardwareIntegrationTime().toUnits(MICROSECOND);
321        
322        //Set up the CC factor.  We'll get as close as we can w/out going over ideal.
323        int rf = group.getRecirculationFactor();
324        
325        BigDecimal idealCcMs = (rf == 1) ? HW_NO_RECIRC_IDEAL_uS : HW_RECIRC_IDEAL_uS;
326        
327        BigDecimal goal = idealCcMs.compareTo(desiredTotalMicroseconds) <= 0 ?
328                          idealCcMs : desiredTotalMicroseconds; //Taking lesser
329        
330        int factor = goal.divide(minHwMs, RoundingMode.HALF_UP).intValue();
331        
332        if (factor < 1)
333          factor = 1;
334        
335        multCorrChip = factor;
336            
337        //Set up the ideal LTA factor.  We'll get as close as we can w/out going over.
338        BigDecimal ccMs = minHwMs.multiply(new BigDecimal(factor));
339        
340        boolean done = ccMs.compareTo(desiredTotalMicroseconds) >= 0;
341        
342        if (!done)
343        {
344          goal = LTA_IDEAL_uS.compareTo(desiredTotalMicroseconds) <= 0 ?
345                 LTA_IDEAL_uS : desiredTotalMicroseconds; //Taking lesser
346          
347          factor = goal.divide(ccMs, RoundingMode.HALF_UP).intValue();
348          
349          if (factor < 1)
350            factor = 1;
351        }
352        else
353        {
354          factor = 1;
355        }
356    
357        multLTA = factor;
358        
359        //If we haven't reached the goal, put rest in CBE. Come as close as possible.
360        BigDecimal ltaMs = null;
361        
362        if (!done) //Ie, if CC did not eat up everything, see if LTA did
363        {
364          ltaMs = ccMs.multiply(new BigDecimal(factor));
365        
366          done = ltaMs.compareTo(desiredTotalMicroseconds) >= 0;
367        }
368        
369        if (!done) //Ie, if LTA did not eat up everything, put rest in CBE
370        {
371          goal = desiredTotalMicroseconds;
372          
373          factor = (int)Math.round(goal.divide(ltaMs, RoundingMode.HALF_UP).doubleValue());
374          
375          if (factor < 1)
376            factor = 1;
377        }
378        else
379        {
380          factor = 1;
381        }
382        
383        //TODO cut CBE factor down by subband.recircFactor?
384    
385        multCBE = factor;
386      }
387    
388      //============================================================================
389      // VCI
390      //============================================================================
391      
392      /**
393       * Returns a <i>Virtual Correlator Interface</i> (VCI) representation of this
394       * object.
395       * 
396       * @return
397       *   a VCI object built from this one.
398       */
399      public BlbProdIntegration toVci()
400      {
401        BlbProdIntegration vciObj = new BlbProdIntegration();
402        
403        vciObj.setMinIntegTime(minHwIntTime.toUnits(MICROSECOND).doubleValue());
404        vciObj.setCcIntegFactor(multCorrChip);
405        vciObj.setLtaIntegFactor(multLTA);
406        vciObj.setCbeIntegFactor(multCBE);
407        
408        vciObj.setRecirculation(group.getRecirculationFactor());
409        
410        return vciObj;
411      }
412      
413      //============================================================================
414      // 
415      //============================================================================
416      
417      /**
418       * Creates a copy of this object.
419       * The new copy is identical to this one, except:
420       * <ol>
421       *   <li>Its group is {@code otherGroup}.</li>
422       * </ol>
423       * Note that this method does <i>not</i> tell {@code otherGroup} about
424       * the new object.  The link forged in this method is one-way; clients
425       * are responsible for the link in the other direction.
426       */
427      WidarIntegrationTime makeCopyFor(WidarCorrelationProductGroup otherGroup)
428      {
429        WidarIntegrationTime newTiming = new WidarIntegrationTime();
430        
431        newTiming.group = otherGroup;
432        
433        newTiming.minHwIntTime = this.minHwIntTime.clone();
434        newTiming.multCorrChip = this.multCorrChip;
435        newTiming.multLTA      = this.multLTA;
436        newTiming.multCBE      = this.multCBE;
437        
438        return newTiming;
439      }
440      
441      /** Returns <i>true</i> if <tt>o</tt> is equal to this object. */
442      @Override
443      public boolean equals(Object o)
444      {
445        //Quick exit if o is null
446        if (o == null)
447          return false;
448        
449        //Quick exit if o is this
450        if (o == this)
451          return true;
452        
453        //Quick exit if classes are different
454        if (!o.getClass().equals(this.getClass()))
455          return false;
456        
457        WidarIntegrationTime other = (WidarIntegrationTime)o;
458    
459        if (other.multCBE      != this.multCBE      ||
460            other.multCorrChip != this.multCorrChip ||
461            other.multLTA      != this.multLTA)
462          return false;
463          
464        return other.minHwIntTime.equals(this.minHwIntTime);
465      }
466    
467      /** Returns a hash code for this object. */
468      @Override
469      public int hashCode()
470      {
471        //Taken from the Effective Java book by Joshua Bloch.
472        //The constants 17 & 37 are arbitrary & carry no meaning.
473        int result = 17;
474        
475        //You MUST keep this method in sync w/ the equals method
476        
477        result = 37 * result + multCBE;
478        result = 37 * result + multCorrChip;
479        result = 37 * result + multLTA;
480        result = 37 * result + minHwIntTime.hashCode();
481        
482        return result;
483      }
484    }