001    package edu.nrao.sss.model.resource.evla;
002    
003    import java.math.BigDecimal;
004    import java.util.ArrayList;
005    import java.util.Arrays;
006    import java.util.Collection;
007    import java.util.HashMap;
008    import java.util.List;
009    import java.util.SortedMap;
010    
011    import edu.nrao.sss.electronics.Signal;
012    import edu.nrao.sss.electronics.SignalFilter;
013    import edu.nrao.sss.electronics.SignalManifold;
014    import edu.nrao.sss.electronics.SignalMixer;
015    import edu.nrao.sss.electronics.SignalMultiplier;
016    import edu.nrao.sss.electronics.SignalPipe;
017    import edu.nrao.sss.electronics.SignalProcessor;
018    import edu.nrao.sss.electronics.SignalSource;
019    import edu.nrao.sss.electronics.SignalSwitch;
020    import edu.nrao.sss.measure.FrequencyRange;
021    
022    /**
023     * The EVLA T303 UX Converter.
024     * <p>
025     * This device has two halves that are identical.  Each half has a single
026     * input<sup>1</sup>,
027     * three internal pathways, and two outputs.  There are two switches near the
028     * outputs that determine which pathways are output.  One of these pathways
029     * filters the signal and uses no mixer.  Each of the other two pathways uses
030     * a distinct local oscillator for mixing the input.  Both of these oscillators
031     * are used for a single path in each of the two halves.  That is, LO1 is used
032     * in one path in the top half, and in the parallel path in the bottom half,
033     * and likewise for L02.</p>
034     * <p>
035     * As used by the {@link EvlaAntennaElectronics EVLA electronics}, the
036     * top input carries left circular polarized signals that have come from
037     * either the Q, Ka, K, or Ku band front ends.  The bottom inputs are similar,
038     * except that they carry RCP signals.  Each of the four outputs is sent to
039     * a different switch, the outputs of which are connected to different
040     * {@link T304} converters.</p>
041     * <img src="doc-files/T303.png" alt="T303 Dgm"
042     *      style="height:auto; width:100%; max-width:1092px"/>
043     * <p>
044     * <sup>1</sup><i>In the diagram, switches S1-1 and S1-2 are shown as residing
045     * within the T303 UX converter.  However, in this software model, those
046     * switches are considered to lie </i>outside<i> the T303.</i></p>
047     * <p>
048     * <b>Version Info:</b>
049     * <table style="margin-left:2em">
050     *   <tr><td>$Revision: 1706 $</td></tr>
051     *   <tr><td>$Date: 2008-11-13 16:09:36 -0700 (Thu, 13 Nov 2008) $</td></tr>
052     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
053     * </table></p>
054     * 
055     * @author David M. Harland
056     * @since 2007-10-29
057     */
058    public class T303
059      implements SignalProcessor
060    {
061      //Internal components
062      private OneHalfT303      topHalf;
063      private OneHalfT303      bottomHalf;
064      private SignalMultiplier lo1Multiplier;
065      private SignalFilter     lo1Filter;
066      private SignalManifold   lo1Fork;
067      private SignalMultiplier lo2Multiplier;
068      private SignalFilter     lo2Filter;
069      private SignalManifold   lo2Fork;
070    
071      //============================================================================
072      // OBJECT CONSTRUCTION
073      //============================================================================
074    
075      /**
076       * Creates a new T303 UX Converter that uses the given sources as its
077       * local oscillators.
078       *  
079       * @param LO1
080       *   provider of the LO-1 local oscillator signal.
081       *   
082       * @param LO2
083       *   provider of the LO-2 local oscillator signal.
084       */
085      public T303(SignalSource LO1, SignalSource LO2)
086      {
087        constructInternalComponents();
088        connectInternalComponents();
089        
090        //Set frequency sources for the mixers
091        lo1Multiplier.getInputPipe().connectInputTo(LO1);
092        lo2Multiplier.getInputPipe().connectInputTo(LO2);
093        
094        setOutputSwitches("DIRECT", "LO2");
095      }
096      
097      /**
098       * Builds, but does not connect, the internal components of this device.
099       */
100      private void constructInternalComponents()
101      {
102        topHalf    = new OneHalfT303("UX.TOP");
103        bottomHalf = new OneHalfT303("UX.BOTTOM");
104        
105        //FILTERS
106        lo1Filter = SignalFilter.make("LO1-filter", "23.0", "29.0");
107        lo2Filter = SignalFilter.make("LO2-filter", "23.0", "29.0");
108        
109        //MULTIPLIERS
110        lo1Multiplier = new SignalMultiplier("LO1-x2", "2.0");
111        lo2Multiplier = new SignalMultiplier("LO2-x2", "2.0");
112        
113        //MANIFOLDS
114        lo1Fork = new SignalManifold(2);
115        lo2Fork = new SignalManifold(2);
116      }
117      
118      /**
119       * Connects the prebuilt internal components of this device to one another.
120       */
121      private void connectInternalComponents()
122      {
123        //LO-1 FILTER PATH
124        lo1Multiplier.getOutputPipe().connectOutputTo(lo1Filter.getInputPipe());
125        lo1Filter.getOutputPipe().connectOutputTo(lo1Fork.getInputPipe());
126        lo1Fork.getOutputPipe(0).connectOutputTo(   topHalf.lo1Mixer.getLocalOscillatorInputPipe());
127        lo1Fork.getOutputPipe(1).connectOutputTo(bottomHalf.lo1Mixer.getLocalOscillatorInputPipe());
128        
129        //LO-2 FILTER PATH
130        lo2Multiplier.getOutputPipe().connectOutputTo(lo2Filter.getInputPipe());
131        lo2Filter.getOutputPipe().connectOutputTo(lo2Fork.getInputPipe());
132        lo2Fork.getOutputPipe(0).connectOutputTo(   topHalf.lo2Mixer.getLocalOscillatorInputPipe());
133        lo2Fork.getOutputPipe(1).connectOutputTo(bottomHalf.lo2Mixer.getLocalOscillatorInputPipe());
134      }
135    
136      //============================================================================
137      // 
138      //============================================================================
139    
140      /**
141       * Returns the top input for this converter.
142       * @return the top input for this converter.
143       */
144      public SignalPipe getTopInputPipe()
145      {
146        return topHalf.inputFork.getInputPipe();
147      }
148      
149      /**
150       * Returns the bottom input for this converter.
151       * @return the bottom input for this converter.
152       */
153      public SignalPipe getBottomInputPipe()
154      {
155        return bottomHalf.inputFork.getInputPipe();
156      }
157      
158      /**
159       * Returns the top outputs for this converter.
160       * <p>
161       * The returned list will contain two outputs.  The output at index 0 will
162       * hold a signal that came either from the direct path, or from the LO-1 path.
163       * The output at index 0 will hold a signal that came either from the direct
164       * path, the LO-1 path, or the LO-2 path.  The valid pairs of output pathways
165       * are:</p>
166       * <ul>
167       *   <li>Direct, Direct</li>
168       *   <li>Direct, LO-2</li>
169       *   <li>LO-1, LO-1</li>
170       *   <li>LO-1, LO-2</li>
171       * </ul>
172       * <p>
173       * The above pairs are dependent on the settings of this converter's
174       * {@link #setOutputSwitches(String, String) output switches}.</p>
175       * 
176       * @return the top outputs for this converter.
177       */
178      public List<SignalPipe> getTopOutputPipes()
179      {
180        List<SignalPipe> outputs = new ArrayList<SignalPipe>();
181        
182        outputs.add(topHalf.outputFork.getOutputPipe(0));
183        outputs.add(topHalf.outputSwitch2.getOutputPipe(0));
184        
185        return outputs;
186      }
187      
188      /**
189       * Returns the bottom outputs for this converter.
190       * <p>
191       * The returned list will contain two outputs.  The output at index 0 will
192       * hold a signal that came either from the direct path, or from the LO-1 path.
193       * The output at index 0 will hold a signal that came either from the direct
194       * path, the LO-1 path, or the LO-2 path.  The valid pairs of output pathways
195       * are:</p>
196       * <ul>
197       *   <li>Direct, Direct</li>
198       *   <li>Direct, LO-2</li>
199       *   <li>LO-1, LO-1</li>
200       *   <li>LO-1, LO-2</li>
201       * </ul>
202       * <p>
203       * The above pairs are dependent on the settings of this converter's
204       * {@link #setOutputSwitches(String, String) output switches}.</p>
205       * 
206       * @return the bottom outputs for this converter.
207       */
208      public List<SignalPipe> getBottomOutputPipes()
209      {
210        List<SignalPipe> outputs = new ArrayList<SignalPipe>();
211        
212        outputs.add(bottomHalf.outputFork.getOutputPipe(0));
213        outputs.add(bottomHalf.outputSwitch2.getOutputPipe(0));
214        
215        return outputs;
216      }
217      
218      //This block defines the valid parameters for setOutputSwitches(...) 
219      private static final ArrayList<String> VALID_OUT1_PATHS =
220        new ArrayList<String>();
221      private static final HashMap<String, List<String>> VALID_OUT2_PATHS =
222        new HashMap<String, List<String>>();
223      static
224      {
225        VALID_OUT1_PATHS.add("DIRECT");
226        VALID_OUT1_PATHS.add("LO1");
227        
228        VALID_OUT2_PATHS.put("DIRECT", Arrays.asList("DIRECT", "LO2"));
229        VALID_OUT2_PATHS.put("LO1",    Arrays.asList("LO1",    "LO2"));
230      }
231      
232      /**
233       * Sets internal switches that determine which internal pathways reach the
234       * outputs.  There are three internal pathways: a direct pathway that is
235       * not mixed down, a pathway that is mixed with {@code LO1} (where "LO1"
236       * is the first parameter of the {@link #T303(SignalSource, SignalSource)
237       * constructor} used to build this device), and a pathway that is mixed
238       * with {@code LO2}.  Only certain combinations are permitted, and are
239       * as follows:<p/>
240       * <ul>
241       *   <li>"Direct", "Direct"</li>
242       *   <li>"Direct, "LO2"</li>
243       *   <li>"LO1", "LO1"</li>
244       *   <li>"LO1, "LO2"</li>
245       * </ul>
246       * <p>
247       * The above names are not case sensitive.</p>
248       * <p>
249       * This method sets the top half and bottom half output switches in
250       * tandem.  That is, if the top half is set to produce outputs
251       * LO1 / LO2, so is the bottom half.</p>
252       * 
253       * @param output1
254       *   the name of the pathway ("Direct", "LO1") to be sent through
255       *   the 0<sup>th</sup> output.
256       *   
257       * @param output2
258       *   the name of the pathway ("Direct", "LO1", "LO2") to be sent through
259       *   the 1<sup>st</sup> output.
260       */
261      public void setOutputSwitches(String output1, String output2)
262      {
263        String outPath1 = output1.toUpperCase();
264        String outPath2 = output2.toUpperCase();
265        
266        if (!VALID_OUT1_PATHS.contains(outPath1))
267        {
268          ; //TODO exception
269        }
270        else
271        {
272          if (!VALID_OUT2_PATHS.get(outPath1).contains(outPath2))
273          {
274            ; //TODO exception
275          }
276          else
277          {
278            topHalf.outputSwitch1.selectInput(outPath1);
279            bottomHalf.outputSwitch1.selectInput(outPath1);
280            
281            if (!outPath2.equals("LO2"))
282              outPath2 = "OUTPUT1";
283    
284            topHalf.outputSwitch2.selectInput(outPath2);
285            bottomHalf.outputSwitch2.selectInput(outPath2);
286          }
287        }
288      }
289    
290      void nameOutputPolesOfOutputSwitches(String topSw1,    String topSw2,
291                                           String bottomSw1, String bottomSw2)
292      {
293        topHalf.outputSwitch1.nameOutputs(topSw1);
294        topHalf.outputSwitch2.nameOutputs(topSw2);
295        bottomHalf.outputSwitch1.nameOutputs(bottomSw1);
296        bottomHalf.outputSwitch2.nameOutputs(bottomSw2);
297      }
298    
299      void addSwitchesTo(SortedMap<String, SignalSwitch> map)
300      {
301        topHalf.addSwitchesTo(map);
302        bottomHalf.addSwitchesTo(map);
303      }
304    
305      /**
306       * Returns the signals produced by this converter.
307       * The returned collection will hold four signals.
308       * 
309       * @return the signals produced by this converter.
310       */
311      public Collection<Signal> getSignals()
312      {
313        Collection<Signal> signals = new ArrayList<Signal>();
314        
315        signals.add(topHalf.outputFork.getSignal());
316        signals.add(topHalf.outputSwitch2.getSignal());
317        signals.add(bottomHalf.outputFork.getSignal());
318        signals.add(bottomHalf.outputSwitch2.getSignal());
319        
320        return signals;
321      }
322    
323      /**
324       * Returns the pass band for the filter on the path that has no local
325       * oscillator.
326       * 
327       * @return pass band of filter on direct (no LO) path.
328       * 
329       * @since 2008-10-28
330       */
331      public FrequencyRange getDirectPathFilterPassBand()
332      {
333        return topHalf.directPathFilter.getPassBand();
334      }
335      
336      /**
337       * Returns the pass band for the filter that is upstream of the two
338       * local oscillator pathways.
339       * 
340       * @return pass band of filter on fork leading to LO pathways.
341       *
342       * @since 2008-10-20
343       */
344      public FrequencyRange getLocOscPathFilterPassBand()
345      {
346        return topHalf.loPathFilter.getPassBand();
347      }
348      
349      /**
350       * Returns the pass band for the filter that is between the multiplier
351       * and mixer for local oscillator one.  This filter effectively narrows
352       * the allowable tunings for that local oscillator.
353       * 
354       * @return pass band for LO-1's signal.
355       */
356      public FrequencyRange getLocOsc1FilterPassBand()
357      {
358        return lo1Filter.getPassBand();
359      }
360      
361      /**
362       * Returns the pass band for the filter that is between the multiplier
363       * and mixer for local oscillator two.  This filter effectively narrows
364       * the allowable tunings for that local oscillator.
365       * 
366       * @return pass band for LO-1's signal.
367       */
368      public FrequencyRange getLocOsc2FilterPassBand()
369      {
370        return lo2Filter.getPassBand();
371      }
372      
373      /**
374       * Returns the factor by which the signal from the LO-1 is multiplied.
375       * @return the multiplier for local oscillator one.
376       */
377      public BigDecimal getLocOsc1Multiplier()
378      {
379        return lo1Multiplier.getMultiplicationFactor();
380      }
381      
382      /**
383       * Returns the factor by which the signal from the LO-2 is multiplied.
384       * @return the multiplier for local oscillator two.
385       */
386      public BigDecimal getLocOsc2Multiplier()
387      {
388        return lo2Multiplier.getMultiplicationFactor();
389      }
390    
391      //============================================================================
392      // INTERFACE SignalProcessor
393      //============================================================================
394      
395      /**
396       * See {@link SignalProcessor#execute()}.
397       */
398      public void execute()
399      {
400        topHalf.execute();
401        bottomHalf.execute();
402      }
403      
404      /**
405       * See {@link SignalProcessor#executeUpTo(SignalProcessor)}.
406       */
407      public void executeUpTo(SignalProcessor firstUnexecutedDevice)
408      {
409        if (firstUnexecutedDevice != this)
410        {
411          topHalf.executeUpTo(firstUnexecutedDevice);
412          bottomHalf.executeUpTo(firstUnexecutedDevice);
413        }
414      }
415    
416      /**
417       * See {@link SignalProcessor#executeFromStartOfChainUpTo(SignalProcessor)}.
418       */
419      public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice)
420      {
421        topHalf.executeFromStartOfChainUpTo(this);
422        bottomHalf.executeFromStartOfChainUpTo(this);
423        
424        executeUpTo(firstUnexecutedDevice);
425      }
426    
427      //============================================================================
428      // HELPER CLASSES
429      //============================================================================
430    
431      /**
432       * Represents roughly one half of the T303 UX Downconverter.
433       * Each half creates a pair of output signals from a single input.
434       * Note that the pair of LO chains (where each chain contains
435       * a multiplier, filter, and fork) are not considered to be
436       * part of this half, but are instead in addition to the
437       * two halves and are handled by class T303 directly.
438       * 
439       * On the antenna block diagram, switches S1-1 and S1-2 are
440       * inside the UX converter.  In our model we have moved these
441       * switched outside this converter.
442       */
443      private class OneHalfT303
444        implements SignalProcessor
445      {
446        private String         name;
447        private SignalManifold inputFork;
448        private SignalFilter   directPathFilter;
449        private SignalFilter   loPathFilter;
450        private SignalManifold loPathFork;
451                SignalMixer    lo1Mixer;
452                SignalMixer    lo2Mixer;
453        private SignalSwitch   outputSwitch1;  //in dgm this is xfer switch
454        private SignalManifold outputFork;
455        private SignalSwitch   outputSwitch2;  //in dgm this is xfer switch
456        
457        /** Creates half of a T303. */
458        OneHalfT303(String nameOfThisHalf)
459        {
460          name = nameOfThisHalf;
461    
462          constructInternalComponents();
463          connectInternalComponents();
464          
465          outputSwitch1.nameInputs("DIRECT",  "LO1");
466          outputSwitch2.nameInputs("OUTPUT1", "LO2");
467        }
468        
469        /**
470         * Builds, but does not connect, the internal components of this device.
471         */
472        private void constructInternalComponents()
473        {
474          //FILTERS
475          directPathFilter = SignalFilter.make(name+".direct-path-filter", "7.5","12.5");
476          loPathFilter     = SignalFilter.make(name+".LO-path-filter",    "11.5","19.5");
477          
478          //MIXERS
479          lo1Mixer = SignalMixer.makeSubtractiveMixer(name+".LO1-mixer");
480          lo2Mixer = SignalMixer.makeSubtractiveMixer(name+".LO2-mixer");
481          
482          //MANIFOLDS
483          inputFork  = new SignalManifold(2);
484          loPathFork = new SignalManifold(2);
485          outputFork = new SignalManifold(2);
486    
487          //SWITCHES
488          outputSwitch1 = SignalSwitch.makeMultiInputSwitch(name+".output-switch-1", 2);
489          outputSwitch2 = SignalSwitch.makeMultiInputSwitch(name+".output-switch-2", 2);
490        }
491        
492        /**
493         * Connects the prebuilt internal components of this device to one another.
494         */
495        private void connectInternalComponents()
496        {
497          //DIRECT PATH
498          inputFork.getOutputPipe(0).connectOutputTo(directPathFilter.getInputPipe());
499          directPathFilter.getOutputPipe().connectOutputTo(outputSwitch1.getInputPipe(0));
500          
501          //LO PATHS
502          inputFork.getOutputPipe(1).connectOutputTo(loPathFilter.getInputPipe());
503          loPathFilter.getOutputPipe().connectOutputTo(loPathFork.getInputPipe());
504          outputFork.getOutputPipe(1).connectOutputTo(outputSwitch2.getInputPipe(0));
505          
506          //LO-1 PATH
507          loPathFork.getOutputPipe(0).connectOutputTo(lo1Mixer.getInputPipe());
508          lo1Mixer.getOutputPipe().connectOutputTo(outputSwitch1.getInputPipe(1));
509          outputSwitch1.getOutputPipe(0).connectOutputTo(outputFork.getInputPipe());
510          
511          //LO-2 PATH
512          loPathFork.getOutputPipe(1).connectOutputTo(lo2Mixer.getInputPipe());
513          lo2Mixer.getOutputPipe().connectOutputTo(outputSwitch2.getInputPipe(1));
514        }
515    
516        void addSwitchesTo(SortedMap<String, SignalSwitch> map)
517        {
518          map.put(outputSwitch1.getName(), outputSwitch1);
519          map.put(outputSwitch2.getName(), outputSwitch2);
520        }
521        
522        //============================================================================
523        // INTERFACE SignalProcessor
524        //============================================================================
525        
526        /* (non-Javadoc)
527         * @see edu.nrao.sss.electronics.SignalProcessor#execute()
528         */
529        public void execute()
530        {
531          inputFork.execute();
532        }
533        
534        public void executeUpTo(SignalProcessor firstUnexecutedDevice)
535        {
536          if (firstUnexecutedDevice != this)
537            inputFork.executeUpTo(firstUnexecutedDevice);
538        }
539    
540        public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice)
541        {
542          inputFork.executeFromStartOfChainUpTo(firstUnexecutedDevice);
543        }
544      }
545    
546      //============================================================================
547      // 
548      //============================================================================
549      /*
550      public static void main(String[] args) throws Exception
551      {
552        Frequency lowFreq  = new Frequency(11904.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ);
553        Frequency highFreq = new Frequency(20352.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ);
554        Frequency stepSize = new Frequency(  256.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ);
555    
556        edu.nrao.sss.electronics.LocalOscillator LO1 =
557          new edu.nrao.sss.electronics.LocalOscillator(new edu.nrao.sss.measure.FrequencyRange(lowFreq, highFreq), stepSize);
558        edu.nrao.sss.electronics.LocalOscillator LO2 =
559          new edu.nrao.sss.electronics.LocalOscillator(new edu.nrao.sss.measure.FrequencyRange(lowFreq, highFreq), stepSize);
560        
561        T303 t303 = new T303(LO1, LO2);
562        
563        Signal lcp = new Signal(new edu.nrao.sss.measure.FrequencyRange(new edu.nrao.sss.measure.Frequency(12.0),
564                                                   new edu.nrao.sss.measure.Frequency(18.0)), edu.nrao.sss.astronomy.PolarizationType.L);
565        
566        Signal rcp = new Signal(new edu.nrao.sss.measure.FrequencyRange(new Frequency(12.0),
567                                                   new edu.nrao.sss.measure.Frequency(18.0)), edu.nrao.sss.astronomy.PolarizationType.R);
568        
569        //SignalMixer mixer = SignalMixer.makeSubtractiveMixer(null);
570        //mixer.getLocalOscillatorInputPipe().connectInputTo(new SignalSource()
571        //{
572        //  public Signal getSignal()
573        //  {
574        //    return new Signal(new Frequency(57.216));
575        //  } 
576        //});
577        
578        final Signal topSignal    = lcp;//mixer.mix(lcp);
579        final Signal bottomSignal = rcp;//mixer.mix(rcp);
580        
581        t303.getTopInputPipe().connectInputTo(new SignalSource()
582        {
583          public Signal getSignal()
584          {
585            return topSignal;
586          } 
587        });
588        
589        t303.getBottomInputPipe().connectInputTo(new SignalSource()
590        {
591          public Signal getSignal()
592          {
593            return bottomSignal;
594          } 
595        });
596    
597        t303.setOutputSwitches("LO1", "LO2");
598        LO1.tuneTo(new Frequency(25.6 / 2.0));  //gets x2
599        LO2.tuneTo(new Frequency(24.0 / 2.0));  //gets x2
600        
601        t303.execute();
602        
603        System.out.println("LO Tunings");
604        System.out.println("  LO1 = " + LO1.getCurrentTuning());
605        System.out.println("  LO2 = " + LO2.getCurrentTuning());
606        System.out.println();
607        
608        System.out.println("Output Signals");
609        System.out.println();
610        SignalFilter filter = SignalFilter.make(null, 7.5, 12.5);
611        for (Signal signal : t303.getSignals())
612        {
613          System.out.println(filter.filter(signal));
614        }
615      }
616      */
617    }