001    package edu.nrao.sss.model.resource.evla;
002    
003    import java.awt.event.ActionEvent;
004    import java.awt.event.ActionListener;
005    import java.util.ArrayList;
006    import java.util.Collection;
007    import java.util.HashMap;
008    import java.util.HashSet;
009    import java.util.List;
010    import java.util.Map;
011    import java.util.Set;
012    import java.util.SortedMap;
013    import java.util.SortedSet;
014    import java.util.TreeMap;
015    import java.util.TreeSet;
016    
017    import edu.nrao.sss.astronomy.PolarizationType;
018    import edu.nrao.sss.electronics.DigitalSignal;
019    import edu.nrao.sss.electronics.Digitizer;
020    import edu.nrao.sss.electronics.LocalOscillator;
021    import edu.nrao.sss.electronics.SignalManifold;
022    import edu.nrao.sss.electronics.SignalPipe;
023    import edu.nrao.sss.electronics.SignalSource;
024    import edu.nrao.sss.electronics.SignalSwitch;
025    import edu.nrao.sss.electronics.SignalTransferSwitch;
026    import edu.nrao.sss.measure.Frequency;
027    import edu.nrao.sss.measure.FrequencyRange;
028    import edu.nrao.sss.measure.FrequencyUnits;
029    import edu.nrao.sss.model.resource.AntennaElectronics;
030    import edu.nrao.sss.model.resource.AntennaElectronicsConfiguration;
031    import edu.nrao.sss.model.resource.AntennaFrontEnd;
032    import edu.nrao.sss.model.resource.ReceiverBand;
033    import edu.nrao.sss.model.resource.TelescopeType;
034    
035    /**
036     * The electronics of an EVLA antenna.  This class represents the hardware from
037     * the feed horns through the digitizers.
038     * <p>
039     * <b>Version Info:</b>
040     * <table style="margin-left:2em">
041     *   <tr><td>$Revision: 1701 $</td></tr>
042     *   <tr><td>$Date: 2008-11-07 16:24:23 -0700 (Fri, 07 Nov 2008) $</td></tr>
043     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
044     * </table></p>
045     * 
046     * @author David M. Harland
047     * @since 2007-10-30
048     */
049    public class EvlaAntennaElectronics
050      implements AntennaElectronics
051    {
052      //--------- ELECTRONIC COMPONENTS ---------
053      
054      //Feeds
055      private Map<ReceiverBand, AntennaFrontEnd> frontEnds;
056      
057      //Switches
058      private SignalSwitch switch1_1, switch1_2;
059      private SignalSwitch switch2_1, switch2_2;
060      private SignalSwitch switch3_1, switch3_2;
061      private SignalSwitch switch4_1, switch4_2;
062    
063      private SignalTransferSwitch switch5, switch6;
064    
065      private SignalSwitch switch7, switch8, switch9;
066      
067      //Local Oscillators
068      private LocalOscillator L301_1, L301_2;
069      private LocalOscillator L302_1, L302_2, L302_3, L302_4;
070      
071      //Converters
072      private T301 fourPConverter;
073      private T302 lscConverter;
074      private T303 uxConverter;
075      private T304 downConverterA, downConverterB, downConverterC, downConverterD;
076      
077      //Digitizers
078      private D301 samplerA, samplerB, samplerC, samplerD;
079    
080      //--------- HELPER VARIABLES ---------
081    
082      //Putting all the signal switches in one collection makes part
083      //of the code easier.
084      private HashSet<SignalSwitch> signalSwitches;
085      
086      //Maps receiver bands to text codes for the converters they use.
087      //The codes are "UX", "LSC", and "X" (which is not a converter).
088      //Bands 4 & P are treated specially and here are mapped to "LSC".
089      private HashMap<ReceiverBand, String> receiverConverterMap;
090      
091      //Maps some receiver bands to the text name of an input pole of the top input
092      //switch on the LSC converter.  Bands L, S, & C map to "S2-2"; bands 4 & P
093      //map to "4P.TOP".
094      private HashMap<ReceiverBand, String> lscTopInputMap;
095      
096      //Maps some receiver bands to the text name of an input pole of the top input
097      //switch on the LSC converter.  Bands L, S, & C map to "S2-1"; bands 4 & P
098      //map to "4P.BOTTOM".
099      private HashMap<ReceiverBand, String> lscBottomInputMap;
100      
101      //Used by the methods that configure these electronics by specifying
102      //receiver bands.  These plans are supplied from an external source and,
103      //if non-null, are used in place our our default plans.
104      private Map<ReceiverBand, Map<String, Frequency>> tuningPlans;
105      
106      private static Map<ReceiverBand, Map<String, Frequency>> DEFAULT_TUNING_PLANS;
107      
108      //Default sampler settings
109      private Map<ReceiverBand, Integer> defaultSampleBits;
110      
111      //The front end (receiver) for which we're currently configured.
112      //TODO need to handle multiple simultaneous front ends, such as 4 & P
113      private AntennaFrontEnd currentFrontEnd;
114      
115      //--------- OTHER ---------
116    
117      Collection<DigitalSignal> mostRecentSignals;
118      List<List<DigitalSignal>> mostRecentSignalPairs;
119    
120      //Execution listeners
121      private List<ActionListener> execListeners;
122    
123      //Holds list of messages regarding the most recent configuration
124      private List<String> configurationProblems;
125      
126      //============================================================================
127      // OBJECT CONSTRUCTION
128      //============================================================================
129    
130      /** Creates a new instance. */
131      public EvlaAntennaElectronics()  { construct(); }
132      
133      /** Used by constructors and clone(). */
134      private void construct()
135      {
136        constructInternalComponents();
137        connectInternalComponents();
138        nameTheSwitchPoles();
139        mapReceiversToConverters();
140        mapReceiversToSamplingBits();
141        
142        initTuners();
143        
144        mostRecentSignals     = new ArrayList<DigitalSignal>();
145        mostRecentSignalPairs = new ArrayList<List<DigitalSignal>>();
146        
147        execListeners = new ArrayList<ActionListener>();
148        
149        tuningPlans = new HashMap<ReceiverBand, Map<String,Frequency>>();
150        
151        configurationProblems = new ArrayList<String>();
152        
153        //Choice of X band is arbitrary
154        configureFor(ReceiverBand.EVLA_X);
155      }
156      
157      /**
158       * Builds, but does not connect, the internal components of this device.
159       */
160      private void constructInternalComponents()
161      {
162        constructSwitches();
163        constructOscillators();
164        constructFrontEndsAndConnectOscillators();
165        constructConvertersAndConnectOscillators();
166        constructDigitizers();
167      }
168    
169      /**
170       * Builds the switches that live in between the converters, feeds,
171       * and digitizers.
172       */
173      private void constructSwitches()
174      {
175        switch1_1 = SignalSwitch.makeMultiInputSwitch("S1-1", 4);
176        switch1_2 = SignalSwitch.makeMultiInputSwitch("S1-2", 4);
177        switch2_1 = SignalSwitch.makeMultiInputSwitch("S2-1", 4);
178        switch2_2 = SignalSwitch.makeMultiInputSwitch("S2-2", 4);
179        switch3_1 = SignalSwitch.makeMultiInputSwitch("S3-1", 4);
180        switch3_2 = SignalSwitch.makeMultiInputSwitch("S3-2", 4);
181        switch4_1 = SignalSwitch.makeMultiInputSwitch("S4-1", 4);
182        switch4_2 = SignalSwitch.makeMultiInputSwitch("S4-2", 4);
183    
184        switch5 = new SignalTransferSwitch("S5", 2);
185        switch6 = new SignalTransferSwitch("S6", 2);
186        
187        switch7 = SignalSwitch.makeMultiOutputSwitch("S7", 4);
188        switch8 = SignalSwitch.makeMultiOutputSwitch("S8", 4);
189        switch9 = SignalSwitch.makeMultiOutputSwitch("S9", 4);
190      }
191    
192      /** Builds the local oscillators. */
193      private void constructOscillators()
194      {
195        //L301s
196        Frequency lowFreq  = new Frequency("11904.0", FrequencyUnits.MEGAHERTZ);
197        Frequency highFreq = new Frequency("20352.0", FrequencyUnits.MEGAHERTZ);
198        Frequency stepSize = new Frequency(  "256.0", FrequencyUnits.MEGAHERTZ);
199        
200        L301_1 = new LocalOscillator(new FrequencyRange(lowFreq,highFreq),stepSize);
201        L301_2 = L301_1.clone();
202        
203        L301_1.setName("L301-1");
204        L301_2.setName("L301-2");
205          
206        //L302s
207        lowFreq  = new Frequency("10.8", FrequencyUnits.GIGAHERTZ);
208        highFreq = new Frequency("14.8", FrequencyUnits.GIGAHERTZ);
209        
210        L302_1 = new LocalOscillator(new FrequencyRange(lowFreq, highFreq));
211        L302_2 = L302_1.clone();
212        L302_3 = L302_1.clone();
213        L302_4 = L302_1.clone();
214    
215        L302_1.setName("L302-1");
216        L302_2.setName("L302-2");
217        L302_3.setName("L302-3");
218        L302_4.setName("L302-4");
219      }
220      
221      /** Builds the receiver band front ends and connects them to LOs. */
222      private void constructFrontEndsAndConnectOscillators()
223      {
224        Map<ReceiverBand, SignalSource> loSignals =
225          new HashMap<ReceiverBand, SignalSource>();
226        
227        loSignals.put(ReceiverBand.EVLA_Q,  switch7.getOutputPipe(0));
228        loSignals.put(ReceiverBand.EVLA_Ka, switch7.getOutputPipe(1));
229        loSignals.put(ReceiverBand.EVLA_K,  switch7.getOutputPipe(2));
230        
231        frontEnds = AntennaFrontEnd.makeEvlaFrontEnds(loSignals);
232      }
233    
234      /** Builds the converters and connects them to the LOs. */
235      private void constructConvertersAndConnectOscillators()
236      {
237        //Converters that use no external LOs
238        fourPConverter = new T301();
239        
240        //Converters and switches that take input from L301s.
241        //Rem: indexing starts from 0 in methods, but from 1 on diagrams
242        switch7.getInputPipe(0).connectInputTo(switch8.getOutputPipe(1));
243        switch8.getInputPipe(0).connectInputTo(L301_1);
244        switch9.getInputPipe(0).connectInputTo(L301_2);
245    
246        lscConverter = new T302(switch8.getOutputPipe(2),   //switch position 3
247                                switch9.getOutputPipe(2));  //switch position 3
248    
249        uxConverter = new T303(switch7.getOutputPipe(3),    //switch position 4
250                               switch9.getOutputPipe(1));   //switch position 2
251        
252        //Converters that take input from L302s
253        downConverterA = new T304("T304-A", L302_1, L302_3);
254        downConverterB = new T304("T304-B", L302_2, L302_4);
255        downConverterC = new T304("T304-C", L302_1, L302_3);
256        downConverterD = new T304("T304-D", L302_2, L302_4);
257      }
258    
259      /** Builds the digitizers. */
260      private void constructDigitizers()
261      {
262        samplerA = new D301("D301 (DTS A)");
263        samplerB = new D301("D302 (DTS B)");
264        samplerC = new D301("D303 (DTS C)");
265        samplerD = new D301("D304 (DTS D)");
266        
267        samplerA.setOutputNames("A0", "A1", "C1");
268        samplerB.setOutputNames("B0", "B1", "D1");
269        samplerC.setOutputNames("C0", "A2", "C2");
270        samplerD.setOutputNames("D0", "B2", "D2");
271      }
272    
273      /**
274       * Connects the prebuilt internal components of this device to one another.
275       */
276      private void connectInternalComponents()
277      {
278        connectFrontEndOutputs();
279        connectConverters();
280        connectDigitizers();
281      }
282    
283      /** Connects the outputs of the front ends to their targets. */
284      private void connectFrontEndOutputs()
285      {
286        //BANDS Q, Ka, K, & Ku
287        //  LCP
288        switch1_2.getInputPipe(0).connectInputTo(frontEnds.get(ReceiverBand.EVLA_Q ).getOuputPipe(PolarizationType.L));
289        switch1_2.getInputPipe(1).connectInputTo(frontEnds.get(ReceiverBand.EVLA_Ka).getOuputPipe(PolarizationType.L));
290        switch1_2.getInputPipe(2).connectInputTo(frontEnds.get(ReceiverBand.EVLA_K ).getOuputPipe(PolarizationType.L));
291        switch1_2.getInputPipe(3).connectInputTo(frontEnds.get(ReceiverBand.EVLA_Ku).getOuputPipe(PolarizationType.L));
292        //  RCP
293        switch1_1.getInputPipe(0).connectInputTo(frontEnds.get(ReceiverBand.EVLA_Q ).getOuputPipe(PolarizationType.R));
294        switch1_1.getInputPipe(1).connectInputTo(frontEnds.get(ReceiverBand.EVLA_Ka).getOuputPipe(PolarizationType.R));
295        switch1_1.getInputPipe(2).connectInputTo(frontEnds.get(ReceiverBand.EVLA_K ).getOuputPipe(PolarizationType.R));
296        switch1_1.getInputPipe(3).connectInputTo(frontEnds.get(ReceiverBand.EVLA_Ku).getOuputPipe(PolarizationType.R));
297        
298        //BANDS C, S, & L
299        //  LCP
300        switch2_2.getInputPipe(0).connectInputTo(frontEnds.get(ReceiverBand.EVLA_L ).getOuputPipe(PolarizationType.L));
301        switch2_2.getInputPipe(1).connectInputTo(frontEnds.get(ReceiverBand.EVLA_S ).getOuputPipe(PolarizationType.L));
302        switch2_2.getInputPipe(2).connectInputTo(frontEnds.get(ReceiverBand.EVLA_C ).getOuputPipe(PolarizationType.L));
303        //  RCP
304        switch2_1.getInputPipe(0).connectInputTo(frontEnds.get(ReceiverBand.EVLA_L ).getOuputPipe(PolarizationType.R));
305        switch2_1.getInputPipe(1).connectInputTo(frontEnds.get(ReceiverBand.EVLA_S ).getOuputPipe(PolarizationType.R));
306        switch2_1.getInputPipe(2).connectInputTo(frontEnds.get(ReceiverBand.EVLA_C ).getOuputPipe(PolarizationType.R));
307        
308        //BAND X
309        //  LCP
310        SignalManifold xTopFork = new SignalManifold(2);
311        xTopFork.getInputPipe().connectInputTo(frontEnds.get(ReceiverBand.EVLA_X).getOuputPipe(PolarizationType.L));
312        switch3_2.getInputPipe(0).connectInputTo(xTopFork.getOutputPipe(0));
313        switch4_2.getInputPipe(0).connectInputTo(xTopFork.getOutputPipe(1));
314        //  RCP
315        SignalManifold xBottomFork = new SignalManifold(2);
316        xBottomFork.getInputPipe().connectInputTo(frontEnds.get(ReceiverBand.EVLA_X).getOuputPipe(PolarizationType.R));
317        switch3_1.getInputPipe(0).connectInputTo(xBottomFork.getOutputPipe(0));
318        switch4_1.getInputPipe(0).connectInputTo(xBottomFork.getOutputPipe(1));
319        
320        //BANDS P & 4
321        //  LCP
322        List<SignalPipe> fourPTopInputs = fourPConverter.getTopInputPipes();
323        fourPTopInputs.get(0).connectInputTo(frontEnds.get(ReceiverBand.EVLA_P).getOuputPipe(PolarizationType.L));
324        fourPTopInputs.get(1).connectInputTo(frontEnds.get(ReceiverBand.EVLA_4).getOuputPipe(PolarizationType.L));
325        //  RCP
326        List<SignalPipe> fourPBottomInputs = fourPConverter.getBottomInputPipes();
327        fourPBottomInputs.get(0).connectInputTo(frontEnds.get(ReceiverBand.EVLA_P).getOuputPipe(PolarizationType.R));
328        fourPBottomInputs.get(1).connectInputTo(frontEnds.get(ReceiverBand.EVLA_4).getOuputPipe(PolarizationType.R));
329      }
330    
331      /**
332       * Connects those inputs and outputs of the converters that have not already
333       * been connected.  (The LOs were connected elsewhere.)
334       */
335      private void connectConverters()
336      {
337        //T303 UX CONVERTER
338        List<SignalPipe> uxTopOutputs    = uxConverter.getTopOutputPipes();
339        List<SignalPipe> uxBottomOutputs = uxConverter.getBottomOutputPipes();
340        //  LCP
341        uxConverter.getTopInputPipe().connectInputTo(switch1_2.getOutputPipe(0));
342        switch3_2.getInputPipe(2).connectInputTo(uxTopOutputs.get(0));
343        switch4_2.getInputPipe(2).connectInputTo(uxTopOutputs.get(1));
344        //  RCP
345        uxConverter.getBottomInputPipe().connectInputTo(switch1_1.getOutputPipe(0));
346        switch3_1.getInputPipe(2).connectInputTo(uxBottomOutputs.get(0));
347        switch4_1.getInputPipe(2).connectInputTo(uxBottomOutputs.get(1));
348        
349        //T302 LSC & T301 4P CONVERTERS
350        List<SignalPipe> lscTopInputs    = lscConverter.getTopInputPipes();
351        List<SignalPipe> lscBottomInputs = lscConverter.getBottomInputPipes();
352        //  LCP
353        lscTopInputs.get(0).connectInputTo(switch2_2.getOutputPipe(0));
354        lscTopInputs.get(1).connectInputTo(fourPConverter.getTopOutputPipe());
355        switch3_2.getInputPipe(1).connectInputTo(lscConverter.getTopLO1Output());
356        switch4_2.getInputPipe(1).connectInputTo(lscConverter.getTopLO2Output());
357        //  RCP
358        lscBottomInputs.get(0).connectInputTo(switch2_1.getOutputPipe(0));
359        lscBottomInputs.get(1).connectInputTo(fourPConverter.getBottomOutputPipe());
360        switch3_1.getInputPipe(1).connectInputTo(lscConverter.getBottomLO1Output());
361        switch4_1.getInputPipe(1).connectInputTo(lscConverter.getBottomLO2Output());
362        
363        //INTER-CONVERTER SWITCHES
364        switch3_1.getOutputPipe(0).connectOutputTo(switch5.getInputPipe(0));
365        switch4_1.getOutputPipe(0).connectOutputTo(switch6.getInputPipe(0));
366        switch3_2.getOutputPipe(0).connectOutputTo(switch5.getInputPipe(1));
367        switch4_2.getOutputPipe(0).connectOutputTo(switch6.getInputPipe(1));
368        
369        //T304 DOWNCONVERTERS
370        downConverterA.getInputPipe().connectInputTo(switch5.getOutputPipe(0));
371        downConverterB.getInputPipe().connectInputTo(switch6.getOutputPipe(0));
372        downConverterC.getInputPipe().connectInputTo(switch5.getOutputPipe(1));
373        downConverterD.getInputPipe().connectInputTo(switch6.getOutputPipe(1));
374      }
375    
376      /** Connects the inputs of the digitizers. */
377      private void connectDigitizers()
378      {
379        //EIGHT BIT INPUTS
380        samplerA.getEightBitInputPipe().connectInputTo(downConverterA.getLO1OutputPipe(0));
381        samplerB.getEightBitInputPipe().connectInputTo(downConverterB.getLO1OutputPipe(0));
382        samplerC.getEightBitInputPipe().connectInputTo(downConverterC.getLO1OutputPipe(0));
383        samplerD.getEightBitInputPipe().connectInputTo(downConverterD.getLO1OutputPipe(0));
384    
385        //THREE BIT INPUTS
386        List<SignalPipe> inputPipes;
387        //  A
388        inputPipes = samplerA.getThreeBitInputPipes();
389        inputPipes.get(0).connectInputTo(downConverterA.getLO1OutputPipe(1));
390        inputPipes.get(1).connectInputTo(downConverterC.getLO1OutputPipe(1));
391        //  B
392        inputPipes = samplerB.getThreeBitInputPipes();
393        inputPipes.get(0).connectInputTo(downConverterB.getLO1OutputPipe(1));
394        inputPipes.get(1).connectInputTo(downConverterD.getLO1OutputPipe(1));
395        //  C
396        inputPipes = samplerC.getThreeBitInputPipes();
397        inputPipes.get(0).connectInputTo(downConverterA.getLO2OutputPipe());
398        inputPipes.get(1).connectInputTo(downConverterC.getLO2OutputPipe());
399        //  D 
400        inputPipes = samplerD.getThreeBitInputPipes();
401        inputPipes.get(0).connectInputTo(downConverterB.getLO2OutputPipe());
402        inputPipes.get(1).connectInputTo(downConverterD.getLO2OutputPipe());
403      }
404      
405      /** Naming the switch poles helps with the configuration logic. */
406      private void nameTheSwitchPoles()
407      {
408        //Name the poles from receiver bands and converters.
409        //(Converter names must be coordinated w/ method mapReceiversToConverters)
410        switch1_1.nameInputs(ReceiverBand.EVLA_Q.name(),
411                             ReceiverBand.EVLA_Ka.name(),
412                             ReceiverBand.EVLA_K.name(),
413                             ReceiverBand.EVLA_Ku.name());
414        switch1_1.nameOutputs("UX");
415        
416        switch1_2.nameInputs(ReceiverBand.EVLA_Q.name(),
417                             ReceiverBand.EVLA_Ka.name(),
418                             ReceiverBand.EVLA_K.name(),
419                             ReceiverBand.EVLA_Ku.name());
420        switch1_2.nameOutputs("UX");
421        
422        switch2_1.nameInputs(ReceiverBand.EVLA_L.name(),
423                             ReceiverBand.EVLA_S.name(),
424                             ReceiverBand.EVLA_C.name(),
425                             "spare");
426        switch2_1.nameOutputs("LSC");
427        
428        switch2_2.nameInputs(ReceiverBand.EVLA_L.name(),
429                             ReceiverBand.EVLA_S.name(),
430                             ReceiverBand.EVLA_C.name(),
431                             "spare");
432        switch2_2.nameOutputs("LSC");
433    
434        switch3_1.nameInputs("X", "LSC", "UX", "spare");
435        switch3_1.nameOutputs("S5");
436    
437        switch3_2.nameInputs("X", "LSC", "UX", "spare");
438        switch3_2.nameOutputs("S5");
439    
440        switch4_1.nameInputs("X", "LSC", "UX", "spare");
441        switch4_1.nameOutputs("S6");
442    
443        switch4_2.nameInputs("X", "LSC", "UX", "spare");
444        switch4_2.nameOutputs("S6");
445        
446        switch7.nameInputs("S8");
447        switch7.nameOutputs(ReceiverBand.EVLA_Q.name(),
448                            ReceiverBand.EVLA_Ka.name(),
449                            ReceiverBand.EVLA_K.name(),
450                            "UX");
451        
452        switch8.nameInputs("L301-1");
453        switch8.nameOutputs("off", "UX", "LSC", "spare");
454        
455        switch9.nameInputs("L301-2");
456        switch9.nameOutputs("off", "UX", "LSC", "spare");
457        
458        //Put all (non-transfer) switches in one collection
459        signalSwitches = new HashSet<SignalSwitch>();
460        signalSwitches.add(switch1_1);  signalSwitches.add(switch1_2);
461        signalSwitches.add(switch2_1);  signalSwitches.add(switch2_2);
462        signalSwitches.add(switch3_1);  signalSwitches.add(switch3_2);
463        signalSwitches.add(switch4_1);  signalSwitches.add(switch4_2);
464        signalSwitches.add(switch7);
465        signalSwitches.add(switch8);
466        signalSwitches.add(switch9);
467        
468        //Name some switches internal to the converters
469        fourPConverter.nameOutputPolesOfOutputSwitches("LSC", "LSC");
470        lscConverter.nameInputPolesOfInputSwitches("S2-2", "4P.TOP", "S2-1", "4P.BOTTOM");
471        uxConverter.nameOutputPolesOfOutputSwitches("S3-2", "S4-2", "S3-1", "S4-1");
472      }
473      
474      /** This method helps the switch configuration logic. */
475      private void mapReceiversToConverters()
476      {
477        receiverConverterMap = new HashMap<ReceiverBand, String>();
478        
479        //The converter names below are the same as those used
480        //for naming the switch poles.
481        
482        //Q, Ka, K, and Ku use the T303 UX Converter
483        receiverConverterMap.put(ReceiverBand.EVLA_Q,  "UX");
484        receiverConverterMap.put(ReceiverBand.EVLA_Ka, "UX");
485        receiverConverterMap.put(ReceiverBand.EVLA_K,  "UX");
486        receiverConverterMap.put(ReceiverBand.EVLA_Ku, "UX");
487        
488        //X doesn't use one
489        receiverConverterMap.put(ReceiverBand.EVLA_X, "X");
490    
491        //C, S, L, P, & 4 use T302 LSC Converter (we ignore T301 4P Converter)
492        receiverConverterMap.put(ReceiverBand.EVLA_C, "LSC");
493        receiverConverterMap.put(ReceiverBand.EVLA_S, "LSC");
494        receiverConverterMap.put(ReceiverBand.EVLA_L, "LSC");
495        receiverConverterMap.put(ReceiverBand.EVLA_P, "LSC");
496        receiverConverterMap.put(ReceiverBand.EVLA_4, "LSC");
497    
498        //Input switches on T302 LSC Converter
499        lscTopInputMap = new HashMap<ReceiverBand, String>();
500        lscTopInputMap.put(ReceiverBand.EVLA_C, "S2-2");
501        lscTopInputMap.put(ReceiverBand.EVLA_S, "S2-2");
502        lscTopInputMap.put(ReceiverBand.EVLA_L, "S2-2");
503        lscTopInputMap.put(ReceiverBand.EVLA_P, "4P.TOP");
504        lscTopInputMap.put(ReceiverBand.EVLA_4, "4P.TOP");
505        
506        lscBottomInputMap = new HashMap<ReceiverBand, String>();
507        lscBottomInputMap.put(ReceiverBand.EVLA_C, "S2-1");
508        lscBottomInputMap.put(ReceiverBand.EVLA_S, "S2-1");
509        lscBottomInputMap.put(ReceiverBand.EVLA_L, "S2-1");
510        lscBottomInputMap.put(ReceiverBand.EVLA_P, "4P.BOTTOM");
511        lscBottomInputMap.put(ReceiverBand.EVLA_4, "4P.BOTTOM");
512      }
513    
514      /** This method helps the T304 and D301 configuration logic. */
515      private void mapReceiversToSamplingBits()
516      {
517        defaultSampleBits = new HashMap<ReceiverBand, Integer>();
518    
519        defaultSampleBits.put(ReceiverBand.EVLA_Q,  3);
520        defaultSampleBits.put(ReceiverBand.EVLA_Ka, 3);
521        defaultSampleBits.put(ReceiverBand.EVLA_K,  3);
522        defaultSampleBits.put(ReceiverBand.EVLA_Ku, 3);
523        defaultSampleBits.put(ReceiverBand.EVLA_X,  3);
524        defaultSampleBits.put(ReceiverBand.EVLA_C,  3);
525        defaultSampleBits.put(ReceiverBand.EVLA_S,  8);
526        defaultSampleBits.put(ReceiverBand.EVLA_L,  8);
527        defaultSampleBits.put(ReceiverBand.EVLA_P,  8);
528        defaultSampleBits.put(ReceiverBand.EVLA_4,  8);
529      }
530      
531      //============================================================================
532      // INTERFACE AntennaElectronics AND SUPPORTING METHODS
533      //============================================================================
534      //----------------------------------------------------------------------------
535      // Execution & Output
536      //----------------------------------------------------------------------------
537    
538      /* (non-Javadoc)
539       * @see edu.nrao.sss.model.resource.AntennaElectronics#execute()
540       */
541      public Collection<DigitalSignal> execute()
542      {
543        samplerA.eraseSignalMemory();
544        samplerB.eraseSignalMemory();
545        samplerC.eraseSignalMemory();
546        samplerD.eraseSignalMemory();
547    
548        currentFrontEnd.execute();
549        
550        updateSignals();
551        
552        notifyExecutionListeners();
553        
554        return getSignals();
555      }
556      
557      /** Called by execute(); remembers output signals for quick fetch. */
558      private void updateSignals()
559      {
560        mostRecentSignals.clear();
561        
562        mostRecentSignals.addAll(samplerA.getSignals());
563        mostRecentSignals.addAll(samplerC.getSignals());
564        mostRecentSignals.addAll(samplerB.getSignals());
565        mostRecentSignals.addAll(samplerD.getSignals());
566        
567        mostRecentSignalPairs.clear();  //Will build just-in-time
568      }
569    
570      /* (non-Javadoc)
571       * @see edu.nrao.sss.model.resource.AntennaElectronics#getSignals()
572       */
573      public Collection<DigitalSignal> getSignals()
574      {
575        return new ArrayList<DigitalSignal>(mostRecentSignals);
576      }
577    
578      /* (non-Javadoc)
579       * @see edu.nrao.sss.model.resource.AntennaElectronics#getSignalPairs()
580       */
581      public List<List<DigitalSignal>> getSignalPairs()
582      {
583        //The execute method cleared the mostRecentSignalPairs.  If it is
584        //still empty, but we have signals, we need to rebuild pairs.
585        if (mostRecentSignalPairs.size() == 0 && mostRecentSignals.size() > 0)
586          buildSignalPairs();
587        
588        List<List<DigitalSignal>> pairs = new ArrayList<List<DigitalSignal>>();
589        
590        for (List<DigitalSignal> pair : mostRecentSignalPairs)
591          pairs.add(new ArrayList<DigitalSignal>(pair));
592    
593        return pairs;
594      }
595    
596      //Expected names for the digital signals, ordered & arranged into pairs
597      private static final String[][] SIGNAL_NAMES =
598      {
599        {"A0", "C0"}, {"B0", "D0"},
600        {"A1", "C1"}, {"A2", "C2"},
601        {"B1", "D1"}, {"B2", "D2"}
602      };
603      
604      private void buildSignalPairs()
605      {
606        //Make temp map of signals, using name as key
607        Map<String, DigitalSignal> signalMap = new HashMap<String, DigitalSignal>();
608        
609        for (DigitalSignal signal : mostRecentSignals)
610          signalMap.put(signal.getName(), signal);
611        
612        //Iterate through names looking for match in temp map
613        for (String[] pairNames : SIGNAL_NAMES)
614        {
615          ArrayList<DigitalSignal> pair = new ArrayList<DigitalSignal>();
616          
617          //If map has one member of pair, make new pair.
618          //Note: other member could be signal or null.
619          if (signalMap.containsKey(pairNames[0]) ||
620              signalMap.containsKey(pairNames[1]))
621          {
622            pair.add(signalMap.get(pairNames[0]));
623            pair.add(signalMap.get(pairNames[1]));
624            
625            mostRecentSignalPairs.add(pair);
626          }
627        }
628      }
629    
630      private void notifyExecutionListeners()
631      {
632        ActionEvent event =
633          new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "execute");
634       
635        //TODO notify listeners in a diff thread?
636        for (ActionListener listener : execListeners)
637          listener.actionPerformed(event);
638      }
639    
640      public void addExecutionListener(ActionListener newListener)
641      {
642        if (newListener != null)
643          execListeners.add(newListener);
644      }
645      
646      public void removeExecutionListener(ActionListener formerListener)
647      {
648        execListeners.remove(formerListener);
649      }
650    
651      //----------------------------------------------------------------------------
652      // Intelligent Configuration
653      //----------------------------------------------------------------------------
654    
655      /**
656       * Configures these electronics to work with the given receiver band.
657       * 
658       * @param band
659       *   the receiver band, or frequency range, for which these electronics
660       *   should be configured.  If this band does not belong to the EVLA
661       *   telescope, no reconfiguration will occur and this method will
662       *   return <i>false</i>.
663       *   
664       * @return
665       *   <i>true</i> if the configuration occurred without any problems.
666       *   If any problems were encountered, they will be listed in
667       *   {@link #getConfigurationProblems()}.
668       *   
669       * @see AntennaElectronics#configureFor(ReceiverBand)
670       */
671      public boolean configureFor(ReceiverBand band)
672      {
673        boolean success = true;
674        
675        configurationProblems.clear();
676        
677        //Make sure incoming band belongs to EVLA telescope
678        if (band == null || !band.getTelescope().equals(TelescopeType.EVLA))
679        {
680          success = false;
681          configurationProblems.add(
682            "Reconfiguration of the EVLA antenna electronics was unsuccessful "+
683            " because receiver band '" + band + "' is not an EVLA receiver.");
684        }
685        else //we have a good band
686        {
687          configureFrontEndsFor(band);
688          configureSwitchesFor(band);
689          tuneLocOscsFor(band);
690          
691          int bits = defaultSampleBits.get(band);
692          
693          boolean wide = (bits <= 3);
694          downConverterA.configureForWidestOutput(wide);
695          downConverterB.configureForWidestOutput(wide);
696          downConverterC.configureForWidestOutput(wide);
697          downConverterD.configureForWidestOutput(wide);
698          
699          samplerA.setBitsPerSample(bits);
700          samplerB.setBitsPerSample(bits);
701          samplerC.setBitsPerSample(bits);
702          samplerD.setBitsPerSample(bits);
703        }
704        
705        return success;
706      }
707      
708    //private boolean configureFrontEndsFor(Set<ReceiverBand> bands)
709      private boolean configureFrontEndsFor(ReceiverBand band)
710      {
711        //Turn on the requested receiver and turn all other off
712        //(except that we keep 4 & P on all the time)
713        for (AntennaFrontEnd fe : frontEnds.values())
714        {
715        //if (bands.contains(fe.getBand()))
716          if (band.equals(fe.getBand()) ||
717              fe.getBand().equals(ReceiverBand.EVLA_4) ||
718              fe.getBand().equals(ReceiverBand.EVLA_P))
719          {
720            fe.turnOn();
721          }
722          else
723          {
724            fe.turnOff();
725          }
726        }
727        //TODO handle multiple receivers
728      //currentFrontEnd = frontEnds.get(bands.iterator().next());
729        currentFrontEnd = frontEnds.get(band);
730        
731        return true;
732      }
733      
734      private boolean configureSwitchesFor(ReceiverBand band)
735      {
736        configurationProblems.clear();
737        
738        String receiver  = band.name();
739        String converter = receiverConverterMap.get(band);
740        
741        for (SignalSwitch ss : signalSwitches)
742        {
743          //Input poles
744          if      (ss.hasInputNamed(receiver ))  ss.selectInput(receiver );
745          else if (ss.hasInputNamed(converter))  ss.selectInput(converter);
746        //else leave as is
747    
748          //Output poles
749          if      (ss.hasOutputNamed(receiver ))  ss.selectOutput(receiver );
750          else if (ss.hasOutputNamed(converter))  ss.selectOutput(converter);
751        //else leave as is
752        }
753        
754        //Input to T302 LSC Converter
755        String lscTopInputPole = lscTopInputMap.get(band);
756        if (lscTopInputPole != null)
757        {
758          lscConverter.setInputSwitches(lscTopInputPole, lscBottomInputMap.get(band));
759        }
760        
761        //TODO T303 should be able to look at input freqs and set own switches.  For now:
762        //Special T303 logic for Ku Band
763        if (band == ReceiverBand.EVLA_Ku)
764          uxConverter.setOutputSwitches("LO1", "LO2");
765        else
766          uxConverter.setOutputSwitches("DIRECT", "LO2");
767        
768        return true;
769      }
770      
771      /**
772       * <i>This method is not yet programmed;
773       *  it will send one band to {@link #configureFor(ReceiverBand)}.</i>
774       */
775      public boolean configureFor(Set<ReceiverBand> bands)
776      {
777        //TODO handle multiple receivers
778        return configureFor(bands.iterator().next());
779      }
780    
781      /**
782       * <i>This method is not yet supported.</i>
783       */
784      public boolean configureToProduce(Collection<DigitalSignal> signals)
785      {
786        throw new UnsupportedOperationException("not yet programmed"); //TODO code me
787      }
788    
789      //----------------------------------------------------------------------------
790      // Manual Configuration
791      //----------------------------------------------------------------------------
792    
793      /* (non-Javadoc)
794       * @see edu.nrao.sss.model.resource.AntennaElectronics#getLocalOscillators()
795       */
796      public SortedMap<String, LocalOscillator> getLocalOscillators()
797      {
798        SortedMap<String, LocalOscillator> map =
799          new TreeMap<String, LocalOscillator>();
800        
801        map.put(L301_1.getName(), L301_1);
802        map.put(L301_2.getName(), L301_2);
803        
804        map.put(L302_1.getName(), L302_1);
805        map.put(L302_2.getName(), L302_2);
806        map.put(L302_3.getName(), L302_3);
807        map.put(L302_4.getName(), L302_4);
808    
809        return map;
810      }
811    
812      /* (non-Javadoc)
813       * @see edu.nrao.sss.model.resource.AntennaElectronics#getSwitches()
814       */
815      public SortedMap<String, SignalSwitch> getSwitches()
816      {
817        //First our own switches
818        SortedMap<String, SignalSwitch> map = new TreeMap<String, SignalSwitch>();
819        
820        for (SignalSwitch ss : signalSwitches)
821          map.put(ss.getName(), ss);
822        
823        //Now those of our complex devices
824        fourPConverter.addSwitchesTo(map);
825    
826        lscConverter.addSwitchesTo(map);
827        
828        uxConverter.addSwitchesTo(map);
829    
830        downConverterA.addSwitchesTo(map);
831        downConverterB.addSwitchesTo(map);
832        downConverterC.addSwitchesTo(map);
833        downConverterD.addSwitchesTo(map);
834        
835        return map;
836      }
837    
838      /* (non-Javadoc)
839       * @see edu.nrao.sss.model.resource.AntennaElectronics#getTransferSwitches()
840       */
841      public SortedMap<String, SignalTransferSwitch> getTransferSwitches()
842      {
843        SortedMap<String, SignalTransferSwitch> map =
844          new TreeMap<String, SignalTransferSwitch>();
845     
846        map.put(switch5.getName(), switch5);
847        map.put(switch6.getName(), switch6);
848    
849        //TODO see if we have xfer switches in T30x that we want to expose
850        
851        return map;
852      }
853      
854      /* (non-Javadoc)
855       * @see edu.nrao.sss.model.resource.AntennaElectronics#getDigitizers()
856       */
857      public SortedMap<String, Digitizer> getDigitizers()
858      {
859        SortedMap<String, Digitizer> map = new TreeMap<String, Digitizer>();
860        
861        map.put(samplerA.getName(), samplerA);
862        map.put(samplerB.getName(), samplerB);
863        map.put(samplerC.getName(), samplerC);
864        map.put(samplerD.getName(), samplerD);
865        
866        return map;
867      }
868      
869      /* (non-Javadoc)
870       * @see AntennaElectronics#configureFrom(AntennaElectronicsConfiguration)
871       */
872      public boolean configureFrom(AntennaElectronicsConfiguration configuration)
873      {
874        boolean success = true;
875        
876        configurationProblems.clear();
877        
878        //TODO reject if config.telescope != EVLA
879    
880        configureFor(configuration.getBands());
881        
882        success &= configureLocOscsFrom(configuration.getLoTunings());
883        success &= configureDigitizersFrom(configuration.getBitsPerSample());
884        success &= configureXfersFrom(configuration.getTransferSwitchPositions());
885        success &= configureSwitchesFrom(configuration.getSwitchPositions());
886        
887        return success;
888      }
889      
890      /** Helps configureFrom(AntennaElectronicsConfiguration). */
891      private boolean configureLocOscsFrom(SortedMap<String, Frequency> loCfg)
892      {
893        boolean success = true;
894        
895        SortedMap<String, LocalOscillator> loMap = getLocalOscillators();
896    
897        for (String loName : loCfg.keySet())
898        {
899          LocalOscillator lo = loMap.get(loName);
900          if (lo != null)
901          {
902            lo.tuneTo(loCfg.get(loName));
903          }
904          else
905          {
906            success = false;
907            configurationProblems.add("EVLA electronics has no LO named '" +
908                                      loName + "'.");
909          }
910        }
911        
912        return success;
913      }
914      
915      /** Helps configureFrom(AntennaElectronicsConfiguration). */
916      private boolean configureDigitizersFrom(Map<String, Integer> digCfg)
917      {
918        boolean success = true;
919        
920        SortedMap<String, Digitizer> digMap = getDigitizers();
921    
922        for (String digName : digCfg.keySet())
923        {
924          Digitizer dig = digMap.get(digName);
925          if (dig != null)
926          {
927            dig.setBitsPerSample(digCfg.get(digName));
928          }
929          else
930          {
931            success = false;
932            configurationProblems.add("EVLA electronics has no digitizer named '" +
933                                      digName + "'.");
934          }
935        }
936    
937        return success;
938      }
939      
940      /** Helps configureFrom(AntennaElectronicsConfiguration). */
941      private boolean configureXfersFrom(SortedMap<String, String> xferCfg)
942      {
943        boolean success = true;
944        
945        SortedMap<String, SignalTransferSwitch> xferMap = getTransferSwitches();
946    
947        for (String xferName : xferCfg.keySet())
948        {
949          SignalTransferSwitch xs = xferMap.get(xferName);
950          if (xs != null)
951          {
952            String orientation = xferCfg.get(xferName);
953            
954            if ("clockwise".equalsIgnoreCase(orientation))
955            {
956              xs.setToClockwiseOutputs();
957            }
958            else if ("counterclockwise".equalsIgnoreCase(orientation))
959            {
960              xs.setToCounterclockwiseOutputs();
961            }
962            else
963            {
964              success = false;
965              configurationProblems.add(
966                "Cannot set switch '" + xferName + "' to orientation of '" +
967                orientation +
968                "'.  Valid values are 'clockwise' and 'counterclockwise'.");
969            }
970          }
971          else
972          {
973            success = false;
974            configurationProblems.add(
975              "EVLA electronics has no transfer switch named '" + xferName + "'.");
976          }
977        }
978    
979        return success;
980      }
981      
982      /** Helps configureFrom(AntennaElectronicsConfiguration). */
983      private boolean configureSwitchesFrom(SortedMap<String, Map<String, String>> switchCfg)
984      {
985        boolean success = true;
986        
987        SortedMap<String, SignalSwitch> switchMap = getSwitches();
988        
989        for (String switchName : switchCfg.keySet())
990        {
991          SignalSwitch ss = switchMap.get(switchName);
992          if (ss != null)
993          {
994            Map<String, String> poleCfg = switchCfg.get(switchName);
995            
996            if (poleCfg.containsKey("input"))
997              ss.selectInput(poleCfg.get("input"));
998            
999            if (poleCfg.containsKey("output"))
1000              ss.selectOutput(poleCfg.get("output"));
1001          }
1002          else
1003          {
1004            success = false;
1005            configurationProblems.add("EVLA electronics has no switch named '" +
1006                                      switchName + "'.");
1007          }
1008        }
1009        return success;
1010      }
1011    
1012      /* (non-Javadoc)
1013       * @see edu.nrao.sss.model.resource.AntennaElectronics#getConfiguration()
1014       */
1015      public AntennaElectronicsConfiguration getConfiguration()
1016      {
1017        AntennaElectronicsConfiguration config =
1018          new AntennaElectronicsConfiguration(TelescopeType.EVLA);
1019        
1020        //Receiver bands
1021        config.getBands().add(currentFrontEnd.getBand());
1022        
1023        //Local oscillators
1024        SortedMap<String, LocalOscillator> loSrcMap = getLocalOscillators();
1025        Map<String, Frequency> loDestMap = config.getLoTunings();
1026        for (LocalOscillator lo : loSrcMap.values())
1027          loDestMap.put(lo.getName(), lo.getCurrentTuning());
1028        
1029        //Bits per sample
1030        SortedMap<String, Digitizer> digSrcMap = getDigitizers();
1031        Map<String, Integer> digDestMap = config.getBitsPerSample();
1032        for (Digitizer d : digSrcMap.values())
1033          digDestMap.put(d.getName(), d.getBitsPerSample());
1034        
1035        //Transfer switches
1036        SortedMap<String, SignalTransferSwitch> xferSrcMap = getTransferSwitches();
1037        Map<String, String> xferDestMap = config.getTransferSwitchPositions();
1038        for (SignalTransferSwitch xs : xferSrcMap.values())
1039          xferDestMap.put(xs.getName(), xferSwitchSetting(xs.isSetToClockwise()));
1040        
1041        //Plain switches
1042        SortedMap<String, SignalSwitch>  switchSrcMap  = getSwitches();
1043        Map<String, Map<String, String>> switchDestMap = config.getSwitchPositions();
1044        for (SignalSwitch ss : switchSrcMap.values())
1045          switchDestMap.put(ss.getName(), switchSetting(ss));
1046        
1047        return config;
1048      }
1049      
1050      //Helps getConfiguration()
1051      private String xferSwitchSetting(boolean isClockwise)
1052      {
1053        return isClockwise ? "clockwise" : "counterclockwise";
1054      }
1055      
1056      //Helps getConfiguration()
1057      private Map<String, String> switchSetting(SignalSwitch s)
1058      {
1059        Map<String, String> map = new HashMap<String, String>();
1060        
1061        map.put("input",  s.getNameOfSelectedInput());
1062        map.put("output", s.getNameOfSelectedOutput());
1063        
1064        return map;
1065      }
1066      
1067      public void submitTuningPlan(Map<ReceiverBand, Map<String, Frequency>> plans)
1068      {
1069        tuningPlans.putAll(plans);
1070      }
1071      
1072      public void submitTuningPlan(ReceiverBand band, Map<String, Frequency> plan)
1073      {
1074        tuningPlans.put(band, plan);
1075      }
1076      
1077      private void tuneLocOscsFor(ReceiverBand band)
1078      {
1079        Map<String, Frequency> plan = tuningPlans.get(band);
1080        
1081        if (plan == null)
1082        {
1083          if (DEFAULT_TUNING_PLANS == null)
1084            DEFAULT_TUNING_PLANS = TelescopeType.EVLA.getTuningPlan();
1085          
1086          plan = DEFAULT_TUNING_PLANS.get(band);
1087        }
1088        
1089        if (plan != null)
1090        {
1091          SortedMap<String, LocalOscillator> locOscMap =
1092            getLocalOscillators();
1093          
1094          for (String loName : plan.keySet())
1095          {
1096            locOscMap.get(loName).tuneTo(plan.get(loName));
1097          }
1098        }
1099      }
1100      
1101      //============================================================================
1102      // QUERIES
1103      //============================================================================
1104      
1105      /* (non-Javadoc)
1106       * @see edu.nrao.sss.model.resource.AntennaElectronics#getActiveReceivers()
1107       */
1108      public SortedSet<ReceiverBand> getActiveReceivers()
1109      {
1110        //TODO update when we start handling multiple receivers
1111        SortedSet<ReceiverBand> active = new TreeSet<ReceiverBand>();
1112        active.add(currentFrontEnd.getBand());
1113        return active;
1114      }
1115      
1116      /* (non-Javadoc)
1117       * @see edu.nrao.sss.model.resource.AntennaElectronics#getActiveFrontEnds()
1118       */
1119      public SortedSet<AntennaFrontEnd> getActiveFrontEnds()
1120      {
1121        //TODO update when we start handling multiple receivers
1122        SortedSet<AntennaFrontEnd> active = new TreeSet<AntennaFrontEnd>();
1123        active.add(currentFrontEnd);
1124        return active;
1125      }
1126      
1127      /* (non-Javadoc)
1128       * @see edu.nrao.sss.model.resource.AntennaElectronics#getConfigurationProblems()
1129       */
1130      public List<String> getConfigurationProblems()
1131      {
1132        return new ArrayList<String>(configurationProblems);
1133      }
1134      
1135      //============================================================================
1136      // EVLA-ONLY METHODS - SETTING QUANTIZATION
1137      //============================================================================
1138    
1139      /**
1140       * Sets the quantization to {@code bits} for the given IF.
1141       * This is a convenience method that sets several switches into the proper
1142       * positions for the type of output requested for the given IF.
1143       * 
1144       * @param ifCode
1145       *   a single letter, case insensitive, code for one of the intermediate
1146       *   frequency singles of the EVLA antennas.  The following list illustrates
1147       *   what happens when a set of signals in 3-bit mode are changed to 8-bits:
1148       *   <ul>
1149       *     <li>'A' results in the replacement of A1 &amp; C1 with A0.</li>
1150       *     <li>'B' results in the replacement of B1 &amp; D1 with B0.</li>
1151       *     <li>'C' results in the replacement of A2, C1, &amp; C2 with C0.</li>
1152       *     <li>'D' results in the replacement of B2, D1, &amp; D2 with D0.</li>
1153       *   </ul>
1154       *   If this value is not in the range 'A'-'D' then this method will do
1155       *   nothing.
1156       * 
1157       * @param bits
1158       *   the only legal values are <tt>3</tt> and <tt>8</tt>, however this method
1159       *   will accept any integer.  If the value is less than 6, 3 will be used,
1160       *   otherwise 8 will be used.
1161       *
1162       * @since 2008-10-20
1163       * 
1164       * @see #setQuantization(int, int, int, int)
1165       */
1166      public void setQuantization(char ifCode, int bits)
1167      {
1168        D301 dts  = null;
1169        T304 t304 = null;
1170        
1171        switch (Character.toUpperCase(ifCode))
1172        {
1173          case 'A':  t304 = downConverterA;  dts = samplerA;  break;
1174          case 'B':  t304 = downConverterB;  dts = samplerB;  break;
1175          case 'C':  t304 = downConverterC;  dts = samplerC;  break;
1176          case 'D':  t304 = downConverterD;  dts = samplerD;  break;
1177            
1178          default:   return;
1179        }
1180        
1181        t304.configureForWidestOutput(bits < 6);
1182        dts.setBitsPerSample(bits);
1183      }
1184      
1185      /**
1186       * Sets the quantization levels of all IFs at once.
1187       * <p>
1188       * For all parameters
1189       * the only legal values are <tt>3</tt> and <tt>8</tt>, however this method
1190       * will accept any integer.  If the value is less than 6, 3 will be used,
1191       * otherwise 8 will be used.</p>
1192       * 
1193       * @param ifA the number of bits for the 'A' IF.
1194       * @param ifB the number of bits for the 'B' IF.
1195       * @param ifC the number of bits for the 'C' IF.
1196       * @param ifD the number of bits for the 'D' IF.
1197       *
1198       * @since 2008-10-23
1199       */
1200      public void setQuantization(int ifA, int ifB, int ifC, int ifD)
1201      {
1202        downConverterA.configureForWidestOutput(ifA < 6);
1203        downConverterB.configureForWidestOutput(ifB < 6);
1204        downConverterC.configureForWidestOutput(ifC < 6);
1205        downConverterD.configureForWidestOutput(ifD < 6);
1206        
1207        samplerA.setBitsPerSample(ifA);
1208        samplerB.setBitsPerSample(ifB);
1209        samplerC.setBitsPerSample(ifC);
1210        samplerD.setBitsPerSample(ifD);
1211      }
1212      
1213      //============================================================================
1214      // EVLA-ONLY METHODS - TUNING TO SKY FREQUENCIES
1215      //============================================================================
1216      // 2008-10-24 DMH:
1217      //
1218      //  Our first pass at automated tuning (as opposed to setting LO frequencies
1219      //  and switch positions manually) is to use a group of tuners that have
1220      //  been customized by receiver band and that have some "insider knowledge"
1221      //  about the structure of these electronics.  This was done because it was
1222      //  fast and easy to do.  However, it means that should these electronics
1223      //  be changed, one or more of the tuners may also need to be changed.  That
1224      //  is not good design.  Our desire is to craft a second generation tuner
1225      //  that is generalized to where it can inspect the targeted electronics
1226      //  and deduce which LOs and switches to manipulate and how to set them.
1227      
1228      private final Map<ReceiverBand, EvlaTuner> TUNERS =
1229        new HashMap<ReceiverBand, EvlaTuner>();
1230      
1231      private void initTuners()
1232      {
1233        EvlaTuner lsc = new EvlaTunerLSC(this);
1234        EvlaTuner ux  = new EvlaTunerUX(this);
1235        
1236        TUNERS.put(ReceiverBand.EVLA_4,  null);
1237        TUNERS.put(ReceiverBand.EVLA_P,  null);
1238        TUNERS.put(ReceiverBand.EVLA_L,  lsc);
1239        TUNERS.put(ReceiverBand.EVLA_S,  lsc);
1240        TUNERS.put(ReceiverBand.EVLA_C,  lsc);
1241        TUNERS.put(ReceiverBand.EVLA_X,  new EvlaTunerDirect(this));
1242        TUNERS.put(ReceiverBand.EVLA_Ku, new EvlaTunerKu(this));
1243        TUNERS.put(ReceiverBand.EVLA_K,  ux);
1244        TUNERS.put(ReceiverBand.EVLA_Ka, ux);
1245        TUNERS.put(ReceiverBand.EVLA_Q,  ux);
1246      }
1247    
1248      /**
1249       * Attempts to configure the EVLA antenna electronics to receive the given
1250       * central sky frequencies.
1251       * 
1252       * @param centerFreq3Bit1 the central sky frequency of a 2.048GHz, 3-bit, signal.
1253       * @param centerFreq3bit2 the central sky frequency of a 2.048GHz, 3-bit, signal.
1254       * @param centerFreq3Bit3 the central sky frequency of a 2.048GHz, 3-bit, signal.
1255       * @param centerFreq3bit4 the central sky frequency of a 2.048GHz, 3-bit, signal.
1256       *
1257       * @since 2008-10-24
1258       */
1259      public void tuneTo(Frequency centerFreq3Bit1, Frequency centerFreq3bit2,
1260                         Frequency centerFreq3Bit3, Frequency centerFreq3bit4)
1261      {
1262        EvlaTuner tuner = TUNERS.get(currentFrontEnd.getBand());
1263        
1264        if (tuner != null)
1265          tuner.tuneTo(centerFreq3Bit1, centerFreq3bit2, centerFreq3Bit3, centerFreq3bit4);
1266      }
1267      
1268      /**
1269       * Attempts to configure the EVLA antenna electronics to receive the given
1270       * central sky frequencies.
1271       * 
1272       * @param centerFreq8Bit1 the central sky frequency of a 1.024GHz, 8-bit, signal.
1273       * @param centerFreq8bit2 the central sky frequency of a 1.024GHz, 8-bit, signal.
1274       *
1275       * @since 2008-10-24
1276       */
1277      public void tuneTo(Frequency centerFreq8Bit1, Frequency centerFreq8bit2)
1278      {
1279        EvlaTuner tuner = TUNERS.get(currentFrontEnd.getBand());
1280        
1281        if (tuner != null)
1282          tuner.tuneTo(centerFreq8Bit1, centerFreq8bit2);
1283      }
1284      
1285      /**
1286       * Attempts to configure the EVLA antenna electronics to receive the given
1287       * central sky frequencies.
1288       * 
1289       * @param centerFreq3Bit1 the central sky frequency of a 2.048GHz, 3-bit, signal.
1290       * @param centerFreq3bit2 the central sky frequency of a 2.048GHz, 3-bit, signal.
1291       * @param centerFreq8Bit the central sky frequency of a 1.024GHz, 8-bit, signal.
1292       *
1293       * @since 2008-10-24
1294       */
1295      public void tuneTo(Frequency centerFreq3Bit1, Frequency centerFreq3bit2,
1296                         Frequency centerFreq8Bit)
1297      {
1298        EvlaTuner tuner = TUNERS.get(currentFrontEnd.getBand());
1299        
1300        if (tuner != null)
1301          tuner.tuneTo(centerFreq3Bit1, centerFreq3bit2, centerFreq8Bit);
1302      }
1303      
1304      //============================================================================
1305      // EVLA-ONLY METHODS - MISC
1306      //============================================================================
1307      
1308      /**
1309       * Returns the T303 UX converter held by these electronics.  Clients should
1310       * only query, not set, properties of the returned object.
1311       */
1312      T303 getUxConverter()  { return uxConverter; }
1313    
1314      /**
1315       * Returns the T304 down converter held by these electronics.  Clients should
1316       * only query, not set, properties of the returned object.
1317       */
1318      T304 getDownConverter(char ifCode)
1319      {
1320        switch (Character.toUpperCase(ifCode))
1321        {
1322          case 'A': return downConverterA;
1323          case 'B': return downConverterB;
1324          case 'C': return downConverterC;
1325          case 'D': return downConverterD;
1326          
1327          default: return null;
1328        }
1329      }
1330      
1331      //============================================================================
1332      // 
1333      //============================================================================
1334    
1335      /**
1336       *  Returns a copy of this electronics.
1337       *  <p>
1338       *  If anything goes wrong during the cloning procedure,
1339       *  a {@code RuntimeException} will be thrown.</p>
1340       */
1341      @Override
1342      public EvlaAntennaElectronics clone()
1343      {
1344        EvlaAntennaElectronics clone = null;
1345        
1346        try
1347        {
1348          //This line takes care of the primitive & immutable fields properly
1349          clone = (EvlaAntennaElectronics)super.clone();
1350          
1351          //This code is a little different than our usual cloning code.
1352          //We rely on the construct method to create all new components and
1353          //stitch them together.  We then configure the clone with a configuration
1354          //from this instance.
1355          clone.construct();
1356          clone.configureFrom(this.getConfiguration());
1357        }
1358        catch (Exception ex)
1359        {
1360          throw new RuntimeException(ex);
1361        }
1362    
1363        return clone;
1364      }
1365      
1366      /** Returns <i>true</i> if {@code o} is equal to this electronics. */
1367      @Override
1368      public boolean equals(Object o)
1369      {
1370        //Quick exit if o is this
1371        if (o == this)
1372          return true;
1373        
1374        //Quick exit if o is null
1375        if (o == null)
1376          return false;
1377       
1378        //Quick exit if classes are different
1379        if (!o.getClass().equals(this.getClass()))
1380          return false;
1381       
1382        //A safe cast if we got this far
1383        EvlaAntennaElectronics other = (EvlaAntennaElectronics)o;
1384        
1385        return other.getConfiguration().equals(this.getConfiguration());
1386      }
1387    
1388      /** Returns a hash code value for this electronics. */
1389      @Override
1390      public int hashCode()
1391      {
1392        //Taken from the Effective Java book by Joshua Bloch.
1393        //The constants 17 & 37 are arbitrary & carry no meaning.
1394        int result = 17;
1395        
1396        //You MUST keep this method in sync w/ the equals method
1397        
1398        result = 37 * result + getConfiguration().hashCode();
1399    
1400        return result;
1401      }
1402    
1403      //============================================================================
1404      // 
1405      //============================================================================
1406      /*
1407      public static void main(String[] args) throws Exception
1408      {
1409        EvlaAntennaElectronics evla = new EvlaAntennaElectronics();
1410    
1411        evla.configureFor(ReceiverBand.EVLA_K);
1412        
1413        System.out.println("INPUT: " + evla.currentFrontEnd.getBand().getDisplayName() +
1414                           ", " + evla.currentFrontEnd.getBand().getFrequencyRange() + "\n");
1415        
1416        System.out.println("LO TUNINGS");
1417        SortedMap<String, LocalOscillator> lo = evla.getLocalOscillators();
1418        for (String loName : lo.keySet())
1419        {
1420          System.out.println(loName + ": " + lo.get(loName).getCurrentTuning());
1421        }
1422        
1423        System.out.println("\nOUTPUT SIGNALS");
1424        
1425        for (DigitalSignal signal : evla.execute())
1426        {
1427          System.out.println(signal);
1428        }
1429        
1430        System.out.println("OUTPUT SIGNAL PAIRS");
1431        
1432        for (List<DigitalSignal> pair : evla.getSignalPairs())
1433        {
1434          System.out.print("  PAIR: ");
1435          for (DigitalSignal member : pair)
1436            if (member == null)
1437              System.out.print("NULL ");
1438            else
1439              System.out.print(member.getName() + ' ');
1440          System.out.println();
1441        }
1442        
1443        System.out.println("\nANTENNA ELECTRONICS CONFIGURATION");
1444        
1445        AntennaElectronicsConfiguration config = evla.getConfiguration();
1446        System.out.println(config.getBands());
1447        System.out.println(config.getLoTunings());
1448        System.out.println(config.getBitsPerSample());
1449        System.out.println(config.getTransferSwitchPositions());
1450        System.out.println(config.getSwitchPositions());
1451        
1452        System.out.println();
1453        System.out.println("ANTENNA ELECTRONICS CONFIGURATION - XML");
1454        System.out.println(config.toXml());
1455      }
1456      */
1457      /*
1458      public static void main(String[] args) throws Exception
1459      {
1460        EvlaAntennaElectronics evla = new EvlaAntennaElectronics();
1461    
1462        evla.configureFor(ReceiverBand.EVLA_Ka);
1463        
1464        System.out.println("INPUT: " + evla.currentFrontEnd.getBand().getDisplayName() +
1465                           ", " + evla.currentFrontEnd.getBand().getFrequencyRange() + "\n");
1466        
1467        System.out.println("LO TUNINGS");
1468        SortedMap<String, LocalOscillator> lo = evla.getLocalOscillators();
1469        for (String loName : lo.keySet())
1470        {
1471          System.out.println(loName + ": " + lo.get(loName).getCurrentTuning());
1472        }
1473        
1474        System.out.println("\nOUTPUT SIGNALS");
1475        
1476        for (DigitalSignal signal : evla.execute())
1477        {
1478          System.out.println(signal.getName() + ": " + signal.getProxiedRange());
1479        }
1480        
1481        lo.get("L301-1").tuneTo(new Frequency("12.4"));
1482        
1483        System.out.println("\nLO TUNINGS");
1484        for (String loName : lo.keySet())
1485        {
1486          System.out.println(loName + ": " + lo.get(loName).getCurrentTuning());
1487        }
1488        
1489        System.out.println("\nOUTPUT SIGNALS");
1490        
1491        for (DigitalSignal signal : evla.execute())
1492        {
1493          System.out.println(signal.getName() + ": " + signal.getProxiedRange());
1494        }
1495      }
1496      */
1497      /*
1498      public static void main(String[] args) throws Exception
1499      {
1500        //Debugging 2008-04-07 to see why a null LO signal does not act
1501        //like an LO signal of zero hertz.  Should lead to RF=receiver range
1502        //and IF=RF.  Instead, am seeing RF=0.0-0.0, IF=2.048GHz-2.048GHz.
1503        EvlaAntennaElectronics evla = new EvlaAntennaElectronics();
1504    
1505        evla.configureFor(ReceiverBand.EVLA_L);
1506        evla.execute();
1507        
1508        evla.getSwitches().get("S8").selectOutput("off");
1509        evla.execute();
1510      }
1511      */
1512    }