001    package edu.nrao.sss.model.resource.evla;
002    
003    import java.awt.event.ActionEvent;
004    import java.io.FileNotFoundException;
005    import java.io.Reader;
006    import java.io.Writer;
007    import java.util.ArrayList;
008    import java.util.Date;
009    import java.util.HashSet;
010    import java.util.List;
011    import java.util.Map;
012    import java.util.Set;
013    import java.util.SortedMap;
014    import java.util.SortedSet;
015    import java.util.TreeMap;
016    import java.util.TreeSet;
017    import java.util.UUID;
018    
019    import javax.xml.bind.JAXBException;
020    import javax.xml.bind.annotation.XmlElement;
021    import javax.xml.bind.annotation.XmlRootElement;
022    import javax.xml.bind.annotation.XmlType;
023    import javax.xml.datatype.XMLGregorianCalendar;
024    import javax.xml.stream.XMLStreamException;
025    
026    import org.apache.log4j.Logger;
027    
028    import ca.nrc.widar.jaxb.vci.BaseBand;
029    import ca.nrc.widar.jaxb.vci.BbParams;
030    import ca.nrc.widar.jaxb.vci.ListOfStations;
031    import ca.nrc.widar.jaxb.vci.Station;
032    import ca.nrc.widar.jaxb.vci.StationInputOutput;
033    import ca.nrc.widar.jaxb.vci.StationListActionType;
034    import ca.nrc.widar.jaxb.vci.SubArray;
035    import ca.nrc.widar.jaxb.vci.SubarrayActionType;
036    
037    import edu.nrao.sss.electronics.DigitalSignal;
038    import edu.nrao.sss.model.resource.AntennaElectronics;
039    import edu.nrao.sss.model.resource.BasebandCollectionEvent;
040    import edu.nrao.sss.model.resource.BasebandCollectionListener;
041    import edu.nrao.sss.model.resource.CorrelationProductGroup;
042    import edu.nrao.sss.model.resource.CorrelatorBaseband;
043    import edu.nrao.sss.model.resource.CorrelatorBasebandAbs;
044    import edu.nrao.sss.model.resource.CorrelatorConfiguration;
045    import edu.nrao.sss.model.resource.CorrelatorName;
046    import edu.nrao.sss.model.resource.CorrelatorSubband;
047    import edu.nrao.sss.model.resource.ResourceSpecification;
048    import edu.nrao.sss.util.JaxbUtility;
049    
050    /**
051     * The configuration of the WIDAR correlator.
052     * <p>
053     * <b>Version Info:</b>
054     * <table style="margin-left:2em">
055     *   <tr><td>$Revision: 2309 $</td></tr>
056     *   <tr><td>$Date: 2009-05-14 16:46:39 -0600 (Thu, 14 May 2009) $</td></tr>
057     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
058     * </table></p>
059     * 
060     * @author David M. Harland
061     * @since 2008-02-04
062     */
063    @XmlRootElement
064    @XmlType(propOrder={"maxAntennaCount","baselineBoardPool"})
065    public class EvlaWidarConfiguration
066      extends CorrelatorConfiguration
067    {
068      private static final Logger LOG = Logger.getLogger(EvlaWidarConfiguration.class);
069      
070      private static final int EVLA_ANT_MAX = 27;
071      
072      private int maxAntennaCount;
073      
074      //The natural state of the basebands is to be paired, and left to its own
075      //devices the actionPerformed method of this class will be biased toward
076      //forming pairs when the signals from the antenna electronics have changed.
077      //To keep that from happening, we keep track of the BB pairs that the client
078      //has intentionally split into singlets.
079      private Set<String> pairsSplitByClient;
080      
081      private BlbpPool blbpPool;
082      
083      /**
084       * Constructs a new configuration that is initialized from {@code signalSrc}.
085       * 
086       * @param signalSrc
087       *   the antenna electronics that provide the input signals for the basebands
088       *   of this configuration.
089       *   
090       * @throws IllegalArgumentException
091       *   if {@code signalSrc} is <i>null</i>.
092       */
093      public EvlaWidarConfiguration(AntennaElectronics signalSrc)
094      {
095        super(signalSrc);
096        init();
097      }
098      
099      //This is here only for persistence mechanisms
100      @SuppressWarnings("unused")
101      private EvlaWidarConfiguration()
102      {
103        init();
104      }
105      
106      private void init()
107      {
108        maxAntennaCount    = EVLA_ANT_MAX;
109        pairsSplitByClient = new HashSet<String>();
110        
111        blbpPool = new BlbpPool(this);
112      }
113    
114      //============================================================================
115      // IDENTIFICATION & ANTENNA COUNT
116      //============================================================================
117    
118      public CorrelatorName getName()  { return CorrelatorName.WIDAR; }
119    
120      /**
121       * Sets the maximum number of antennas in a subarray that may use this
122       * configuration.  This configuration needs to know the maximum number
123       * of antennas to be used in an observation because many of the calculations
124       * for WIDAR depend on the number of antennas used.
125       * 
126       * @param newMax
127       *   the new maximum number of antennas.
128       *   If this value is less than one, one will be used.
129       *   If this value is greater than the number of antennas in the full array
130       *   (27), 27 will be used. 
131       */
132      public void setMaxAntennaCount(int newMax)
133      {
134        if      (newMax < 1)             newMax = 1;
135        else if (newMax > EVLA_ANT_MAX)  newMax = EVLA_ANT_MAX;
136        
137        maxAntennaCount = newMax;
138      }
139      
140      /**
141       * Returns the maximum number of antennas that are compatible with this
142       * configuration.  See {@link #setMaxAntennaCount(int)} for more
143       * information.
144       * 
145       * @return
146       *   the maximum number of antennas.
147       */
148      public int getMaxAntennaCount()
149      {
150        return maxAntennaCount;
151      }
152      
153      //============================================================================
154      // BASEBANDS
155      //============================================================================
156      
157      @Override
158      protected List<CorrelatorBaseband> getOrderedBasebands()
159      {
160        SortedMap<String, CorrelatorBaseband> nameMap =
161          new TreeMap<String, CorrelatorBaseband>();
162        
163        //Alpha name ordering, but w/ a twist related to pairs and partnered-singles
164        for (CorrelatorBaseband bb : activeBBs)
165        {
166          if (bb.isPair())
167          {
168            nameMap.put(bb.getName(), bb);
169          }
170          else
171          {
172            WidarBasebandSinglet wbs = (WidarBasebandSinglet)bb;
173            if (!wbs.hasPartner())
174            {
175              nameMap.put(wbs.getName(), wbs);
176            }
177            else //put name of partner w/ lower-valued name
178            {
179              String name1 = wbs.getName();
180              String name2 = wbs.getPartner().getName();
181              if (name1.compareTo(name2) <= 0)
182                nameMap.put(name1, wbs);
183              else
184                nameMap.put(name2, wbs.getPartner());
185            }
186          }
187        }
188        
189        //Build list from map, but account for unmapped partners
190        ArrayList<CorrelatorBaseband> result = new ArrayList<CorrelatorBaseband>();
191        
192        for (CorrelatorBaseband bb : nameMap.values())
193        {
194          result.add(bb);
195          if (bb.isSinglet())
196          {
197            CorrelatorBaseband p = ((WidarBasebandSinglet)bb).getPartner();
198            if (p != null)
199              result.add(p);
200          }
201        }
202        
203        return result;
204      }
205    
206      /* (non-Javadoc)
207       * @see CorrelatorConfiguration#makeBasebandFrom(DigitalSignal)
208       */
209      protected CorrelatorBaseband makeBasebandFrom(DigitalSignal ds)
210      {
211        if (ds == null)
212          return null;
213        
214        WidarBaseband bb = new WidarBasebandSinglet(ds);
215        bb.setEvlaWidarConfiguration(this);
216     
217        return bb;
218      }
219    
220      /* (non-Javadoc)
221       * @see CorrelatorConfiguration#makeBasebandFrom(DigitalSignal)
222       */
223      protected CorrelatorBaseband makeBasebandFrom(DigitalSignal ds1,
224                                                    DigitalSignal ds2)
225      {
226        if (ds1 == null || ds2 == null)
227          return null;
228        
229        WidarBaseband bb = new WidarBasebandPair(ds1, ds2);
230        bb.setEvlaWidarConfiguration(this);
231        
232        return bb;
233      }
234      
235      /**
236       * Updates this configuration by removing <tt>currentPair</tt> and
237       * replacing it with two partnered baseband singlets generated
238       * from it.
239       * 
240       * @param currentPair
241       *   the baseband pair to be split into partnered singles and replaced
242       *   by them.
243       */
244      public void splitBasebandPair(WidarBasebandPair currentPair)
245      {
246        //Perform the split
247        WidarBasebandSinglet[] newBBs = currentPair.toSinglePartners();
248        
249        //Remember that this was done by client request
250        pairsSplitByClient.add(currentPair.getName());
251        
252        //Update collection of active basebands.  NOT updating inactiveBBs.
253        activeBBs.remove(currentPair);
254        activeBBs.add(newBBs[0]);
255        activeBBs.add(newBBs[1]);
256        
257        //Notify listeners
258        List<CorrelatorBaseband> removed = new ArrayList<CorrelatorBaseband>();
259        removed.add(currentPair);
260        
261        List<CorrelatorBaseband> added = new ArrayList<CorrelatorBaseband>();
262        added.add(newBBs[0]);
263        added.add(newBBs[1]);
264        
265        notifyBasebandCollectionListeners(added, removed);
266      }
267      
268      /**
269       * Updates this configuration by removing <tt>currentSinglet</tt> and
270       * its partner and replacing them with a baseband pair generated by
271       * their union.  If <tt>currentSinglet</tt> has no partner, this
272       * method does nothing.
273       * <p>
274       * Note that which baseband in a tuple of partnered singles is passed
275       * to this method is significant.  For example, imagine the single partnered
276       * basebands A1 and C1.  If A1 is passed to this method, the A1/C1 pair
277       * will have characteristics more similar to A1 than C1, such as the
278       * decimation of the baseband into subbands.</p>
279       * 
280       * @param currentSinglet
281       *   the baseband singlet, along with its partner, that should be converted
282       *   to a baseband pair and replaced by it.
283       */
284      public void formPairWithPartner(WidarBasebandSinglet currentSinglet)
285      {
286        //Form the pair.  Quick exit if null.
287        WidarBasebandPair newPair = currentSinglet.toPairWithPartner();
288        if (newPair == null)
289          return;
290        
291        //In case this is an un-do of a previous client split
292        pairsSplitByClient.remove(newPair.getName());
293        
294        //Update collection of active basebands.  NOT updating inactiveBBs.
295        activeBBs.remove(currentSinglet);
296        activeBBs.remove(currentSinglet.getPartner());
297        activeBBs.add(newPair);
298        
299        //Notify listeners
300        List<CorrelatorBaseband> removed = new ArrayList<CorrelatorBaseband>();
301        removed.add(currentSinglet);
302        removed.add(currentSinglet.getPartner());
303        
304        List<CorrelatorBaseband> added = new ArrayList<CorrelatorBaseband>();
305        added.add(newPair);
306        
307        notifyBasebandCollectionListeners(added, removed);
308      }
309      
310      //This is called when the antenna electronics' execute method
311      //is called.
312      public void actionPerformed(ActionEvent event)
313      {
314        //Remember the names of the active BBs before we clear everything
315        Set<String> namesOfFormerlyActiveBBs = new HashSet<String>();
316        for (CorrelatorBaseband activeBB : activeBBs)
317          namesOfFormerlyActiveBBs.add(activeBB.getName());
318        
319        //Move all BBs to inactive
320        for (CorrelatorBasebandAbs activeBB : activeBBs)
321          inactiveBBs.put(activeBB.getName(), activeBB);
322        
323        activeBBs.clear();
324        
325        //Use input signals to update, or create, basebands
326        for (List<DigitalSignal> inputSignalPair : signalSource.getSignalPairs())
327        {
328          DigitalSignal ds0 = inputSignalPair.get(0);
329          DigitalSignal ds1 = inputSignalPair.get(1);
330          
331          //TODO we could put a test here to ensure at least on ds is non-null.
332          //     If both are null, it's a pgmr error, because sigSrc.getSigPairs
333          //     says it won't make a pair of two nulls.
334          
335          if      (ds1 == null)  updateBaseband(ds0);
336          else if (ds0 == null)  updateBaseband(ds1);
337          else                   updateBaseband(ds0, ds1);
338        }
339        
340        //See if we activated or deactivated BBs.
341        //(This does NOT check if indiv BBs changed internally.)
342        Map<String, CorrelatorBaseband> activeBbMap =
343          new TreeMap<String, CorrelatorBaseband>();
344        
345        for (CorrelatorBaseband activeBB : activeBBs)
346          activeBbMap.put(activeBB.getName(), activeBB);
347        
348        if (!namesOfFormerlyActiveBBs.equals(activeBbMap.keySet()))
349          notifyBasebandCollectionListeners(namesOfFormerlyActiveBBs,
350                                            activeBbMap);
351      }
352    
353      //Helps actionPerformed method
354      private WidarBasebandSinglet updateBaseband(DigitalSignal inputSignal)
355      {
356        String signalName = inputSignal.getName();
357    
358        WidarBasebandSinglet bb;
359        
360        //NOTE: Logic below presupposes that we've moved all BBs to inactive
361        
362        //See if we already have this BB.  If so, update it w/ new signal.
363        if (inactiveBBs.containsKey(signalName))
364        {
365          bb = (WidarBasebandSinglet)inactiveBBs.remove(signalName);
366          bb.setInput(inputSignal);
367        }
368        //Make new BB if we didn't already have this one.
369        else
370        {
371          bb = new WidarBasebandSinglet(inputSignal);
372        }
373    
374        bb.setEvlaWidarConfiguration(this);
375        
376        activeBBs.add(bb);
377        
378        return bb;
379      }
380    
381      //Helps actionPerformed method
382      private void updateBaseband(DigitalSignal inputSignal0,
383                                  DigitalSignal inputSignal1)
384      {
385        String pairName = WidarBasebandPair.makeName(inputSignal0, inputSignal1);
386        
387        //If the client has expressly decided to treat the natural pair as
388        //singlet(s), don't go ahead w/ the pairing.
389        if (pairsSplitByClient.contains(pairName))
390        {
391          WidarBasebandSinglet bb1 = updateBaseband(inputSignal0);
392          WidarBasebandSinglet bb2 = updateBaseband(inputSignal1);
393          
394          WidarBasebandSinglet.formPartnership(bb1, bb2);
395        }
396        else //update or create a BB pair
397        {
398          WidarBasebandPair bbp;
399          
400          //NOTE: Logic below presupposes that we've moved all BBs to inactive
401          
402          //See if we already have this BB.  If so, update it w/ new signal.
403          if (inactiveBBs.containsKey(pairName))
404          {
405            bbp = (WidarBasebandPair)inactiveBBs.remove(pairName);
406            bbp.setInput(inputSignal0, inputSignal1);
407          }
408          //Make new BB if we didn't already have this one.
409          else
410          {
411            bbp = new WidarBasebandPair(inputSignal0, inputSignal1);
412          }
413          
414          bbp.setEvlaWidarConfiguration(this);
415          
416          activeBBs.add(bbp);
417        }
418      }
419     
420      //Called AFTER determining that notification is needed
421      private void notifyBasebandCollectionListeners(Set<String> formerBbNames,
422                                                     Map<String, CorrelatorBaseband> currentBBs)
423      {
424        //By removing the names of the BBs before the change to our collection
425        //from the current set of names, we arrive at the names of the newly added.
426        ArrayList<CorrelatorBaseband> addedBBs =
427          new ArrayList<CorrelatorBaseband>();
428        
429        Set<String> currentBbNames = new HashSet<String>(currentBBs.keySet());
430        
431        currentBbNames.removeAll(formerBbNames);
432        
433        for (String name : currentBbNames)
434          addedBBs.add(currentBBs.get(name));
435        
436        //By removing the names of the BBs after the change to our collection from
437        //the current set of names, we arrive at the names of the newly removed.
438        ArrayList<CorrelatorBaseband> removedBBs =
439          new ArrayList<CorrelatorBaseband>();
440        
441        formerBbNames = new HashSet<String>(formerBbNames);
442        
443        formerBbNames.removeAll(currentBBs.keySet());
444        
445        for (String name : formerBbNames)
446          removedBBs.add(inactiveBBs.get(name));
447        
448        notifyBasebandCollectionListeners(addedBBs, removedBBs);
449      }
450      
451      //Called AFTER determining that notification is needed
452      private void notifyBasebandCollectionListeners(List<CorrelatorBaseband> addedBBs,
453                                                     List<CorrelatorBaseband> removedBBs)
454      {
455        //Create an event object and pass to listeners
456        BasebandCollectionEvent event =
457          new BasebandCollectionEvent(this, addedBBs, removedBBs);
458        
459        //TODO notify listeners in a diff thread?
460        for (BasebandCollectionListener listener : bbListeners)
461          listener.basebandCollectionChanged(event);
462      }
463      
464      @Override
465      protected void createdBasebandsFromPersistentStore()
466      {
467        LOG.debug("Setting container of basebands; activeBBs.size = "+activeBBs.size());
468    
469        for (CorrelatorBasebandAbs bb : activeBBs)
470          ((WidarBaseband)bb).setEvlaWidarConfiguration(this);
471      }
472      
473      /**
474       * <i>This method is not yet supported.</i>
475       */
476      public void configureFrom(ResourceSpecification scienceView)
477      {
478        throw new UnsupportedOperationException("not yet programmed"); //TODO code me
479      }
480    
481      //============================================================================
482      // BASELINE BOARD PAIRS
483      //============================================================================
484    
485      public BlbpPool getBaselineBoardPool()
486      {
487        return blbpPool;
488      }
489      
490      @XmlElement
491      @SuppressWarnings("unused") //Used by JAXB and Hibernate
492      private void setBaselineBoardPool(BlbpPool newPool)
493      {
494        if (newPool != null)
495        {
496          newPool.setPoolOwner(this);
497          blbpPool = newPool;
498        }
499      }
500      
501      /** Helps BlbpPool find owner of a BLBP. */
502      WidarCorrelationProductGroup findGroup(String groupUUID)
503      {
504        for (CorrelatorBaseband bb : getBasebands())
505          for (CorrelatorSubband sb: bb.getSubbands())
506            for (CorrelationProductGroup cpg : sb.getCorrelationProductGroups())
507              if (cpg.getUUID().equals(groupUUID))
508                return (WidarCorrelationProductGroup)cpg;
509    
510        return null;
511      }
512      
513      //============================================================================
514      // VCI ELEMENTS
515      //============================================================================
516    
517      /**
518       * Expresses this configuration as a VCI <tt>SubArray</tt> object.
519       * 
520       * @param stationIds
521       *   the IDs of the antennas to be included in the returned object.
522       *   
523       * @return
524       *   a VCI <tt>SubArray</tt> object based on this configuration
525       *   and {@code stationIds}.
526       */
527      public SubArray toVciSubArray(List<Integer> stationIds)
528      {
529        SubArray vciObj = new SubArray();
530        
531        vciObj.setListOfStations(toVciListOfStations(stationIds));
532        vciObj.getStationInputOutput().add(toVciStationInputOutput());
533        
534        UUID uuid = UUID.randomUUID();
535        
536        vciObj.setAction(SubarrayActionType.CREATE);
537        vciObj.setActivationId(uuid.toString());
538        vciObj.setMappingOrder(1);
539        vciObj.setMsgId(Math.abs(uuid.hashCode())+1);
540        vciObj.setName(signalSource.getActiveFrontEnds().first().getBand().getDisplayName());
541        
542        XMLGregorianCalendar xgc = VciJaxbUtil.getXmlGregorianFor(new Date());
543        if (xgc != null)
544          vciObj.setTimeStamp(xgc);
545    
546        return vciObj;
547      }
548    
549      /**
550       * Expresses this configuration as a VCI <tt>StationInputOutput</tt> object.
551       * 
552       * @return
553       *   a VCI <tt>StationInputOutput</tt> object based on this configuration.
554       */
555      public StationInputOutput toVciStationInputOutput()
556      {
557        StationInputOutput vciObj = new StationInputOutput();
558        
559        List<BaseBand> vciBBs = vciObj.getBaseBand();
560        
561        for (CorrelatorBaseband bb : getBasebands())
562        {
563          BaseBand[] convertedBBs = ((WidarBaseband)bb).toVci();
564          for (BaseBand convertedBB : convertedBBs)
565            vciBBs.add(convertedBB);
566        }
567    
568        return vciObj;
569      }
570      
571      /**
572       * Expresses this configuration as a VCI <tt>ListOfStations</tt> object.
573       * 
574       * @param stationIds
575       *   the IDs of the antennas to be included in the returned object.
576       *   
577       * @return
578       *   a VCI <tt>ListOfStations</tt> object based on this configuration
579       *   and {@code stationIds}.
580       */
581      public ListOfStations toVciListOfStations(List<Integer> stationIds)
582      {
583        ListOfStations vciObj = new ListOfStations();
584        
585        vciObj.setAction(StationListActionType.ADD);
586        
587        List<Station>      vciStations = vciObj.getStation();
588        SortedSet<Integer> stationNums = new TreeSet<Integer>(stationIds);
589        
590        for (int id : stationNums)
591          vciStations.add(makeStation(id));
592        
593        return vciObj;
594      }
595      
596      /**
597       * Creates and populates a VCI <tt>Station</tt> object based on the given
598       * ID and the basebands held by this configuration.
599       */
600      private Station makeStation(int stationId)
601      {
602        Station vciStation = new Station();
603       
604        vciStation.setSid(stationId);
605        
606        List<BbParams> bbParamList = vciStation.getBbParams();
607        for (CorrelatorBaseband bb : getBasebands())
608          ((WidarBaseband)bb).addBbParams(bbParamList, stationId);
609    
610        return vciStation;
611      }
612      
613      //============================================================================
614      // 
615      //============================================================================
616    
617      /**
618       * Returns an XML representation of this configuration.
619       * @return an XML representation of this configuration.
620       * @throws JAXBException if anything goes wrong during the conversion to XML.
621       * @see #writeAsXmlTo(Writer)
622       */
623      public String toXml() throws JAXBException
624      {
625        return JaxbUtility.getSharedInstance().objectToXmlString(this);
626      }
627    
628      /**
629       * Writes an XML representation of this configuration to {@code writer}.
630       * @param writer the device to which XML is written.
631       * @throws JAXBException if anything goes wrong during the conversion to XML.
632       */
633      public void writeAsXmlTo(Writer writer) throws JAXBException
634      {
635        JaxbUtility.getSharedInstance().writeObjectAsXmlTo(writer, this, null);
636      }
637    
638      /**
639       * Creates a new configuration from the XML data in the given file.
640       * 
641       * @param xmlFile
642       *   the name of an XML file.  This method will attempt to locate
643       *   the file by using {@link Class#getResource(String)}.
644       *                
645       * @return
646       *   a new configuration from the XML data in the given file.
647       * 
648       * @throws FileNotFoundException
649       *   if the XML file cannot be found.
650       * 
651       * @throws JAXBException
652       *   if the schema file used (if any) is malformed, if the XML file cannot be
653       *   read, or if the XML file is not schema-valid.
654       * 
655       * @throws XMLStreamException
656       *   if there is a problem opening the XML file, if the XML is not well-formed,
657       *   or for some other "unexpected processing conditions".
658       */
659      public static EvlaWidarConfiguration fromXml(String xmlFile)
660        throws JAXBException, XMLStreamException, FileNotFoundException
661      {
662        return JaxbUtility.getSharedInstance()
663                          .xmlFileToObject(xmlFile, EvlaWidarConfiguration.class);
664      }
665    
666      /**
667       * Creates a new configuration based on the XML data read from {@code reader}.
668       * 
669       * @param reader
670       *   the source of the XML data.
671       *   If this value is <i>null</i>, <i>null</i> is returned.
672       *               
673       * @return
674       *  a new configuration based on the XML data read from {@code reader}.
675       * 
676       * @throws XMLStreamException
677       *   if the XML is not well-formed,
678       *   or for some other "unexpected processing conditions".
679       *           
680       * @throws JAXBException
681       *   if anything else goes wrong during the transformation.
682       */
683      public static EvlaWidarConfiguration fromXml(Reader reader)
684        throws JAXBException, XMLStreamException
685      {
686        return JaxbUtility.getSharedInstance()
687                          .readObjectAsXmlFrom(reader, EvlaWidarConfiguration.class, null);
688      }
689    
690      //============================================================================
691      // 
692      //============================================================================
693    
694      /**
695       *  Returns a copy of this configuration.
696       *  <p>
697       *  The returned clone is <i>almost</i> a deep copy.  The exceptions which
698       *  prevent us from saying it <i>is</i> a deep copy are:</p>
699       *  <ol>
700       *    <li>No baseline board pairs are associated with the correlation product
701       *        groups of the cloned configuration.</li>
702       *  </ol>
703       *  <p>
704       *  If anything goes wrong during the cloning procedure,
705       *  a {@code RuntimeException} will be thrown.</p>
706       */
707      @Override
708      public EvlaWidarConfiguration clone()
709      {
710        EvlaWidarConfiguration clone = null;
711        
712        try
713        {
714          clone = (EvlaWidarConfiguration)super.clone();
715          
716          //A new, EMPTY, pool.
717          //TODO clone pool contents?  Rem: ownerIDs are the UUIDs of corr prod grps,
718          //     and those UUIDs will be diff between orig and cloned configs.
719          clone.blbpPool = new BlbpPool(clone);
720        }
721        catch (Exception ex)
722        {
723          throw new RuntimeException(ex);
724        }
725    
726        return clone;
727      }
728      
729      /** Returns <i>true</i> if {@code o} is equal to this configuration. */
730      @Override
731      public boolean equals(Object o)
732      {
733        //Quick exit if super class says not equal
734        if (!super.equals(o))
735          return false;
736       
737        //A safe cast if we got this far
738        EvlaWidarConfiguration other = (EvlaWidarConfiguration)o;
739        
740        //Intentionally not comparing: blbpPool
741        
742        return other.maxAntennaCount == this.maxAntennaCount;
743      }
744    
745      /** Returns a hash code value for this configuration. */
746      @Override
747      public int hashCode()
748      {
749        //Taken from the Effective Java book by Joshua Bloch.
750        //The constants 17 & 37 are arbitrary & carry no meaning.
751        int result = super.hashCode();
752        
753        //You MUST keep this method in sync w/ the equals method
754        
755        result = 37 * result + maxAntennaCount;
756    
757        return result;
758      }
759    
760      //============================================================================
761      // 
762      //============================================================================
763      /*
764      public static void main(String[] args) throws Exception
765      {
766        EvlaWidarConfiguration widar = new WidarBuilder().makeCorrelator();
767    
768        List<Integer> antennas = new ArrayList<Integer>();
769        antennas.add(1);  antennas.add(10);  antennas.add(21);
770        
771        //ListOfStations vciObj = widar.toVciListOfStations(antennas);
772        //StationInputOutput vciObj = widar.toVciStationInputOutput();
773        SubArray vciObj = widar.toVciSubArray(antennas);
774        
775        VciJaxbUtil.writeObjectAsXmlTo(new java.io.PrintWriter(System.out), vciObj);
776      }
777      */
778      /*
779      public static void main(String[] args) throws Exception
780      {
781        EvlaWidarConfiguration widar = new WidarBuilder().makeCorrelator();
782    System.out.println("widar has "+widar.getBasebands().size()+" basebands.");
783        try
784        {
785          widar.writeAsXmlTo(new java.io.PrintWriter(System.out));
786        }
787        catch (Exception ex)
788        {
789          System.out.println("Trouble w/ widar.toXml.  Msg:");
790          System.out.println(ex.getMessage());
791          ex.printStackTrace();
792          
793          System.out.println("Attempting to write XML w/out schema verification:");
794          JaxbUtility.getSharedInstance().setLookForDefaultSchema(false);
795          try
796          {
797            widar.writeAsXmlTo(new java.io.PrintWriter(System.out));
798          }
799          catch (JAXBException ex2)
800          {
801            System.out.println("Still had trouble w/ widar.toXml.  Msg:");
802            System.out.println(ex.getMessage());
803            ex.printStackTrace();
804          }
805        }
806      }
807      */
808      /*
809      public static void main(String[] args) throws Exception
810      {
811        AntennaElectronics evla = new EvlaAntennaElectronics();
812        
813        EvlaWidarConfiguration widar = (EvlaWidarConfiguration)
814          CorrelatorConfiguration.makeFor(edu.nrao.sss.model.resource.CorrelatorName.WIDAR, evla);
815        
816        widar.addBasebandCollectionListener
817        (
818          new BasebandCollectionListener()
819          {
820            public void basebandCollectionChanged(BasebandCollectionEvent e)
821            {
822              System.out.println();
823              System.out.println("CHANGE DETECTED:");
824              for (CorrelatorBaseband bb : e.getBasebandsAdded())
825                System.out.println("  BB Added: "+bb.getName());
826              for (CorrelatorBaseband bb : e.getBasebandsRemoved())
827                System.out.println("  BB Removed: "+bb.getName());
828            }
829          }
830        );
831        
832        for (edu.nrao.sss.model.resource.ReceiverBand rb : 
833             edu.nrao.sss.model.resource.TelescopeType.EVLA.getReceivers())
834        {
835          evla.configureFor(rb);
836          evla.execute();
837          System.out.println("\nRECEIVER "+rb);
838          for (CorrelatorBaseband bb : widar.getBasebands())
839            System.out.println("  " + bb.toString());
840        }
841    
842        evla.configureFor(edu.nrao.sss.model.resource.ReceiverBand.EVLA_X);
843        evla.execute();
844        
845        widar.splitBasebandPair((WidarBasebandPair)widar.getBasebands().get(1));
846        for (CorrelatorBaseband bb : widar.getBasebands())
847          System.out.println("  " + bb.toString());
848        
849        widar.splitBasebandPair((WidarBasebandPair)widar.getBasebands().get(3));
850        for (CorrelatorBaseband bb : widar.getBasebands())
851          System.out.println("  " + bb.toString());
852        
853        widar.formPairWithPartner((WidarBasebandSinglet)widar.getBasebands().get(2));
854        for (CorrelatorBaseband bb : widar.getBasebands())
855          System.out.println("  " + bb.toString());
856    
857        System.out.println();
858        
859        for (CorrelatorBaseband bb : widar.getBasebands())
860          for (edu.nrao.evla.widar.xml.jaxb.BaseBand b : ((WidarBaseband)bb).toVci())
861            VciJaxbUtil.writeObjectAsXmlTo(new java.io.PrintWriter(System.out), b);
862    
863        System.out.println();
864      
865        widar.writeAsXmlTo(new java.io.PrintWriter(System.out));
866      }
867      */
868    }