001    package edu.nrao.sss.model.resource.evla;
002    
003    import java.io.FileNotFoundException;
004    import java.io.Reader;
005    import java.io.Writer;
006    import java.math.BigDecimal;
007    import java.util.List;
008    import java.util.SortedSet;
009    import java.util.TreeSet;
010    
011    import javax.xml.bind.JAXBException;
012    import javax.xml.bind.annotation.XmlAttribute;
013    import javax.xml.bind.annotation.XmlElement;
014    import javax.xml.bind.annotation.XmlElementWrapper;
015    import javax.xml.bind.annotation.XmlRootElement;
016    import javax.xml.bind.annotation.XmlTransient;
017    import javax.xml.stream.XMLStreamException;
018    
019    import static ca.nrc.widar.jaxb.vci.YesNoType.NO;
020    import static ca.nrc.widar.jaxb.vci.YesNoType.YES;
021    import static edu.nrao.sss.measure.FrequencyUnits.HERTZ;
022    
023    import ca.nrc.widar.jaxb.vci.AutoCorrMode;
024    import ca.nrc.widar.jaxb.vci.BurstMode;
025    import ca.nrc.widar.jaxb.vci.PhasedArray;
026    import ca.nrc.widar.jaxb.vci.PolProducts;
027    import ca.nrc.widar.jaxb.vci.PulsarBinning;
028    import ca.nrc.widar.jaxb.vci.RadarMode;
029    import ca.nrc.widar.jaxb.vci.SubBand;
030    import ca.nrc.widar.jaxb.vci.ToneExtraction;
031    import edu.nrao.sss.measure.Frequency;
032    import edu.nrao.sss.measure.FrequencyRange;
033    import edu.nrao.sss.model.resource.CorrelationProductGroupAbs;
034    import edu.nrao.sss.model.resource.CorrelatorBaseband;
035    import edu.nrao.sss.model.resource.CorrelatorSubband;
036    import edu.nrao.sss.model.resource.CorrelatorSubbandAbs;
037    import edu.nrao.sss.util.JaxbUtility;
038    
039    /**
040     * A subband of a WIDAR correlator baseband.
041     * <p>
042     * <b>Version Info:</b>
043     * <table style="margin-left:2em">
044     *   <tr><td>$Revision: 2291 $</td></tr>
045     *   <tr><td>$Date: 2009-05-08 11:09:46 -0600 (Fri, 08 May 2009) $</td></tr>
046     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
047     * </table></p>
048     * 
049     * @author David M. Harland
050     * @since 2008-03-11
051     */
052    @XmlRootElement
053    public class WidarSubband
054      extends CorrelatorSubbandAbs
055    {
056      private static final Frequency MIN_BW = new Frequency("31250.0", HERTZ);
057      
058      private static final Frequency MAX_BW_4_BIT_RQ = new Frequency("128000000.0", HERTZ);
059      private static final Frequency MAX_BW_7_BIT_RQ = new Frequency( "64000000.0", HERTZ);
060    
061      private SubbandChannelCalculator channelCalculator =
062        SubbandChannelCalculator.getInstance();
063      
064      /** Helps create a new WIDAR subband. */
065      public WidarSubband()
066      {
067        super();
068        setVciDefaults();
069        
070        Frequency center = WidarBaseband.SB_GRID_WIDTH.clone().divideBy("2.0");
071        freqRange.setCenterAndWidth(center, WidarBaseband.SB_GRID_WIDTH);
072        setRequantization(4);
073      }
074    
075      void setBaseband(WidarBaseband container)
076      {
077        setContainer(container);
078      }
079    
080      @Override
081      @XmlTransient
082      public WidarBaseband getBaseband()
083      {
084        return (WidarBaseband)super.getBaseband();
085      }
086      
087      //============================================================================
088      // FREQUENCY
089      //============================================================================
090      
091      /* (non-Javadoc)
092       * @see CorrelatorSubband#getMinimumBandwidth()
093       */
094      public Frequency getMinimumBandwidth()
095      {
096        //2008-09-03 According to M.Rupen the algorithm in ver 3.3 of the VCI
097        //document stating that the minimum is the greater of below and
098        //BB_BW/4096 is incorrect. 
099        return MIN_BW.clone();
100      }
101      
102      /* (non-Javadoc)
103       * @see CorrelatorSubband#getMaximumBandwidth()
104       */
105      public Frequency getMaximumBandwidth()
106      {
107        Frequency max = (requantizationBits == 4) ? MAX_BW_4_BIT_RQ.clone()
108                                                  : MAX_BW_7_BIT_RQ.clone();
109        CorrelatorBaseband bb = getContainer();
110        //max = lesser of MAX_BW_* and baseband's BW
111        if (bb != null)
112        {
113          Frequency bbBw = bb.getBandwidth(); //returns a clone
114          
115          if (bbBw.compareTo(max) < 0)
116            max = bbBw;
117        }
118        
119        return max;
120      }
121    
122      /* (non-Javadoc)
123       * @see CorrelatorSubband#hasDiscreteBandwidths()
124       */
125      public boolean hasDiscreteBandwidths()  { return true; }
126    
127      /* (non-Javadoc)
128       * @see CorrelatorSubband#getAllowableBandwidths()
129       */
130      public SortedSet<Frequency> getAllowableBandwidths()
131      {
132        SortedSet<Frequency> bws = new TreeSet<Frequency>();
133        
134        double minHz = getMinimumBandwidth().toUnits(HERTZ).doubleValue();
135        double maxHz = getMaximumBandwidth().toUnits(HERTZ).doubleValue();
136        
137        //Only non-negative powers-of-two multiples of min are allowed
138        for (double hertz = minHz; hertz <= maxHz; hertz *= 2.0)
139        {
140          bws.add(new Frequency(new BigDecimal(hertz), HERTZ).normalize());
141        }
142        
143        return bws;
144      }
145      
146      /**
147       * Sets the bandwidth of this subband.
148       * <p>
149       * In addition to doing the work described in the
150       * {@link CorrelatorSubband#setBandwidth(Frequency) method specifications},
151       * this WIDAR version also turns off the <i>stage 2 mixer</i>, if it
152       * happened to be on, and if the new width is 128 MHz.</p>
153       */
154      public void setBandwidth(Frequency newWidth)
155      {
156        super.setBandwidth(newWidth);
157    
158        //If we're not using the mixer, the super class's code already does correct thing.
159        //This code is here because super class things all frequencies are valid, but
160        //for widar we have the 128MHz boundaries.
161        if (useMixer)
162          keepWithinGridCell();
163        
164        //Turn off mixer if BW is 128MHz
165        if (useMixer && getBandwidth().equals(WidarBaseband.SB_GRID_WIDTH))
166          useMixer = false;
167      }
168    
169      /**
170       * Sets the central frequency of this subband.
171       * <p>
172       * In addition to the
173       * {@link CorrelatorSubband#setCentralFrequency(Frequency) side effects}
174       * mentioned in the specification of this method, this implementation
175       * will also snap the center frequency to an imaginary grid.
176       * If the <i>stage 2 filter</i> is not turned on, the center of this
177       * subband must be equal to <tt>BW / 2 + BW * n, n=0,1,2,...</tt>,
178       * where <tt>BW</tt> is the bandwidth of this subband.</p>
179       */
180      @Override
181      public void setCentralFrequency(Frequency newCenter)
182      {
183        if (newCenter == null)
184          throw new IllegalArgumentException(
185            "Cannot set center of subband to NULL.");
186    
187        //If the stage 2 mixer is off, the center of the subband must
188        //be in the center of a slot of an imaginary grid whose slots
189        //are the same width as the subband.
190        if (!useMixer)
191        {
192          BigDecimal mult = newCenter.dividedBy(getBandwidth());
193          
194          double goodMult = mult.intValue() + 0.5;
195          
196          newCenter = getBandwidth().multiplyBy(BigDecimal.valueOf(goodMult));
197        }
198        
199        super.setCentralFrequency(newCenter);
200    
201        //If we're not using the mixer, the super class's code already does correct thing.
202        //This code is here because super class things all frequencies are valid, but
203        //for widar we have the 128MHz boundaries.
204        if (useMixer)
205          keepWithinGridCell();
206      }
207      
208      /**
209       * Holds the bandwidth constant and moves center frequency if any part
210       * of this subband overlaps one of the boundary lines specified by the
211       * containing baseband.
212       */
213      private void keepWithinGridCell()
214      {
215        WidarBaseband bb = getBaseband();
216        
217        if (bb != null)
218        {
219          Frequency      sbCenter    = getCentralFrequency();
220          Frequency      sbHalfWidth = getBandwidth().divideBy("2.0");
221          FrequencyRange gridCell    = bb.getGridCellContaining(sbCenter);
222          
223          Frequency minCenter = gridCell.getLowFrequency().add(sbHalfWidth);
224          
225          if (sbCenter.compareTo(minCenter) < 0)
226          {
227            super.setCentralFrequency(minCenter);
228          }
229          else
230          {
231            Frequency maxCenter = gridCell.getHighFrequency().subtract(sbHalfWidth);
232            
233            if (sbCenter.compareTo(maxCenter) > 0)
234              super.setCentralFrequency(maxCenter);
235          }
236        }
237      }
238    
239      //============================================================================
240      // QUANTIZATION
241      //============================================================================
242      
243      /* (non-Javadoc)
244       * @see CorrelatorSubband#getAllowableRequantizations()
245       */
246      public SortedSet<Integer> getAllowableRequantizations()
247      {
248        SortedSet<Integer> allowable = new TreeSet<Integer>();
249        
250        allowable.add(4);
251        
252        if (getBandwidth().compareTo(MAX_BW_7_BIT_RQ) <= 0)
253          allowable.add(7);
254        
255        return allowable;
256      }
257      
258      /* (non-Javadoc)
259       * @see CorrelatorSubband#setRequantization(int)
260       */
261      public void setRequantization(int bits)
262      {
263        requantizationBits = bits <= 5 ? 4 : 7;  
264      }
265    
266      //============================================================================
267      // CHANNELS
268      //============================================================================
269      // TODO: at one time methods such as those below were part of the
270      //       CorrelatorSubband I/F.  Consider making them so again.
271      
272      public int getMaximumChannelCount()
273      {
274        //TODO BLBPs are now in a diff VCI element
275        return channelCalculator.getMaxChannels(this, 1 /*this.blbps.size()*/);
276      }
277      
278      public int getChannelIncrementSize()
279      {
280        //TODO recirculation is now in a diff VCI element
281        return channelCalculator.getChannelIncrement(  1  /*recirculation*/);
282      }
283    
284      //============================================================================
285      // CORRELATION PRODUCT GROUPS
286      //============================================================================
287    
288      @Override
289      protected WidarCorrelationProductGroup makeCorrelationProductGroup()
290      {
291        return new WidarCorrelationProductGroup(this);
292      }
293      
294      @XmlElementWrapper(name="correlationProductGroups")
295      @XmlElement(name="group")
296      @SuppressWarnings("unused")  //JAXB use
297      private void setWidarProdGroupList(WidarCorrelationProductGroup[] replacements)
298      {
299        productGroups.clear();
300        
301        for (WidarCorrelationProductGroup prodGrp : replacements)
302        {
303          prodGrp.setSubband(this);
304          productGroups.add(prodGrp);
305        }
306      }
307      
308      @SuppressWarnings("unused")  //JAXB use
309      private WidarCorrelationProductGroup[] getWidarProdGroupList()
310      {
311        return productGroups.toArray(new WidarCorrelationProductGroup[productGroups.size()]);
312      }
313      
314      //============================================================================
315      // VCI ELEMENTS
316      //============================================================================
317    
318      //VCI element SubBand
319      private AutoCorrMode   autoCorrMode;
320      private BurstMode      burstMode;
321      //                     bw          - part of this class already
322      //                     centralFreq - part of this class already
323      private boolean        mixerPhaseErrorCorr;
324      //                     name        - part of super class
325      private PhasedArray    phasedArray;
326      //                     polProducts - part of super class
327      private PulsarBinning  pulsarBinning;
328      private int            pulsarGatingPhase;
329      private RadarMode      radarMode;
330      //                     rqNumBits   - part of this class already
331      private int            sbId;
332      private int            signalToNoise;
333      private ToneExtraction toneExtraction;
334      private boolean        useMixer;
335      
336      //VCI element SbParams
337      //TODO model in this class?
338    
339      private void setVciDefaults()
340      {
341        sbId = 0;
342        
343        mixerPhaseErrorCorr    = true;
344        pulsarGatingPhase      = 0;
345        signalToNoise          = 0;
346        useMixer               = false;
347    
348        overrideAutoCorr       = false;
349        autoCorrMode           = null;
350        autoCorrCache          = VciJaxbUtil.makeAutoCorrMode();
351    
352        overrideBurstMode      = false;
353        burstMode              = null;
354        burstModeCache         = VciJaxbUtil.makeBurstMode();
355        
356        overridePhasedArray    = false;
357        phasedArray            = null;
358        phasedArrayCache       = VciJaxbUtil.makePhasedArray();
359        
360        overridePulsarBinning  = false;
361        pulsarBinning          = null;
362        pulsarBinningCache     = VciJaxbUtil.makePulsarBinning();
363        
364        overrideRadarMode      = false;
365        radarMode              = null;
366        radarModeCache         = new RadarMode();
367        
368        overrideToneExtraction = false;
369        toneExtraction         = null;
370        toneExtractionCache    = new ToneExtraction();
371      }
372      
373      /**
374       * Makes copies of the VCI elements of this subband for <tt>other</tt> subband.
375       */
376      private void copyVciElementsInto(WidarSubband other)
377      {
378        other.mixerPhaseErrorCorr = this.mixerPhaseErrorCorr;
379        other.pulsarGatingPhase   = this.pulsarGatingPhase;
380        other.signalToNoise       = this.signalToNoise;
381        other.useMixer            = this.useMixer;
382    
383        other.overrideAutoCorr = this.overrideAutoCorr;
384        other.autoCorrMode     = VciJaxbUtil.clone(this.autoCorrMode);
385        other.autoCorrCache    = VciJaxbUtil.clone(this.autoCorrCache);
386    
387        other.overrideBurstMode = this.overrideBurstMode;
388        other.burstMode         = VciJaxbUtil.clone(this.burstMode);
389        other.burstModeCache    = VciJaxbUtil.clone(this.burstModeCache);
390        
391        other.overridePhasedArray = this.overridePhasedArray;
392        other.phasedArray         = VciJaxbUtil.clone(this.phasedArray);
393        other.phasedArrayCache    = VciJaxbUtil.clone(this.phasedArrayCache);
394        
395        other.overridePulsarBinning = this.overridePulsarBinning;
396        other.pulsarBinning         = VciJaxbUtil.clone(this.pulsarBinning);
397        other.pulsarBinningCache    = VciJaxbUtil.clone(this.pulsarBinningCache);
398        
399        other.overrideRadarMode = this.overrideRadarMode;
400        other.radarMode         = VciJaxbUtil.clone(this.radarMode);
401        other.radarModeCache    = VciJaxbUtil.clone(this.radarModeCache);
402        
403        other.overrideToneExtraction = this.overrideToneExtraction;
404        other.toneExtraction         = VciJaxbUtil.clone(this.toneExtraction);
405        other.toneExtractionCache    = VciJaxbUtil.clone(this.toneExtractionCache);
406      }
407      
408      /**
409       * Returns true if this and other subband have equal VCI elements.
410       */
411      private boolean vciElementsAreEqual(WidarSubband other)
412      {
413        //Intentionally NOT comparing: sbId
414        
415        //Simple properties
416        if (other.mixerPhaseErrorCorr != this.mixerPhaseErrorCorr ||
417            other.pulsarGatingPhase   != this.pulsarGatingPhase   ||
418            other.signalToNoise       != this.signalToNoise       ||
419            other.useMixer            != this.useMixer)
420          return false;
421        
422        //We're going to say that two SBs that have equal values for VCI property V
423        //are NOT equal if one has V in its default state and the other has overridden
424        //V's default with a value equal to the default.
425        
426        //Default states
427        if (other.overrideAutoCorr       != this.overrideAutoCorr      ||
428            other.overrideBurstMode      != this.overrideBurstMode     ||
429            other.overridePhasedArray    != this.overridePhasedArray   ||
430            other.overridePulsarBinning  != this.overridePulsarBinning ||
431            other.overrideRadarMode      != this.overrideRadarMode     ||
432            other.overrideToneExtraction != this.overrideToneExtraction)
433          return false;
434        
435        //We're going to say that if a VCI property V is in its default state for
436        //both subbands, then these subbands are equal with respect to V, even
437        //if their cached values for V are different.
438        
439        //VCI objects
440        if (overrideAutoCorr &&
441            !VciJaxbUtil.testEquality(other.autoCorrMode, this.autoCorrMode))
442          return false;
443        
444        if (overrideBurstMode &&
445            !VciJaxbUtil.testEquality(other.burstMode, this.burstMode))
446          return false;
447        
448        if (overridePhasedArray &&
449            !VciJaxbUtil.testEquality(other.phasedArray, this.phasedArray))
450          return false;
451        
452        if (overridePulsarBinning &&
453            !VciJaxbUtil.testEquality(other.pulsarBinning, this.pulsarBinning))
454          return false;
455        
456        if (overrideRadarMode &&
457            !VciJaxbUtil.testEquality(other.radarMode, this.radarMode))
458          return false;
459        
460        if (overrideToneExtraction &&
461            !VciJaxbUtil.testEquality(other.toneExtraction, this.toneExtraction))
462          return false;
463        
464        return true;
465      }
466      
467      /** Returns a hash code for the VCI elements of this subband. */
468      private int vciElementsHashCode()
469      {
470        //Taken from the Effective Java book by Joshua Bloch.
471        //The constants 17 & 37 are arbitrary & carry no meaning.
472        int result = 17;
473    
474        result = 37 * result + Boolean.valueOf(mixerPhaseErrorCorr).hashCode();
475        result = 37 * result + pulsarGatingPhase;
476        result = 37 * result + signalToNoise;
477        result = 37 * result + Boolean.valueOf(useMixer).hashCode();
478    
479        if (overrideAutoCorr)
480          result = 37 * result + VciJaxbUtil.makeHashCode(autoCorrMode);
481    
482        if (overrideBurstMode)
483          result = 37 * result + VciJaxbUtil.makeHashCode(burstMode);
484        
485        if (overridePhasedArray)
486          result = 37 * result + VciJaxbUtil.makeHashCode(phasedArray);
487        
488        if (overridePulsarBinning)
489          result = 37 * result + VciJaxbUtil.makeHashCode(pulsarBinning);
490        
491        if (overrideRadarMode)
492          result = 37 * result + VciJaxbUtil.makeHashCode(radarMode);
493        
494        if (overrideToneExtraction)
495          result = 37 * result + VciJaxbUtil.makeHashCode(toneExtraction);
496        
497        return result;
498      }
499      
500      public SubBand toVci()
501      {
502        SubBand vciObj = new SubBand();
503    
504        vciObj.setSbid(sbId);
505    
506        //Modeled as VCI elements
507        vciObj.setAutoCorrMode  (VciJaxbUtil.clone(autoCorrMode  ));
508        vciObj.setBurstMode     (VciJaxbUtil.clone(burstMode     ));
509        vciObj.setPhasedArray   (VciJaxbUtil.clone(phasedArray   ));
510        vciObj.setPulsarBinning (VciJaxbUtil.clone(pulsarBinning ));
511        vciObj.setRadarMode     (VciJaxbUtil.clone(radarMode     ));
512        vciObj.setToneExtraction(VciJaxbUtil.clone(toneExtraction));
513        
514        vciObj.setMixerPhaseErrorCorr(mixerPhaseErrorCorr ? YES : NO);
515        vciObj.setPulsarGatingPhase  (pulsarGatingPhase);
516        vciObj.setSignalToNoise      (signalToNoise);
517        vciObj.setUseMixer           (useMixer ? YES : NO);
518    
519        //Mapped from SSS objects
520        if (name != null)
521          vciObj.setName(name);
522    
523        vciObj.setBw(getBandwidth().toUnits(HERTZ).stripTrailingZeros().toPlainString());
524        vciObj.setCentralFreq(getCentralFrequency().toUnits(HERTZ).intValue());
525        vciObj.setRqNumBits(getRequantization());
526        
527        List<PolProducts> polProducts = vciObj.getPolProducts();
528        for (CorrelationProductGroupAbs prodGrp : productGroups)
529          polProducts.add(((WidarCorrelationProductGroup)prodGrp).toVci()); //see note below
530    
531        return vciObj;
532        
533        //Note: this cast should be safe because this class is in control of
534        //making new product group objects for itself.
535      }
536      
537      //----------------------------------------------------------------------------
538      // ID
539      //----------------------------------------------------------------------------
540    
541      //Since this method is used only by trusted classes, we don't check newId value
542      void setSbId(int newId)  { sbId = newId; }
543    
544      @XmlAttribute(required=true)
545      public int getSbId()  { return sbId; }
546      
547      //----------------------------------------------------------------------------
548      // Auto Correlation Mode
549      //----------------------------------------------------------------------------
550      
551      private boolean      overrideAutoCorr;
552      private AutoCorrMode autoCorrCache;
553    
554      /**
555       * Tells this subband to override, or restore, auto correlation mode defaults.
556       * 
557       * @param override
558       *   instruction to override, or restore, auto correlation mode defaults.
559       *   
560       * @return
561       *   <i>null</i> if {@code override} is <i>false</i>.
562       *   If {@code override} is <i>true</i>, returns the <tt>AutoCorrMode</tt>
563       *   held internally by this subband.  This means clients can operate
564       *   directly on the returned object and impact this subband.
565       */
566      public AutoCorrMode overrideDefaultAutoCorrMode(boolean override)
567      {
568        //Changing state?
569        if (override != overrideAutoCorr)
570        {
571          overrideAutoCorr = override;
572          
573          if (override) //user-specified
574          {
575            autoCorrMode = autoCorrCache;
576          }
577          else //default setting
578          {
579            autoCorrCache = VciJaxbUtil.clone(autoCorrMode);
580            autoCorrMode  = null;
581          }
582        }
583        
584        return autoCorrMode;
585      }
586      
587      /**
588       * Returns the autocorrelation mode to use for this subband, if any.
589       * The default value of this property is <i>null</i>.
590       * <p>
591       * This is a VCI property.
592       * The returned object was generated from VCI XML schema elements and
593       * is the actual instance held internally by this subband.</p>
594       * 
595       * @return the autocorrelation mode to use for this subband, if any.
596       * 
597       * @see #overrideDefaultAutoCorrMode(boolean)
598       */
599      @XmlElement(namespace="http://www.nrc.ca/namespaces/widar")
600      public AutoCorrMode getAutoCorrMode()  { return autoCorrMode; }
601      
602      //This method is for persistence frameworks such as JAXB & Hibernate
603      @SuppressWarnings("unused")
604      private void setAutoCorrMode(AutoCorrMode newMode)
605      {
606        autoCorrMode = newMode;
607        overrideAutoCorr = (newMode != null);
608      }
609    
610      //----------------------------------------------------------------------------
611      // Burst Mode
612      //----------------------------------------------------------------------------
613      
614      private boolean   overrideBurstMode;
615      private BurstMode burstModeCache;
616    
617      /**
618       * Tells this subband to override, or restore, burst mode defaults.
619       * 
620       * @param override
621       *   instruction to override, or restore, burst mode defaults.
622       *   
623       * @return
624       *   <i>null</i> if {@code override} is <i>false</i>.
625       *   If {@code override} is <i>true</i>, returns the <tt>BurstMode</tt>
626       *   held internally by this subband.  This means clients can operate
627       *   directly on the returned object and impact this subband.
628       */
629      public BurstMode overrideDefaultBurstMode(boolean override)
630      {
631        //Changing state?
632        if (override != overrideBurstMode)
633        {
634          overrideBurstMode = override;
635          
636          if (override) //user-specified
637          {
638            burstMode = burstModeCache;
639          }
640          else //default setting
641          {
642            burstModeCache = VciJaxbUtil.clone(burstMode);
643            burstMode      = null;
644          }
645        }
646        
647        return burstMode;
648      }
649      
650      /**
651       * Returns the burst mode to use for this subband, if any.
652       * The default value of this property is <i>null</i>.
653       * <p>
654       * This is a VCI property.
655       * The returned object was generated from VCI XML schema elements and
656       * is the actual instance held internally by this subband.</p>
657       * 
658       * @return the autocorrelation mode to use for this subband, if any.
659       * 
660       * @see #overrideDefaultBurstMode(boolean)
661       */
662      @XmlElement(namespace="http://www.nrc.ca/namespaces/widar")
663      public BurstMode getBurstMode()  { return burstMode; }
664      
665      //This method is for persistence frameworks such as JAXB & Hibernate
666      @SuppressWarnings("unused")
667      private void setBurstMode(BurstMode newMode)
668      {
669        burstMode = newMode;
670        overrideBurstMode = (newMode != null);
671      }
672    
673      //----------------------------------------------------------------------------
674      // Phased Array
675      //----------------------------------------------------------------------------
676      
677      private boolean     overridePhasedArray;
678      private PhasedArray phasedArrayCache;
679    
680      /**
681       * Tells this subband to override, or restore, phased array defaults.
682       * 
683       * @param override
684       *   instruction to override, or restore, phased array defaults.
685       *   
686       * @return
687       *   <i>null</i> if {@code override} is <i>false</i>.
688       *   If {@code override} is <i>true</i>, returns the <tt>PhasedArray</tt>
689       *   held internally by this subband.  This means clients can operate
690       *   directly on the returned object and impact this subband.
691       */
692      public PhasedArray overrideDefaultPhasedArray(boolean override)
693      {
694        //Changing state?
695        if (override != overridePhasedArray)
696        {
697          overridePhasedArray = override;
698          
699          if (override) //user-specified
700          {
701            phasedArray = phasedArrayCache;
702          }
703          else //default setting
704          {
705            phasedArrayCache = VciJaxbUtil.clone(phasedArray);
706            phasedArray      = null;
707          }
708        }
709        
710        return phasedArray;
711      }
712      
713      /**
714       * Returns the phase array parameters to use for this subband, if any.
715       * The default value of this property is <i>null</i>.
716       * <p>
717       * This is a VCI property.
718       * The returned object was generated from VCI XML schema elements and
719       * is the actual instance held internally by this subband.</p>
720       * 
721       * @return the phase array parameters to use for this subband, if any.
722       * 
723       * @see #overrideDefaultPhasedArray(boolean)
724       */
725      @XmlElement(namespace="http://www.nrc.ca/namespaces/widar")
726      public PhasedArray getPhasedArray()  { return phasedArray; }
727      
728      //This method is for persistence frameworks such as JAXB & Hibernate
729      @SuppressWarnings("unused")
730      private void setPhasedArray(PhasedArray newArray)
731      {
732        phasedArray = newArray;
733        overridePhasedArray = (newArray != null);
734      }
735    
736      //----------------------------------------------------------------------------
737      // Pulsar Binning
738      //----------------------------------------------------------------------------
739      
740      private boolean       overridePulsarBinning;
741      private PulsarBinning pulsarBinningCache;
742    
743      /**
744       * Tells this subband to override, or restore, pulsar binning defaults.
745       * 
746       * @param override
747       *   instruction to override, or restore, pulsar binning defaults.
748       *   
749       * @return
750       *   <i>null</i> if {@code override} is <i>false</i>.
751       *   If {@code override} is <i>true</i>, returns the <tt>PulsarBinning</tt>
752       *   held internally by this subband.  This means clients can operate
753       *   directly on the returned object and impact this subband.
754       */
755      public PulsarBinning overrideDefaultPulsarBinning(boolean override)
756      {
757        //Changing state?
758        if (override != overridePulsarBinning)
759        {
760          overridePulsarBinning = override;
761          
762          if (override) //user-specified
763          {
764            pulsarBinning = pulsarBinningCache;
765          }
766          else //default setting
767          {
768            pulsarBinningCache = VciJaxbUtil.clone(pulsarBinning);
769            pulsarBinning      = null;
770          }
771        }
772        
773        return pulsarBinning;
774      }
775      
776      /**
777       * Returns the pulsar binning parameters to use for this subband, if any.
778       * The default value of this property is <i>null</i>.
779       * <p>
780       * This is a VCI property.
781       * The returned object was generated from VCI XML schema elements and
782       * is the actual instance held internally by this subband.</p>
783       * 
784       * @return the pulsar binning parameters to use for this subband, if any.
785       * 
786       * @see #overrideDefaultPulsarBinning(boolean)
787       */
788      @XmlElement(namespace="http://www.nrc.ca/namespaces/widar")
789      public PulsarBinning getPulsarBinning()  { return pulsarBinning; }
790      
791      //This method is for persistence frameworks such as JAXB & Hibernate
792      @SuppressWarnings("unused")
793      private void setPulsarBinning(PulsarBinning newBinning)
794      {
795        pulsarBinning = newBinning;
796        overridePulsarBinning = (newBinning != null);
797      }
798    
799      //----------------------------------------------------------------------------
800      // Radar Mode
801      //----------------------------------------------------------------------------
802      
803      private boolean   overrideRadarMode;
804      private RadarMode radarModeCache;
805    
806      /**
807       * Tells this subband to override, or restore, radar mode defaults.
808       * 
809       * @param override
810       *   instruction to override, or restore, radar mode defaults.
811       *   
812       * @return
813       *   <i>null</i> if {@code override} is <i>false</i>.
814       *   If {@code override} is <i>true</i>, returns the <tt>RadarMode</tt>
815       *   held internally by this subband.  This means clients can operate
816       *   directly on the returned object and impact this subband.
817       */
818      public RadarMode overrideDefaultRadarMode(boolean override)
819      {
820        //Changing state?
821        if (override != overrideRadarMode)
822        {
823          overrideRadarMode = override;
824          
825          if (override) //user-specified
826          {
827            radarMode = radarModeCache;
828          }
829          else //default setting
830          {
831            radarModeCache = VciJaxbUtil.clone(radarMode);
832            radarMode      = null;
833          }
834        }
835        
836        return radarMode;
837      }
838      
839      /**
840       * Returns the pulsar binning parameters to use for this subband, if any.
841       * The default value of this property is <i>null</i>.
842       * <p>
843       * This is a VCI property.
844       * The returned object was generated from VCI XML schema elements and
845       * is the actual instance held internally by this subband.</p>
846       * 
847       * @return the pulsar binning parameters to use for this subband, if any.
848       * 
849       * @see #overrideDefaultRadarMode(boolean)
850       */
851      @XmlElement(namespace="http://www.nrc.ca/namespaces/widar")
852      public RadarMode getRadarMode()  { return radarMode; }
853      
854      //This method is for persistence frameworks such as JAXB & Hibernate
855      @SuppressWarnings("unused")
856      private void setRadarMode(RadarMode newMode)
857      {
858        radarMode = newMode;
859        overrideRadarMode = (newMode != null);
860      }
861    
862      //----------------------------------------------------------------------------
863      // Tone Extraction
864      //----------------------------------------------------------------------------
865      
866      private boolean        overrideToneExtraction;
867      private ToneExtraction toneExtractionCache;
868    
869      /**
870       * Tells this subband to override, or restore, pulsar gating defaults.
871       * 
872       * @param override
873       *   instruction to override, or restore, pulsar gating defaults.
874       *   
875       * @return
876       *   <i>null</i> if {@code override} is <i>false</i>.
877       *   If {@code override} is <i>true</i>, returns the <tt>ToneExtraction</tt>
878       *   held internally by this subband.  This means clients can operate
879       *   directly on the returned object and impact this subband.
880       */
881      public ToneExtraction overrideDefaultToneExtraction(boolean override)
882      {
883        //Changing state?
884        if (override != overrideToneExtraction)
885        {
886          overrideToneExtraction = override;
887          
888          if (override) //user-specified
889          {
890            toneExtraction = toneExtractionCache;
891          }
892          else //default setting
893          {
894            toneExtractionCache = VciJaxbUtil.clone(toneExtraction);
895            toneExtraction      = null;
896          }
897        }
898        
899        return toneExtraction;
900      }
901      
902      /**
903       * Returns the tone extraction parameters to use for this subband, if any.
904       * The default value of this property is <i>null</i>.
905       * <p>
906       * This is a VCI property.
907       * The returned object was generated from VCI XML schema elements and
908       * is the actual instance held internally by this subband.</p>
909       * 
910       * @return the tone extraction parameters to use for this subband, if any.
911       * 
912       * @see #overrideDefaultToneExtraction(boolean)
913       */
914      @XmlElement(namespace="http://www.nrc.ca/namespaces/widar")
915      public ToneExtraction getToneExtraction()  { return toneExtraction; }
916      
917      //This method is for persistence frameworks such as JAXB & Hibernate
918      @SuppressWarnings("unused")
919      private void setToneExtraction(ToneExtraction newTone)
920      {
921        toneExtraction = newTone;
922        overrideToneExtraction = (newTone != null);
923      }
924    
925      //----------------------------------------------------------------------------
926      // 
927      //----------------------------------------------------------------------------
928      
929      /**
930       * Returns <i>true</i> if the {@link #getUseStage2Mixer() stage 2 mixer}
931       * should update the phase error and model.
932       * If the stage 2 mixer is not being used, this value is irrelevant.
933       * The default value of this property is <i>true</i>.
934       * 
935       * @return
936       *   <i>true</i> if the stage 2 mixer should update the phase error and model.
937       */
938      @XmlAttribute(required=false)
939      public boolean getMixerPhaseErrorCorr()  { return mixerPhaseErrorCorr; }
940      
941      /**
942       * Tells the correlator whether or not the stage 2 mixer should update the
943       * phase error and model.
944       * 
945       * @param correct
946       *   use a value of <i>true</i>
947       *   if the stage 2 mixer should update the phase error and model.
948       */
949      public void setMixerPhaseErrorCorr(boolean correct)  { mixerPhaseErrorCorr = correct; }
950      
951      /**
952       * Returns the shift of the pulsar gate in fractions of a cycle.
953       * 
954       * @return
955       *   the shift of the pulsar gate in fractions of a cycle.
956       *   The returned value will be an integer from 0 through 100.
957       */
958      @XmlAttribute(required=false)
959      public int getPulsarGatingPhase()  { return pulsarGatingPhase; }
960      
961      /**
962       * Sets the shift of the pulsar gate in fractions of a cycle
963       * 
964       * @param newPhase
965       *   fraction of a cycle.  The value must be in the range 0 through 100.
966       *   
967       * @throws IllegalArgumentException
968       *   if {@code newPhase} is less than zero or greater than one hundred.
969       */
970      public void setPulsarGatingPhase(int newPhase)
971      {
972        if (newPhase < 0 || newPhase > 100)
973          throw new IllegalArgumentException("Phase value of " + newPhase +
974            " is illegal.  Value must be in range [0-100].");
975        
976        pulsarGatingPhase = newPhase;
977      }
978      
979      /**
980       * Returns the percent of signal in the input data.
981       * 
982       * @return
983       *   the percent of signal in the input data.
984       *   The returned value will be an integer from 0 through 100.
985       */
986      @XmlAttribute(required=false)
987      public int getSignalToNoiseRatio()  { return signalToNoise; }
988      
989      /**
990       * Sets the percent of signal in the input data.
991       * 
992       * @param newRatio
993       *   percent of signal in the input data.
994       *   The value must be in the range 0 through 100.
995       *   
996       * @throws IllegalArgumentException
997       *   if {@code newRatio} is less than zero or greater than one hundred.
998       */
999      public void setSignalToNoiseRatio(int newRatio)
1000      {
1001        if (newRatio < 0 || newRatio > 100)
1002          throw new IllegalArgumentException("S/N ratio of " + newRatio +
1003            " is illegal.  Value must be in range [0-100].");
1004        
1005        signalToNoise = newRatio;
1006      }
1007    
1008      /**
1009       * Turns the stage 2 mixer on or off for this subband.
1010       * <p>
1011       * If the stage 2 mixer is off, this subband is restricted to certain slots
1012       * in its baseband.  If this subband's bandwidth is W, and if the stage 2
1013       * mixer is off, then the low frequency of this subband's range must start
1014       * at 0, W, 2W, etc.</p>
1015       *  
1016       * @param use
1017       *   <i>true</i> if the stage 2 mixer should be used for this subband.
1018       */
1019      @XmlAttribute(required=false)
1020      public void setUseStage2Mixer(boolean use)  { useMixer = use; }
1021      
1022      /**
1023       * Returns <i>true</i> if the stage 2 mixer is in use for this subband.
1024       * @return <i>true</i> if the stage 2 mixer is in use for this subband.
1025       * @see #setUseStage2Mixer(boolean)
1026       */
1027      public boolean getUseStage2Mixer()  { return useMixer; }
1028      
1029      //============================================================================
1030      // 
1031      //============================================================================
1032    
1033      /**
1034       * Returns an XML representation of this subband.
1035       * @return an XML representation of this subband.
1036       * @throws JAXBException if anything goes wrong during the conversion to XML.
1037       * @see #writeAsXmlTo(Writer)
1038       */
1039      public String toXml() throws JAXBException
1040      {
1041        return JaxbUtility.getSharedInstance().objectToXmlString(this);
1042      }
1043    
1044      /**
1045       * Writes an XML representation of this subband to {@code writer}.
1046       * @param writer the device to which XML is written.
1047       * @throws JAXBException if anything goes wrong during the conversion to XML.
1048       */
1049      public void writeAsXmlTo(Writer writer) throws JAXBException
1050      {
1051        JaxbUtility.getSharedInstance().writeObjectAsXmlTo(writer, this, null);
1052      }
1053      
1054      /**
1055       * Creates a new subband from the XML data in the given file.
1056       * 
1057       * @param xmlFile
1058       *   the name of an XML file.  This method will attempt to locate
1059       *   the file by using {@link Class#getResource(String)}.
1060       *                
1061       * @return
1062       *   a new subband from the XML data in the given file.
1063       * 
1064       * @throws FileNotFoundException
1065       *   if the XML file cannot be found.
1066       * 
1067       * @throws JAXBException
1068       *   if the schema file used (if any) is malformed, if the XML file cannot be
1069       *   read, or if the XML file is not schema-valid.
1070       * 
1071       * @throws XMLStreamException
1072       *   if there is a problem opening the XML file, if the XML is not well-formed,
1073       *   or for some other "unexpected processing conditions".
1074       */
1075      public static WidarSubband fromXml(String xmlFile)
1076        throws JAXBException, XMLStreamException, FileNotFoundException
1077      {
1078        return JaxbUtility.getSharedInstance()
1079                          .xmlFileToObject(xmlFile, WidarSubband.class);
1080      }
1081    
1082      /**
1083       * Creates a new subband based on the XML data read from {@code reader}.
1084       * 
1085       * @param reader
1086       *   the source of the XML data.
1087       *   If this value is <i>null</i>, <i>null</i> is returned.
1088       *               
1089       * @return
1090       *  a new subband based on the XML data read from {@code reader}.
1091       * 
1092       * @throws XMLStreamException
1093       *   if the XML is not well-formed,
1094       *   or for some other "unexpected processing conditions".
1095       *           
1096       * @throws JAXBException
1097       *   if anything else goes wrong during the transformation.
1098       */
1099      public static WidarSubband fromXml(Reader reader)
1100        throws JAXBException, XMLStreamException
1101      {
1102        return JaxbUtility.getSharedInstance()
1103                          .readObjectAsXmlFrom(reader, WidarSubband.class, null);
1104      }
1105    
1106      //============================================================================
1107      // 
1108      //============================================================================
1109      
1110      /**
1111       *  Returns a copy of this subband.
1112       *  <p>
1113       *  The returned subband is <i>nearly</i>, but not quite, a deep copy of this
1114       *  subband.  Properties that are not copied:</p>
1115       *  <ol>
1116       *    <li>Those properties not copied by the
1117       *        {@link CorrelatorSubbandAbs#clone() super class}.</li>
1118       *    <li><b>baseline board pairs</b> - this collection will be empty.</li>
1119       *  </ol>
1120       *  <p>
1121       *  If anything goes wrong during the cloning procedure,
1122       *  a {@code RuntimeException} will be thrown.</p>
1123       */
1124      @Override
1125      public WidarSubband clone()
1126      {
1127        WidarSubband clone = null;
1128        
1129        try
1130        {
1131          clone = (WidarSubband)super.clone();
1132          
1133          copyVciElementsInto(clone);
1134        }
1135        catch (Exception ex)
1136        {
1137          throw new RuntimeException(ex);
1138        }
1139        
1140        return clone;
1141      }
1142      
1143      /** Returns <i>true</i> if {@code o} is equal to this subband. */
1144      @Override
1145      public boolean equals(Object o)
1146      {
1147        //Quick exit if super class says not equal
1148        if (!super.equals(o))
1149          return false;
1150       
1151        //A safe cast if we got this far
1152        WidarSubband other = (WidarSubband)o;
1153        
1154        //Attributes that we INTENTIONALLY DO NOT COMPARE:
1155        //  blbps
1156        
1157        return vciElementsAreEqual(other);
1158      }
1159      
1160      /** Returns a hash code for this subband. */
1161      @Override
1162      public int hashCode()
1163      {
1164        //Taken from the Effective Java book by Joshua Bloch.
1165        //The constants 17 & 37 are arbitrary & carry no meaning.
1166        int result = super.hashCode();
1167        
1168        //You MUST keep this method in sync w/ the equals method
1169    
1170        result = 37 * result + vciElementsHashCode();
1171        
1172        return result;
1173      }
1174      
1175      //============================================================================
1176      // 
1177      //============================================================================
1178      /*
1179      public static void main(String[] args)
1180      {
1181        WidarBuilder builder = new WidarBuilder();
1182        
1183        WidarSubband sb = builder.makeSubband();
1184    
1185        try
1186        {
1187          sb.writeAsXmlTo(new java.io.PrintWriter(System.out));
1188        }
1189        catch (Exception ex)
1190        {
1191          System.out.println("Trouble w/ sb.toXml.  Msg:");
1192          System.out.println(ex.getMessage());
1193          ex.printStackTrace();
1194          
1195          System.out.println("Attempting to write XML w/out schema verification:");
1196          JaxbUtility.getSharedInstance().setLookForDefaultSchema(false);
1197          try
1198          {
1199            System.out.println(sb.toXml());
1200          }
1201          catch (JAXBException ex2)
1202          {
1203            System.out.println("Still had trouble w/ sb.toXml.  Msg:");
1204            System.out.println(ex2.getMessage());
1205            ex2.printStackTrace();
1206          }
1207        }
1208      }
1209      */
1210      /*
1211      public static void main(String... args) throws Exception
1212      {
1213        WidarBaseband bb = new WidarBasebandSinglet();
1214        bb.setBandwidth(new Frequency("2.048"));
1215        
1216        WidarSubband sb = (WidarSubband)bb.makeNewSubband();
1217        bb.addSubband(sb);
1218        sb.setBandwidth(new Frequency("64.0", FrequencyUnits.MEGAHERTZ));
1219        
1220        sb.setCentralFrequency(new Frequency("200.0", FrequencyUnits.MEGAHERTZ));
1221        System.out.println("Wanted 200, got " + sb.getCentralFrequency());
1222        sb.setCentralFrequency(new Frequency("223.0", FrequencyUnits.MEGAHERTZ));
1223        System.out.println("Wanted 223, got " + sb.getCentralFrequency());
1224        sb.setCentralFrequency(new Frequency("224.0", FrequencyUnits.MEGAHERTZ));
1225        System.out.println("Wanted 224, got " + sb.getCentralFrequency());
1226        sb.setCentralFrequency(new Frequency("225.0", FrequencyUnits.MEGAHERTZ));
1227        System.out.println("Wanted 225, got " + sb.getCentralFrequency());
1228        sb.setCentralFrequency(new Frequency("255.0", FrequencyUnits.MEGAHERTZ));
1229        System.out.println("Wanted 255, got " + sb.getCentralFrequency());
1230        sb.setCentralFrequency(new Frequency("256.0", FrequencyUnits.MEGAHERTZ));
1231        System.out.println("Wanted 256, got " + sb.getCentralFrequency());
1232        sb.setCentralFrequency(new Frequency("257.0", FrequencyUnits.MEGAHERTZ));
1233        System.out.println("Wanted 257, got " + sb.getCentralFrequency());
1234        System.out.println();
1235        sb.setCentralFrequency(new Frequency("20.0", FrequencyUnits.MEGAHERTZ));
1236        System.out.println("Wanted 20, got " + sb.getCentralFrequency());
1237        System.out.println();
1238        sb.setCentralFrequency(new Frequency("2040.0", FrequencyUnits.MEGAHERTZ));
1239        System.out.println("Wanted 2040, got " + sb.getCentralFrequency());
1240        sb.setCentralFrequency(new Frequency("2050.0", FrequencyUnits.MEGAHERTZ));
1241        System.out.println("Wanted 2050, got " + sb.getCentralFrequency());
1242        sb.setCentralFrequency(new Frequency("2200.0", FrequencyUnits.MEGAHERTZ));
1243        System.out.println("Wanted 2200, got " + sb.getCentralFrequency());
1244      }
1245      */
1246    }