001    package edu.nrao.sss.model.resource.evla;
002    
003    import java.util.ArrayList;
004    import java.util.Collection;
005    import java.util.SortedMap;
006    
007    import edu.nrao.sss.electronics.Signal;
008    import edu.nrao.sss.electronics.SignalFilter;
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    import edu.nrao.sss.measure.FrequencyRange;
017    
018    /**
019     * The EVLA T304/T305 Downconverter.
020     * <p>
021     * This device takes a single input, filters it, then sends it down two
022     * pathways.  Each pathway leads to a mixer, and each of these receives
023     * its signal from a different local oscillator.  The pathway containing
024     * local oscillator LO-1 has a pair of switches that either send the signal
025     * through another, fixed frequency, LO and filters, or bypasses them.
026     * This LO-1 pathway is then switched so that it ends up going through
027     * one of two output filters and pipes.  The LO-2 pathway has no further
028     * mixing.</p>
029     * <p>
030     * The LO-2 output enters a single pipe and is always active (though, as
031     * used in the {@link EvlaAntennaElectronics EVLA electronics}, it can be
032     * ignored downstream), while the LO-1 output goes through one of two pipes,
033     * {@link #configureForWidestOutput(boolean) selectable} by clients.</p>
034     *  
035     * <img src="doc-files/T304.png" alt="T304 Dgm"
036     *      style="height:auto; width:100%; max-width:974px"/>
037     * <p>
038     * <b>Version Info:</b>
039     * <table style="margin-left:2em">
040     *   <tr><td>$Revision: 1664 $</td></tr>
041     *   <tr><td>$Date: 2008-10-31 16:54:14 -0600 (Fri, 31 Oct 2008) $</td></tr>
042     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
043     * </table></p>
044     * 
045     * @author David M. Harland
046     * @since 2007-10-25
047     */
048    public class T304
049      implements SignalProcessor
050    {
051      private static final Frequency INTERNAL_LO_FREQ = new Frequency("4.096");
052      
053      private String name;
054      
055      //Internal components
056      private SignalFilter   inputFilter;
057      private SignalManifold inputFork;
058      private SignalMixer    lo1Mixer;
059      private SignalMixer    lo2Mixer;
060      private SignalFilter   postLo1Filter;
061      private SignalFilter   postLo2Filter;
062      private SignalManifold lo1PathFork;
063      //private SignalSwitch   lo1PathExtraFilterBypassSwitch1;
064      private SignalFilter   preInternalLoFilter;
065      private SignalMixer    internalLoMixer;
066      private SignalFilter   postInternalLoFilter;
067      private SignalSwitch   internalLoSwitch;
068      private SignalSwitch   lo1PathOutputSwitch;
069      private SignalFilter   outputFilter0;
070      private SignalFilter   outputFilter1;
071      private SignalFilter   outputFilter2;
072      
073      private boolean useLo1Wide;
074    
075      //============================================================================
076      // OBJECT CONSTRUCTION
077      //============================================================================
078    
079      /**
080       * Creates a new T304 Downconverter that uses the given sources as its
081       * local oscillators.
082       * 
083       * @param deviceName
084       *   an optional name for this device.  If this value is <i>null</i>
085       *   a default name will be used in its place.
086       *   
087       * @param LO1
088       *   provider of the LO-1 local oscillator signal.
089       *   
090       * @param LO2
091       *   provider of the LO-2 local oscillator signal.
092       */
093      public T304(String deviceName, SignalSource LO1, SignalSource LO2)
094      {
095        name = (deviceName == null) ? "T304" : deviceName;
096        
097        constructInternalComponents();
098        connectInternalComponents();
099        
100        lo1PathOutputSwitch.nameInputs("LO1");
101        lo1PathOutputSwitch.nameOutputs("NARROW", "WIDE");
102    
103        //Set frequency sources for the mixers
104        lo1Mixer.getLocalOscillatorInputPipe().connectInputTo(LO1);
105        lo2Mixer.getLocalOscillatorInputPipe().connectInputTo(LO2);
106        
107        internalLoMixer.getLocalOscillatorInputPipe().connectInputTo
108        (
109          new SignalSource()
110          {
111            public Signal getSignal()
112            {
113              return new Signal(INTERNAL_LO_FREQ);
114            }
115          }
116        );
117        
118        configureForWidestOutput(true);
119      }
120      
121      /**
122       * Builds, but does not connect, the internal components of this device.
123       */
124      private void constructInternalComponents()
125      {
126        //FILTERS
127        inputFilter          = SignalFilter.make(name+".input-filter", "7.5", "12.5");
128        postLo1Filter        = SignalFilter.makeLowPass(null, "5.0");
129        postLo2Filter        = SignalFilter.makeLowPass(null, "5.0");
130        preInternalLoFilter  = SignalFilter.make(null, "2.048", "4.096");
131        postInternalLoFilter = SignalFilter.make(null, "1.024", "2.048");
132        outputFilter0        = SignalFilter.make(name+".output-0-filter", "1.024", "2.048");
133        outputFilter1        = SignalFilter.make(name+".output-1-filter", "2.048", "4.096");
134        outputFilter2        = SignalFilter.make(name+".output-2-filter", "2.048", "4.096");
135        
136        //MIXERS
137        lo1Mixer        = SignalMixer.makeSubtractiveMixer(name+".LO1-mixer");
138        lo2Mixer        = SignalMixer.makeSubtractiveMixer(name+".LO2-mixer");
139        internalLoMixer = SignalMixer.makeSubtractiveMixer(name+".internal-LO-mixer");
140        
141        //MANIFOLDS
142        inputFork   = new SignalManifold(2);
143        lo1PathFork = new SignalManifold(2);
144    
145        //SWITCHES
146        internalLoSwitch    = SignalSwitch.makeMultiInputSwitch (name+".internal-LO-switch", 2);
147        lo1PathOutputSwitch = SignalSwitch.makeMultiOutputSwitch(name+".LO1-output-switch",  2);
148      }
149      
150      /**
151       * Connects the prebuilt internal components of this device to one another.
152       */
153      private void connectInternalComponents()
154      {
155        //T304 INPUT
156        inputFilter.getOutputPipe().connectOutputTo(inputFork.getInputPipe());
157        
158        //TOP PATH (LO 1)
159        inputFork.getOutputPipe(0).connectOutputTo(lo1Mixer.getInputPipe());
160        lo1Mixer.getOutputPipe().connectOutputTo(postLo1Filter.getInputPipe());
161        
162        //Using manifold after LO1 filter and before the extra mixer path instead of switch
163        postLo1Filter.getOutputPipe().connectOutputTo(lo1PathFork.getInputPipe());
164        lo1PathFork.getOutputPipe(0).connectOutputTo(internalLoSwitch.getInputPipe(0));
165        lo1PathFork.getOutputPipe(1).connectOutputTo(preInternalLoFilter.getInputPipe());
166    
167        //Construct the extra mixer path
168        internalLoSwitch.nameInputs("BYPASS", "MIX");
169        internalLoSwitch.nameOutputs("LO1-OUTPUT");
170        preInternalLoFilter.getOutputPipe().connectOutputTo(internalLoMixer.getInputPipe());
171        internalLoMixer.getOutputPipe().connectOutputTo(postInternalLoFilter.getInputPipe());
172        postInternalLoFilter.getOutputPipe().connectOutputTo(internalLoSwitch.getInputPipe(1));
173        
174        //Connect the output switch
175        internalLoSwitch.getOutputPipe(0).connectOutputTo(lo1PathOutputSwitch.getInputPipe(0));
176        lo1PathOutputSwitch.getOutputPipe(0).connectOutputTo(outputFilter0.getInputPipe());
177        lo1PathOutputSwitch.getOutputPipe(1).connectOutputTo(outputFilter1.getInputPipe());
178            
179        //BOTTOM PATH (LO 2)
180        inputFork.getOutputPipe(1).connectOutputTo(lo2Mixer.getInputPipe());
181        lo2Mixer.getOutputPipe().connectOutputTo(postLo2Filter.getInputPipe());
182        postLo2Filter.getOutputPipe().connectOutputTo(outputFilter2.getInputPipe());
183      }
184    
185      //============================================================================
186      // 
187      //============================================================================
188    
189      /**
190       * Returns the pass band for the input filter.
191       * @return the pass band for the input filter.
192       */
193      FrequencyRange getInputFilterPassBand()
194      {
195        return inputFilter.getPassBand();
196      }
197      
198      /**
199       * Returns the input for this converter.
200       * @return the input for this converter.
201       */
202      public SignalPipe getInputPipe()
203      {
204        return inputFilter.getInputPipe();
205      }
206      
207      /**
208       * Returns one of the two LO-1 outputs.
209       * The pipe at index <tt>0</tt> comes from the narrower
210       * 1-2GHz filter; the pipe at index <tt>1</tt> comes from
211       * the wider 2-4GHz filter.
212       * 
213       * @param index
214       *   the index of the desired LO-1 output pipe.  The valid
215       *   values are <tt>0</tt> and <tt>1</tt>.
216       *   
217       * @return one of the two LO-1 outputs.
218       * 
219       * @throws IllegalArgumentException
220       *   if {@code index} is not a legal value.
221       */
222      public SignalPipe getLO1OutputPipe(int index)
223      {
224        switch (index)
225        {
226          case 0: return outputFilter0.getOutputPipe();
227          case 1: return outputFilter1.getOutputPipe();
228          
229          default:
230            throw new IllegalArgumentException("'" + index +
231            "' is not a valid index for T304 output pipes.  "+
232            "The valid values are 0 & 1.");
233        }
234      }
235      
236      /**
237       * Returns the output that was mixed with the LO-2 signal.
238       * @return the output that was mixed with the LO-2 signal.
239       */
240      public SignalPipe getLO2OutputPipe()
241      {
242        return outputFilter2.getOutputPipe();
243      }
244      
245      /**
246       * Sets internal switches so that the LO-1 signal is passed through
247       * either the wider or narrower output filter.
248       * When using the narrow filter, the LO-1 signal is also passed through
249       * the extra LO and filters.  When using the wider filter the extra
250       * LO and filters are bypassed.
251       * 
252       * @param wide
253       *   <i>true</i> if the LO-1 signal should be passed through the
254       *   wider output filter and into the output pipe at index <tt>1</tt>.
255       */
256      public void configureForWidestOutput(boolean wide)
257      {
258        useInternalLO(wide);
259        
260        lo1PathOutputSwitch.selectOutput(wide ? "WIDE" : "NARROW");
261        
262        useLo1Wide = wide;
263      }
264      
265      /**
266       * Flips switches to either bypass or use the extra filters
267       * in the LO1 output path.  It seems like we would want to
268       * use the filters for 8-bit output and bypass them for 3-bit,
269       * but there could be exceptions, i'm sure!
270       */
271      private void useInternalLO(boolean bypass)
272      {
273        internalLoSwitch.selectInput(bypass ? "BYPASS" : "MIX");
274      }
275    
276      void addSwitchesTo(SortedMap<String, SignalSwitch> map)
277      {
278        map.put(internalLoSwitch.getName(),    internalLoSwitch);
279        map.put(lo1PathOutputSwitch.getName(), lo1PathOutputSwitch);
280      }
281    
282      /**
283       * Returns the signals produced by this converter.
284       * The returned collection will hold two signals, one from
285       * each of the local oscillator paths.
286       * 
287       * @return the signals produced by this converter.
288       */
289      public Collection<Signal> getSignals()
290      {
291        Collection<Signal> signals = new ArrayList<Signal>();
292        
293        signals.add(useLo1Wide ? outputFilter1.getMostRecentOutput()
294                               : outputFilter0.getMostRecentOutput());
295        
296        signals.add(outputFilter2.getMostRecentOutput());
297        
298        return signals;
299      }
300    
301      //============================================================================
302      // INTERFACE SignalProcessor
303      //============================================================================
304      
305      /**
306       * See {@link SignalProcessor#execute()}.
307       */
308      public void execute()
309      {
310        inputFilter.execute();
311      }
312      
313      /**
314       * See {@link SignalProcessor#executeUpTo(SignalProcessor)}.
315       */
316      public void executeUpTo(SignalProcessor firstUnexecutedDevice)
317      {
318        if (firstUnexecutedDevice != this)
319          inputFilter.executeUpTo(firstUnexecutedDevice);
320      }
321    
322      /**
323       * See {@link SignalProcessor#executeFromStartOfChainUpTo(SignalProcessor)}.
324       */
325      public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice)
326      {
327        inputFilter.executeFromStartOfChainUpTo(firstUnexecutedDevice);
328      }
329    
330      //============================================================================
331      // 
332      //============================================================================
333      /*
334      public static void main(String[] args) throws Exception
335      {
336        FrequencyRange loFreqRange = new FrequencyRange(new Frequency(10.8), new Frequency(14.8));
337        LocalOscillator LO1 = new LocalOscillator(loFreqRange);
338        LocalOscillator LO2 = new LocalOscillator(loFreqRange);
339        
340        T304 t304 = new T304("Test T304", LO1, LO2);
341        
342    //    Signal raw = new Signal(new FrequencyRange(new Frequency(44.716),
343      //                                             new Frequency(49.716)), PolarizationType.L);
344        
345        Signal raw = new Signal(new FrequencyRange(new Frequency(40.000),
346                                                   new Frequency(45.716)), PolarizationType.L);
347        
348        SignalMixer mixer = SignalMixer.makeSubtractiveMixer(null);
349        mixer.getLocalOscillatorInputPipe().connectInputTo(new SignalSource()
350        {
351          public Signal getSignal()
352          {
353            //return new Signal(new Frequency(57.216));
354            return new Signal(new Frequency(33.408));
355          } 
356        });
357    
358        final Signal signal = mixer.mix(raw);
359        
360        System.out.println("Input Signal");
361        System.out.println(signal);
362        
363        t304.getInputPipe().connectInputTo(new SignalSource()
364        {
365          public Signal getSignal()
366          {
367            return signal;
368          } 
369        });
370        
371        LO1.tuneTo(new Frequency(14.308));
372        LO2.tuneTo(new Frequency(12.308));
373        
374        System.out.println("LO Tunings");
375        System.out.println("  LO1 = " + LO1.getCurrentTuning());
376        System.out.println("  LO2 = " + LO2.getCurrentTuning());
377        System.out.println();
378        
379        t304.configureForWidestOutput(true);
380    
381        t304.execute();
382    
383        System.out.println("Output Signals");
384        System.out.println("==============");
385        for (Signal output : t304.getSignals())
386        {
387          System.out.println(output);
388        }
389      }
390      */
391    }