001    package edu.nrao.sss.model.resource.evla;
002    
003    import java.util.ArrayList;
004    import java.util.Collection;
005    import java.util.List;
006    import java.util.SortedMap;
007    
008    import edu.nrao.sss.electronics.Signal;
009    import edu.nrao.sss.electronics.SignalManifold;
010    import edu.nrao.sss.electronics.SignalMixer;
011    import edu.nrao.sss.electronics.SignalPipe;
012    import edu.nrao.sss.electronics.SignalProcessor;
013    import edu.nrao.sss.electronics.SignalSource;
014    import edu.nrao.sss.electronics.SignalSwitch;
015    import edu.nrao.sss.measure.Frequency;
016    
017    /**
018     * The EVLA T302 LSC Upconverter.
019     * <p>
020     * This device has two halves that are identical.  Each half has two inputs,
021     * only one of which may be selected (by an internal switch) at a time.
022     * The input is split onto two paths, each of which is mixed with a different
023     * local oscillator signal and output.  The local oscillator deemed
024     * "LO1" influences equally one input in the top half and one in the bottom.
025     * The same is true for LO2.</p>
026     * <p>
027     * As used by the {@link EvlaAntennaElectronics EVLA electronics}, the
028     * top inputs carry left circular polarized signals that have come from
029     * either the 4/P converter, or from a switch that outputs either the 
030     * L, S, or C band LCP signal.  The bottom inputs are similar, except that
031     * they carry RCP signals.  Note: the text above is accurate only if the
032     * 4/P converter output switches are in there standard positions.
033     * It is possible to feed the 4/P LCP or RCP to both the top and bottom
034     * inputs of this T302.</p>
035     * <img src="doc-files/T302.png" alt="T302 Dgm"/>
036     * <p>
037     * <b>Version Info:</b>
038     * <table style="margin-left:2em">
039     *   <tr><td>$Revision: 1241 $</td></tr>
040     *   <tr><td>$Date: 2008-04-25 14:37:37 -0600 (Fri, 25 Apr 2008) $</td></tr>
041     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
042     * </table></p>
043     * 
044     * @author David M. Harland
045     * @since 2007-10-30
046     */
047    public class T302
048      implements SignalProcessor
049    {
050      //The target output range of this device is 8-12GHz, making tgt ctr = 10GHz.
051      //This value is used for helping the suggestLOFrequencies() method.
052      //TODO this information should be set by the outside world, not treated
053      //     as internal knowledge.
054      private static final Frequency CENTER_OF_TARGET_OUTPUT = new Frequency("10.0");
055      
056      //Internal components
057      private OneHalfT302    topHalf;
058      private OneHalfT302    bottomHalf;
059      private SignalManifold lo1Fork;
060      private SignalManifold lo2Fork;
061    
062      //============================================================================
063      // OBJECT CONSTRUCTION
064      //============================================================================
065    
066      /**
067       * Creates a new T302 LSC Upconverter that uses the given sources as its
068       * local oscillators.
069       *  
070       * @param LO1
071       *   provider of the LO-1 local oscillator signal.
072       *   
073       * @param LO2
074       *   provider of the LO-2 local oscillator signal.
075       */
076      public T302(SignalSource LO1, SignalSource LO2)
077      {
078        constructInternalComponents();
079        connectInternalComponents();
080        
081        //Set frequency sources for the mixers
082        lo1Fork.getInputPipe().connectInputTo(LO1);
083        lo2Fork.getInputPipe().connectInputTo(LO2);
084      }
085      
086      /**
087       * Builds, but does not connect, the internal components of this device.
088       */
089      private void constructInternalComponents()
090      {
091        topHalf    = new OneHalfT302("LSC.TOP");
092        bottomHalf = new OneHalfT302("LSC.BOTTOM");
093        
094        lo1Fork = new SignalManifold(2);
095        lo2Fork = new SignalManifold(2);
096      }
097      
098      /**
099       * Connects the prebuilt internal components of this device to one another.
100       */
101      private void connectInternalComponents()
102      {
103        lo1Fork.getOutputPipe(0).connectOutputTo(   topHalf.lo1Mixer.getLocalOscillatorInputPipe());
104        lo1Fork.getOutputPipe(1).connectOutputTo(bottomHalf.lo1Mixer.getLocalOscillatorInputPipe());
105    
106        lo2Fork.getOutputPipe(0).connectOutputTo(   topHalf.lo2Mixer.getLocalOscillatorInputPipe());
107        lo2Fork.getOutputPipe(1).connectOutputTo(bottomHalf.lo2Mixer.getLocalOscillatorInputPipe());
108      }
109    
110      //============================================================================
111      // INPUTS AND OUTPUTS
112      //============================================================================
113      
114      /**
115       * Sets the input top and bottom input switches to select one
116       * of their inputs.  The names of the poles of these switches
117       * are set by a call to
118       * {@link #nameInputPolesOfInputSwitches(String, String, String, String)}.
119       * 
120       * @param topSwInputPoleName
121       *   the name of an input pole of the top input switch of this converter.
122       * 
123       * @param bottomSwInputPoleName
124       *   the name of an input pole of the bottom input switch of this converter.
125       */
126      public void setInputSwitches(String topSwInputPoleName,
127                                   String bottomSwInputPoleName)
128      {
129        topHalf.inputSwitch.selectInput(topSwInputPoleName);
130        bottomHalf.inputSwitch.selectInput(bottomSwInputPoleName);
131      }
132      
133      /**
134       * Returns the two top inputs for this converter.
135       * An internal switch will select only one of these two inputs.
136       * The selected input will be sent down two pathways.
137       * On one pathway it is mixed with the LO-1 signal, on the other with
138       * the LO-2 signal.  These are then sent out through the top two outputs.
139       * <p>
140       * In the EVLA antenna electronics, the top inputs will be used
141       * for left circular polarized signals coming from switch S2-2 (input
142       * pole 0) and the T301 top output (input pole 1).</p>
143       * 
144       * @return the top two inputs for this converter.
145       */
146      public List<SignalPipe> getTopInputPipes()
147      {
148        List<SignalPipe> inputs = new ArrayList<SignalPipe>();
149        
150        inputs.add(topHalf.inputSwitch.getInputPipe(0));
151        inputs.add(topHalf.inputSwitch.getInputPipe(1));
152        
153        return inputs;
154      }
155    
156      /**
157       * Returns the two bottom inputs for this converter.
158       * An internal switch will select only one of these two inputs.
159       * The selected input will be sent down two pathways.
160       * On one pathway it is mixed with the LO-1 signal, on the other with
161       * the LO-2 signal.  These are then sent out through the bottom two outputs.
162       * <p>
163       * In the EVLA antenna electronics, the bottom inputs will be used
164       * for rigth circular polarized signals coming from switch S2-1 (input
165       * pole 0) and the T301 bottom output (input pole 1).</p>
166       * 
167       * @return the bottom two inputs for this converter.
168       */
169      public List<SignalPipe> getBottomInputPipes()
170      {
171        List<SignalPipe> inputs = new ArrayList<SignalPipe>();
172        
173        inputs.add(bottomHalf.inputSwitch.getInputPipe(0));
174        inputs.add(bottomHalf.inputSwitch.getInputPipe(1));
175        
176        return inputs;
177      }
178      
179      /**
180       * Returns the output that came from one of the top inputs and
181       * was mixed with the LO-1 signal.
182       * 
183       * @return the output that came from one of the top inputs and
184       *         was mixed with the LO-1 signal.
185       */
186      public SignalPipe getTopLO1Output()
187      {
188        return topHalf.lo1Mixer.getOutputPipe();
189      }
190      
191      /**
192       * Returns the output that came from one of the top inputs and
193       * was mixed with the LO-2 signal.
194       * 
195       * @return the output that came from one of the top inputs and
196       *         was mixed with the LO-2 signal.
197       */
198      public SignalPipe getTopLO2Output()
199      {
200        return topHalf.lo2Mixer.getOutputPipe();
201      }
202      
203      /**
204       * Returns the output that came from one of the bottom inputs and
205       * was mixed with the LO-1 signal.
206       * 
207       * @return the output that came from one of the bottom inputs and
208       *         was mixed with the LO-1 signal.
209       */
210      public SignalPipe getBottomLO1Output()
211      {
212        return bottomHalf.lo1Mixer.getOutputPipe();
213      }
214      
215      /**
216       * Returns the output that came from one of the bottom inputs and
217       * was mixed with the LO-2 signal.
218       * 
219       * @return the output that came from one of the bottom inputs and
220       *         was mixed with the LO-2 signal.
221       */
222      public SignalPipe getBottomLO2Output()
223      {
224        return bottomHalf.lo2Mixer.getOutputPipe();
225      }
226    
227      void nameInputPolesOfInputSwitches(String topSwPole0,    String topSwPole1,
228                                         String bottomSwPole0, String bottomSwPole1)
229      {
230        topHalf.inputSwitch.nameInputs(topSwPole0, topSwPole1);
231        bottomHalf.inputSwitch.nameInputs(bottomSwPole0, bottomSwPole1);
232      }
233    
234      void addSwitchesTo(SortedMap<String, SignalSwitch> map)
235      {
236        map.put(   topHalf.inputSwitch.getName(),    topHalf.inputSwitch);
237        map.put(bottomHalf.inputSwitch.getName(), bottomHalf.inputSwitch);
238      }
239    
240      /**
241       * Returns the signals produced by this converter.
242       * The returned collection will hold four signals.
243       * 
244       * @return the signals produced by this converter.
245       */
246      public Collection<Signal> getSignals()
247      {
248        Collection<Signal> signals = new ArrayList<Signal>();
249        
250        signals.add(topHalf.lo1Mixer.getMostRecentOutput());
251        signals.add(topHalf.lo2Mixer.getMostRecentOutput());
252        signals.add(bottomHalf.lo1Mixer.getMostRecentOutput());
253        signals.add(bottomHalf.lo2Mixer.getMostRecentOutput());
254        
255        return signals;
256      }
257    
258      /**
259       * Suggests frequencies for tuning the local oscillators that were used in the
260       * {@link #T302(SignalSource, SignalSource) construction} of this device.
261       * The suggested tunings take into account the current input signals being
262       * fed to this converter, as well as a target center for the output signal
263       * of 10.0 GHz.
264       * <p>
265       * <b>Note:</b> this method makes some assumptions in its determinations.
266       * These assumptions, which may not suit all clients, are as follows:
267       * <ol>
268       *   <li>The bottom half is receiving signals that have the same frequencies
269       *       as their counterparts in the top half.</li>
270       *   <li>The LO1 and LO2 oscillators will be tuned to the same
271       *       frequencies.</li>
272       * </ol>
273       * 
274       * @return a list containing the suggested frequency for LO1 at index 0 and
275       *         the suggested frequency for LO2 at index 1.
276       */
277      public List<Frequency> suggestLOFrequencies()
278      {
279        return topHalf.suggestLOFrequencies();
280      }
281      
282      //============================================================================
283      // INTERFACE SignalProcessor
284      //============================================================================
285      
286      /**
287       * See {@link SignalProcessor#execute()}.
288       */
289      public void execute()
290      {
291        topHalf.execute();
292        bottomHalf.execute();
293      }
294      
295      /**
296       * See {@link SignalProcessor#executeUpTo(SignalProcessor)}.
297       */
298      public void executeUpTo(SignalProcessor firstUnexecutedDevice)
299      {
300        if (firstUnexecutedDevice != this)
301        {
302          topHalf.executeUpTo(firstUnexecutedDevice);
303          bottomHalf.executeUpTo(firstUnexecutedDevice);
304        }
305      }
306    
307      /**
308       * See {@link SignalProcessor#executeFromStartOfChainUpTo(SignalProcessor)}.
309       */
310      public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice)
311      {
312        topHalf.executeFromStartOfChainUpTo(this);
313        bottomHalf.executeFromStartOfChainUpTo(this);
314        
315        executeUpTo(firstUnexecutedDevice);
316      }
317    
318      //============================================================================
319      // HELPER CLASSES
320      //============================================================================
321    
322      /**
323       * Represents roughly one half of the T302 LSC Upconverter.
324       * Each half creates a pair of output signals from an input signal selected
325       * by a switch that has two input signals.
326       * Note that the forks that feed the LO signals
327       * to the mixers are not considered to be
328       * part of this half, but are instead in addition to the
329       * two halves and are handled by class T302 directly.
330       */
331      private class OneHalfT302 implements SignalProcessor
332      {
333        private String         name;
334                SignalSwitch   inputSwitch;
335        private SignalManifold inputFork;
336                SignalMixer    lo1Mixer;
337                SignalMixer    lo2Mixer;
338        
339        /** Creates half of a T302. */
340        OneHalfT302(String nameOfThisHalf)
341        {
342          name = nameOfThisHalf;
343          constructInternalComponents();
344          connectInternalComponents();
345        }
346        
347        /**
348         * Builds, but does not connect, the internal components of this device.
349         */
350        private void constructInternalComponents()
351        {
352          String switchName = name + ".input-switch";
353          
354          inputSwitch = SignalSwitch.makeMultiInputSwitch(switchName, 2);
355          lo1Mixer    = SignalMixer.makeSubtractiveMixer("LO1-mixer");
356          lo2Mixer    = SignalMixer.makeSubtractiveMixer("LO2-mixer");
357          inputFork   = new SignalManifold(2);
358        }
359        
360        /**
361         * Connects the prebuilt internal components of this device to one another.
362         */
363        private void connectInternalComponents()
364        {
365          inputSwitch.getOutputPipe(0).connectOutputTo(inputFork.getInputPipe());
366          inputFork.getOutputPipe(0).connectOutputTo(lo1Mixer.getInputPipe());
367          inputFork.getOutputPipe(1).connectOutputTo(lo2Mixer.getInputPipe());
368        }
369    
370        /**
371         * This method currently assumes that both LOs should be set
372         * to the same frequency.
373         */
374        List<Frequency> suggestLOFrequencies()
375        {
376          List<Frequency> loSettings = new ArrayList<Frequency>();
377          
378          inputSwitch.executeFromStartOfChainUpTo(this);
379          
380          Signal inputSignal = inputSwitch.getSignal();
381          
382          if (inputSignal != null)
383          {
384            Frequency inputCenter =
385              inputSignal.getCurrentRange().getCenterFrequency();
386          
387            Frequency setting = CENTER_OF_TARGET_OUTPUT.clone().add(inputCenter);
388          
389            loSettings.add(setting);
390            loSettings.add(setting.clone());
391          }
392          else
393          {
394            loSettings.add(new Frequency());
395            loSettings.add(new Frequency());
396          }
397          
398          return loSettings;
399        }
400    
401        //============================================================================
402        // INTERFACE SignalProcessor
403        //============================================================================
404        
405        /* (non-Javadoc)
406         * @see edu.nrao.sss.electronics.SignalProcessor#execute()
407         */
408        public void execute()
409        {
410          inputSwitch.execute();
411        }
412        
413        public void executeUpTo(SignalProcessor firstUnexecutedDevice)
414        {
415          if (firstUnexecutedDevice != this)
416            inputSwitch.executeUpTo(firstUnexecutedDevice);
417        }
418    
419        public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice)
420        {
421          inputSwitch.executeFromStartOfChainUpTo(firstUnexecutedDevice);
422        }
423      }
424    
425      //============================================================================
426      // 
427      //============================================================================
428      /*
429      public static void main(String[] args) throws Exception
430      {
431        //LOs
432        Frequency lowFreq  = new Frequency(11904.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ);
433        Frequency highFreq = new Frequency(20352.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ);
434        Frequency stepSize = new Frequency(  256.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ);
435        
436        edu.nrao.sss.electronics.LocalOscillator L301_1 =
437          new edu.nrao.sss.electronics.LocalOscillator(new edu.nrao.sss.measure.FrequencyRange(lowFreq,highFreq),stepSize);
438        edu.nrao.sss.electronics.LocalOscillator L301_2 = L301_1.clone();
439    
440        //LSC INPUTS
441        SignalSwitch lcpSwitch = SignalSwitch.makeMultiInputSwitch("LCP", 3);
442        SignalSwitch rcpSwitch = SignalSwitch.makeMultiInputSwitch("RCP", 3);
443        
444        java.util.Map<edu.nrao.sss.model.resource.ReceiverBand, edu.nrao.sss.model.resource.AntennaFrontEnd> frontEnds =
445          edu.nrao.sss.model.resource.AntennaFrontEnd.makeEvlaFrontEnds(new java.util.HashMap<edu.nrao.sss.model.resource.ReceiverBand, SignalSource>());
446    
447        edu.nrao.sss.model.resource.AntennaFrontEnd fe = frontEnds.get(edu.nrao.sss.model.resource.ReceiverBand.EVLA_L);
448        lcpSwitch.getInputPipe(0).connectInputTo(fe.getOuputPipe(edu.nrao.sss.astronomy.PolarizationType.L));
449        rcpSwitch.getInputPipe(0).connectInputTo(fe.getOuputPipe(edu.nrao.sss.astronomy.PolarizationType.R));
450    
451        fe = frontEnds.get(edu.nrao.sss.model.resource.ReceiverBand.EVLA_S);
452        lcpSwitch.getInputPipe(1).connectInputTo(fe.getOuputPipe(edu.nrao.sss.astronomy.PolarizationType.L));
453        rcpSwitch.getInputPipe(1).connectInputTo(fe.getOuputPipe(edu.nrao.sss.astronomy.PolarizationType.R));
454    
455        fe = frontEnds.get(edu.nrao.sss.model.resource.ReceiverBand.EVLA_C);
456        lcpSwitch.getInputPipe(2).connectInputTo(fe.getOuputPipe(edu.nrao.sss.astronomy.PolarizationType.L));
457        rcpSwitch.getInputPipe(2).connectInputTo(fe.getOuputPipe(edu.nrao.sss.astronomy.PolarizationType.R));
458    
459        //4P INPUTS
460        //ignoring these
461        
462        //CONNECT INPUTS TO T302
463        T302 t302 = new T302(L301_1, L301_2);
464        
465        List<SignalPipe> topInputs    = t302.getTopInputPipes();
466        List<SignalPipe> bottomInputs = t302.getBottomInputPipes();
467        
468        lcpSwitch.getOutputPipe(0).connectOutputTo(topInputs.get(0));
469        rcpSwitch.getOutputPipe(0).connectOutputTo(bottomInputs.get(0));
470        
471        t302.namePolesOfInputSwitches("LSC", "null");
472        t302.setInputSwitches("LSC");
473        //t302.setInputSwitches("null");
474        
475        //RUN T302 WITH DIFFERENT INPUTS
476        for (int pole=0; pole < 3; pole++)
477        {
478          lcpSwitch.selectInput(pole);
479          rcpSwitch.selectInput(pole);
480          
481          List<Frequency> loSettings = t302.suggestLOFrequencies();
482          L301_1.tuneTo(loSettings.get(0));
483          L301_2.tuneTo(loSettings.get(1));
484          
485          t302.executeFromStartOfChainUpTo(null);
486          
487          System.out.println("---------- LOs tuned to " + L301_1.getCurrentTuning() + " ----------\n");
488          
489          for (Signal s : t302.getSignals())
490            System.out.println("T302 Output Signal:\n" + s);
491        }
492      }
493      */
494    }