001 package edu.nrao.sss.model.resource; 002 003 import java.util.HashMap; 004 import java.util.HashSet; 005 import java.util.Map; 006 import java.util.Set; 007 008 import edu.nrao.sss.astronomy.PolarizationType; 009 import edu.nrao.sss.electronics.LocalOscillatorPath; 010 import edu.nrao.sss.electronics.Signal; 011 import edu.nrao.sss.electronics.SignalManifold; 012 import edu.nrao.sss.electronics.SignalMixer; 013 import edu.nrao.sss.electronics.SignalPipe; 014 import edu.nrao.sss.electronics.SignalSource; 015 import edu.nrao.sss.measure.Frequency; 016 import edu.nrao.sss.measure.FrequencyRange; 017 018 /** 019 * The portion of an antenna's electronics that immediately follows 020 * the collector of the sky signal. 021 * <p> 022 * <b>Version Info:</b> 023 * <table style="margin-left:2em"> 024 * <tr><td>$Revision: 1710 $</td></tr> 025 * <tr><td>$Date: 2008-11-14 11:54:07 -0700 (Fri, 14 Nov 2008) $</td></tr> 026 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 027 * </table></p> 028 * 029 * @author David M. Harland 030 * @since 2007-11-01 031 */ 032 public class AntennaFrontEnd 033 { 034 private ReceiverBand receiverBand; 035 private Signal signalTemplate; 036 private LocalOscillatorPath loSignalPath; 037 private SignalManifold loSignalFork; 038 039 private boolean useSubtractiveMixers; 040 041 private HashMap<PolarizationType, SignalMixer> mixers; 042 private HashMap<PolarizationType, SignalPipe> outputs; 043 044 private boolean isOn; 045 046 //============================================================================ 047 // OBJECT CONSTRUCTION 048 //============================================================================ 049 050 /** 051 * Creates a new front end whose output signals are the same as those of 052 * its receiver band. To create a front end that mixes its receiver band's 053 * signals up or down, see the 054 * {@link #AntennaFrontEnd(ReceiverBand, Set, SignalSource, int, FrequencyRange, boolean) 055 * other constructor}. 056 * This constructor is equivalent to calling 057 * {@code AntennaFrontEnd(band, polarizations, null, 1.0, true)}. 058 * Note that this front end is initially turned off. 059 * 060 * @param band 061 * the receiver band that this front end handles. 062 * 063 * @param polarizations 064 * the polarizations of the outputs of this feed. This feed will have one 065 * output for each of these polarizations. 066 * 067 * @see #turnOn() 068 */ 069 public AntennaFrontEnd(ReceiverBand band, Set<PolarizationType> polarizations) 070 { 071 this(band, polarizations, null, 1, null, true); 072 } 073 074 /** 075 * Creates a new front end that is initially turned off. 076 * 077 * @param band 078 * the receiver band that this front end handles. 079 * 080 * @param polarizations 081 * the polarizations of the outputs of this feed. This feed will have one 082 * output for each of these polarizations. 083 * 084 * @param localOscillator 085 * the source of the signal, if any, to mix with the feed before producing 086 * the outputs of this front end. For front ends that do no mixing, 087 * use a value of <i>null</i> here. 088 * 089 * @param loMultiplier 090 * the factor to apply to the signal of the {@code localOscillator} 091 * before mixing with the sky signal. This value will be ignored if 092 * {@code localOscillator} is <i>null</i>. If this feed has an LO 093 * that does not get multiplied, use a value of <tt>1.0</tt> here. 094 * 095 * @param filterRange 096 * the range of the filter, if any, attached to the output of the 097 * multiplier, or directly to the LO if there is no multiplier. 098 * If no filter is used, pass a value of <i>null</i>. 099 * 100 * @param mixDown 101 * <i>true</i> if the {@code localOscillator}'s signal should be used 102 * to mix this front end's signal down to lower frequencies, <i>false</i> 103 * if it should use the LO to mix it upward. Even if no mixing is required 104 * (as indicated by a <i>null</i> {@code localOscillator}), this value has 105 * an impact on the way 106 * {@link #suggestLOFrequencyToMoveOutputCenterTo(Frequency)} 107 * calculates its result. 108 * 109 * @see #turnOn() 110 */ 111 public AntennaFrontEnd(ReceiverBand band, Set<PolarizationType> polarizations, 112 SignalSource localOscillator, int loMultiplier, 113 FrequencyRange filterRange, boolean mixDown) 114 { 115 //TODO parameter checking 116 117 receiverBand = band; 118 119 signalTemplate = new Signal(receiverBand.getWidestRange(), 120 PolarizationType.UNSPECIFIED); 121 122 useSubtractiveMixers = mixDown; 123 124 outputs = new HashMap<PolarizationType, SignalPipe>(); 125 126 if (localOscillator == null) 127 { 128 initForNoLocOsc(polarizations); 129 } 130 else 131 { 132 initForLocOsc(polarizations, localOscillator, loMultiplier, filterRange); 133 } 134 } 135 136 private void initForNoLocOsc(Set<PolarizationType> polarizations) 137 { 138 loSignalPath = null; 139 loSignalFork = null; 140 mixers = null; 141 142 //Pass the receiver band's signal straight to output 143 for (PolarizationType p : polarizations) 144 outputs.put(p, SignalPipe.makeAndWeldInputTo(new RadioSource(p))); 145 } 146 147 private void initForLocOsc(Set<PolarizationType> polarizations, 148 SignalSource LO, int mult, FrequencyRange filterRange) 149 { 150 int outputCount = polarizations.size(); 151 152 loSignalPath = new LocalOscillatorPath(LO, mult, filterRange); 153 loSignalFork = new SignalManifold(outputCount); 154 loSignalFork.getInputPipe().connectInputTo(loSignalPath); 155 156 mixers = new HashMap<PolarizationType, SignalMixer>(); 157 158 int outFork = 0; 159 160 for (PolarizationType p : polarizations) 161 { 162 SignalMixer mixer = 163 useSubtractiveMixers ? SignalMixer.makeSubtractiveMixer("mix-down") 164 : SignalMixer.makeAdditiveMixer("mix-up"); 165 166 SignalPipe output = SignalPipe.makeAndWeldInputTo(mixer.getOutputPipe()); 167 168 mixers.put(p, mixer); 169 outputs.put(p, output); 170 171 //Connect receiver band's signal to mixer 172 mixer.getInputPipe().connectInputTo(new RadioSource(p)); 173 mixer.getLocalOscillatorInputPipe() 174 .connectInputTo(loSignalFork.getOutputPipe(outFork++)); 175 } 176 } 177 178 //============================================================================ 179 // EVLA HELPERS 180 //============================================================================ 181 182 private static final Map<ReceiverBand, Integer> EVLA_LO_MULTIPLIERS = 183 new HashMap<ReceiverBand, Integer>(); 184 185 static 186 { 187 EVLA_LO_MULTIPLIERS.put(ReceiverBand.EVLA_Q, 3); 188 EVLA_LO_MULTIPLIERS.put(ReceiverBand.EVLA_Ka, 3); 189 EVLA_LO_MULTIPLIERS.put(ReceiverBand.EVLA_K , 2); 190 } 191 192 private static final Map<ReceiverBand, FrequencyRange> EVLA_LO_FILTERS = 193 new HashMap<ReceiverBand, FrequencyRange>(); 194 195 static 196 { 197 EVLA_LO_FILTERS.put(ReceiverBand.EVLA_Q, 198 new FrequencyRange(new Frequency("48.0"), 199 new Frequency("58.5"))); 200 EVLA_LO_FILTERS.put(ReceiverBand.EVLA_Ka, 201 new FrequencyRange(new Frequency("44.0"), 202 new Frequency("49.0"))); 203 EVLA_LO_FILTERS.put(ReceiverBand.EVLA_K, 204 new FrequencyRange(new Frequency("30.0"), 205 new Frequency("36.0"))); 206 } 207 208 /** 209 * Creates all of the EVLA front ends and returns them in a map whose 210 * key is the receiver band of that front end. 211 * 212 * @param loSignals 213 * a map whose key is a receiver band and whose value will be treated 214 * as a local oscillator of the front end for that band. Only those 215 * front ends that use LOs need have an entry in this map. 216 * 217 * @return 218 * a map containing all the EVLA front ends, keyed by receiver band. 219 */ 220 public static Map<ReceiverBand, AntennaFrontEnd> 221 makeEvlaFrontEnds(Map<ReceiverBand, SignalSource> loSignals) 222 { 223 HashMap<ReceiverBand, AntennaFrontEnd> frontEnds = 224 new HashMap<ReceiverBand, AntennaFrontEnd>(); 225 226 HashSet<PolarizationType> polarizations = new HashSet<PolarizationType>(); 227 polarizations.add(PolarizationType.L); 228 polarizations.add(PolarizationType.R); 229 230 for (ReceiverBand band : TelescopeType.EVLA.getReceivers()) 231 { 232 int multiplier = 233 EVLA_LO_MULTIPLIERS.containsKey(band) ? EVLA_LO_MULTIPLIERS.get(band) : 1; 234 235 FrequencyRange filterRange = EVLA_LO_FILTERS.get(band); 236 237 frontEnds.put(band, 238 new AntennaFrontEnd(band, polarizations, loSignals.get(band), 239 multiplier, filterRange, true)); 240 } 241 242 return frontEnds; 243 } 244 245 //============================================================================ 246 // 247 //============================================================================ 248 249 /** 250 * Returns the receiver band handled by this front end. 251 * @return the receiver band handled by this front end. 252 */ 253 public ReceiverBand getBand() 254 { 255 return receiverBand; 256 } 257 258 /** 259 * Returns the polarizations of the outputs produced by this front end. 260 * This front end will have one output for each of these polarizations. 261 * 262 * @return the polarizations of the outputs produced by this front end. 263 */ 264 public Set<PolarizationType> getPolarizations() 265 { 266 return new HashSet<PolarizationType>(outputs.keySet()); 267 } 268 269 /** 270 * Returns the output of this front end that produces signals with the given 271 * polarization. If this front end has no such outputs, <i>null</i> is 272 * returned. 273 * 274 * @param polarizationType 275 * the polarization of signals produced by the returned output. 276 * 277 * @return 278 * an output whose signals have {@code polarizationType} polarization. 279 */ 280 public SignalPipe getOuputPipe(PolarizationType polarizationType) 281 { 282 return outputs.get(polarizationType); 283 } 284 285 /** 286 * Returns the local oscillator path, if any, for this front end. 287 * 288 * @return 289 * the local oscillator path for this front end. If this front end 290 * has no local oscillator path, <i>null</i> is returned. 291 */ 292 public LocalOscillatorPath getLocalOscillatorPath() 293 { 294 return loSignalPath; 295 } 296 297 /** 298 * Returns a frequency that can be used to shift the center frequency 299 * of this front end to {@code ifCenter}. 300 * <p><i>TODO: determine how to handle impossible requests.</i></p> 301 * 302 * @param ifCenter 303 * the central frequency produced by mixing this front end's signal 304 * with a local oscillator's signal. 305 * 306 * @return a frequency that can be used to shift the center frequency 307 * of this front end to {@code ifCenter}. 308 */ 309 public Frequency suggestLOFrequencyToMoveOutputCenterTo(Frequency ifCenter) 310 { 311 Frequency loFreq = null; 312 Frequency currCtr = receiverBand.getNominalRange().getCenterFrequency(); 313 314 if (loSignalPath == null) 315 { 316 loFreq = useSubtractiveMixers ? ifCenter.clone().add(currCtr) 317 : ifCenter.clone().subtract(currCtr); 318 } 319 else 320 { 321 loFreq = useSubtractiveMixers ? 322 loSignalPath.getLOFreqToMoveCenterViaSubtraction(currCtr, ifCenter) : 323 loSignalPath.getLOFreqToMoveCenterViaAddition(currCtr, ifCenter); 324 } 325 326 return loFreq; 327 } 328 329 /** 330 * Executes all the devices that are connected to the outputs of this 331 * front end. 332 */ 333 public void execute() 334 { 335 for (SignalPipe output : outputs.values()) 336 output.executeFromStartOfChainUpTo(null); 337 } 338 339 /** 340 * Returns <i>true</i> if this front end is picking up signals. 341 * @return <i>true</i> if this front end is picking up signals. 342 */ 343 public boolean isOn() { return isOn; } 344 345 /** 346 * Returns <i>true</i> if this front end is not picking up signals. 347 * @return <i>true</i> if this front end is not picking up signals. 348 */ 349 public boolean isOff() { return !isOn; } 350 351 /** Turns on this front end so that it picks up signals. */ 352 public void turnOn() { isOn = true; } 353 354 /** Turns off this front end so that it does not pick up signals. */ 355 public void turnOff() { isOn = false; } 356 357 //============================================================================ 358 // HELPER CLASSES 359 //============================================================================ 360 361 /** 362 * This class is a stand-in for a sky source. It produces a signal equivalent 363 * to the feed of this front end, polarized according to the particular output 364 * that is using it. 365 */ 366 private class RadioSource implements SignalSource 367 { 368 private Signal signal; 369 370 RadioSource(PolarizationType p) 371 { 372 signal = signalTemplate.clone(p); 373 signal.appendToDevicePath(receiverBand.name()+"-"+p.name()); 374 } 375 376 public Signal getSignal() 377 { 378 return isOn ? signal.clone() : null; 379 } 380 } 381 382 //============================================================================ 383 // 384 //============================================================================ 385 /* 386 public static void main(String[] args) throws Exception 387 { 388 //L301s 389 Frequency low = new Frequency(11904.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ); 390 Frequency high = new Frequency(20352.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ); 391 Frequency step = new Frequency( 256.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ); 392 393 edu.nrao.sss.electronics.LocalOscillator L301_1 = 394 new edu.nrao.sss.electronics.LocalOscillator( 395 new edu.nrao.sss.measure.FrequencyRange(low,high),step); 396 397 Map<ReceiverBand, SignalSource> LOs = new HashMap<ReceiverBand, SignalSource>(); 398 LOs.put(ReceiverBand.EVLA_Q, L301_1); 399 LOs.put(ReceiverBand.EVLA_Ka, L301_1); 400 LOs.put(ReceiverBand.EVLA_K, L301_1); 401 402 Map<ReceiverBand, AntennaFrontEnd> FEs = AntennaFrontEnd.makeEvlaFrontEnds(LOs); 403 404 for (AntennaFrontEnd fe : FEs.values()) 405 { 406 if (LOs.get(fe.getBand()) != null) 407 { 408 L301_1.tuneTo(fe.suggestLOFrequencyToMoveOutputCenterTo(new Frequency(12.0))); 409 } 410 411 fe.execute(); 412 413 for (PolarizationType p : fe.getPolarizations()) 414 { 415 System.out.print(fe.getBand().name()+", "); 416 System.out.println(p.toString()); 417 System.out.println(fe.getOuputPipe(p).getSignal()); 418 System.out.println(); 419 } 420 } 421 } 422 */ 423 }