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 }