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 }