001 package edu.nrao.sss.model.resource; 002 003 import java.util.ArrayList; 004 import java.util.Date; 005 import java.util.EnumSet; 006 import java.util.HashSet; 007 import java.util.List; 008 import java.util.Map; 009 import java.util.Set; 010 import java.util.TimeZone; 011 import java.util.TreeSet; 012 013 import org.apache.log4j.Logger; 014 015 import edu.nrao.sss.astronomy.CoordinateConversionException; 016 import edu.nrao.sss.geom.EarthPosition; 017 import edu.nrao.sss.measure.Distance; 018 import edu.nrao.sss.measure.DistanceUnits; 019 import edu.nrao.sss.measure.Frequency; 020 import edu.nrao.sss.measure.FrequencyRange; 021 import edu.nrao.sss.measure.Latitude; 022 import edu.nrao.sss.measure.LinearVelocity; 023 import edu.nrao.sss.measure.Longitude; 024 import edu.nrao.sss.model.resource.evla.EvlaAntennaElectronics; 025 import edu.nrao.sss.model.source.Source; 026 import edu.nrao.sss.util.EnumerationUtility; 027 import edu.nrao.sss.util.StringUtil; 028 029 /** 030 * An enumeration of the NRAO telescopes. 031 * <p> 032 * <b>Version Info:</b> 033 * <table style="margin-left:2em"> 034 * <tr><td>$Revision: 1598 $</td></tr> 035 * <tr><td>$Date: 2008-10-07 10:25:51 -0600 (Tue, 07 Oct 2008) $</td></tr> 036 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 037 * </table></p> 038 * 039 * @author David M. Harland 040 * @since 2006-02-23 041 */ 042 public enum TelescopeType 043 implements ReceiverProvider, ReceiverSelector 044 { 045 /** The Greenbank Telescope. */ 046 GBT("G", "The Greenbank Telescope", 047 new EarthPosition(), 048 NullAntennaElectronics.class), 049 050 /** The Very Large Array. */ 051 //VLA position came from http://www.vla.nrao.edu/genpub/overview/ 052 VLA("A", "The Very Large Array", 053 new EarthPosition(Longitude.parse("-107d 37' 03.819\""), 054 Latitude.parse("34d 04' 43.497\""), 055 new Distance("2124.0", DistanceUnits.METER), 056 TimeZone.getTimeZone("America/Denver")), 057 NullAntennaElectronics.class, 058 CorrelatorName.VLA) 059 { 060 @Override 061 void initCombos() 062 { 063 TreeSet<ReceiverBand> combo1 = new TreeSet<ReceiverBand>(); 064 TreeSet<ReceiverBand> combo2 = new TreeSet<ReceiverBand>(); 065 TreeSet<ReceiverBand> combo3 = new TreeSet<ReceiverBand>(); 066 067 combo1.add(ReceiverBand.VLA_4); 068 combo1.add(ReceiverBand.VLA_P); 069 070 combo2.add(ReceiverBand.VLA_4); 071 combo2.add(ReceiverBand.VLA_L); 072 073 combo3.add(ReceiverBand.VLA_P); 074 combo3.add(ReceiverBand.VLA_L); 075 076 telescope.addCombo(combo1); 077 telescope.addCombo(combo2); 078 telescope.addCombo(combo3); 079 } 080 }, 081 082 /** The Very Long Baseline Array. */ 083 VLBA("B", "The Very Long Baseline Array", 084 new EarthPosition(), 085 NullAntennaElectronics.class), 086 087 /** The Expanded Very Large Array. */ 088 EVLA("E", "The Expanded Very Large Array", 089 new EarthPosition(Longitude.parse("-107d 37' 03.819\""), 090 Latitude.parse("34d 04' 43.497\""), 091 new Distance("2124.0", DistanceUnits.METER), 092 TimeZone.getTimeZone("America/Denver")), 093 EvlaAntennaElectronics.class, 094 CorrelatorName.VLA, 095 CorrelatorName.WIDAR) 096 { 097 @Override 098 void initCombos() 099 { 100 TreeSet<ReceiverBand> combo1 = new TreeSet<ReceiverBand>(); 101 TreeSet<ReceiverBand> combo2 = new TreeSet<ReceiverBand>(); 102 TreeSet<ReceiverBand> combo3 = new TreeSet<ReceiverBand>(); 103 104 combo1.add(ReceiverBand.EVLA_4); 105 combo1.add(ReceiverBand.EVLA_P); 106 107 combo2.add(ReceiverBand.EVLA_4); 108 combo2.add(ReceiverBand.EVLA_L); 109 110 combo3.add(ReceiverBand.EVLA_P); 111 combo3.add(ReceiverBand.EVLA_L); 112 113 telescope.addCombo(combo1); 114 telescope.addCombo(combo2); 115 telescope.addCombo(combo3); 116 } 117 }, 118 119 /** The Atacama Large Millimeter Array. */ 120 ALMA("L", "The Atacama Large Millimeter Array", 121 new EarthPosition(), 122 NullAntennaElectronics.class), 123 124 /** A telescope other than those listed herein. */ 125 OTHER("O", "Other", 126 new EarthPosition(), 127 NullAntennaElectronics.class); 128 129 private final String abbrev; 130 private final String longName; 131 private final EarthPosition location; 132 private List<CorrelatorName> backends; 133 134 private final Class<? extends AntennaElectronics> antennaElectronicsClass; 135 136 //Many of the methods delegate their work to this object 137 Telescope telescope; 138 139 /** 140 * Constructs a new telescope type. 141 * 142 * @param abbreviation a short name for the new telescope type. 143 */ 144 private TelescopeType(String abbreviation, String longName, 145 EarthPosition location, 146 Class<? extends AntennaElectronics> aeClass, 147 CorrelatorName... backends) 148 { 149 this.abbrev = abbreviation; 150 this.longName = longName; 151 this.location = location; 152 153 this.antennaElectronicsClass = aeClass; 154 155 this.telescope = null; 156 157 this.backends = new ArrayList<CorrelatorName>(); 158 if (backends != null) 159 for (CorrelatorName backend : backends) 160 this.backends.add(backend); 161 } 162 163 /** Returns the object that does much of the work for this one. */ 164 private Telescope getDelegate() 165 { 166 if (telescope == null) 167 initTelescope(); 168 169 return telescope; 170 } 171 172 /** Initializes the telescope instance variable. */ 173 private void initTelescope() 174 { 175 telescope = new Telescope(); 176 177 for (ReceiverBand rb : ReceiverBand.values()) 178 if (rb.getTelescope() == this) 179 telescope.addReceiver(rb); 180 181 initCombos(); 182 } 183 184 /** Initializes valid receiver combinations. */ 185 void initCombos() 186 { 187 //default implementation is to do nothing. 188 } 189 190 /** 191 * Returns the historical abbreviation for this telescope. 192 * 193 * @return a short name for this telescope. 194 */ 195 public String getLegacyAbbreviation() 196 { 197 return abbrev; 198 } 199 200 /** 201 * Returns a long name for this telescope. For example, the long name of the 202 * VLA is "The Very Large Array". 203 * @return a long name for this telescope. 204 */ 205 public String getLongName() 206 { 207 return longName; 208 } 209 210 /** 211 * Returns a copy of the location of this telescope. 212 * @return a copy of the location of this telescope. 213 */ 214 public EarthPosition getLocation() 215 { 216 //Even though EarthPosition looks immutable, it is not, because the 217 //objects it holds are themselves mutable. Therefore, return a clone. 218 return location.clone(); 219 } 220 221 /** 222 * Returns the receiver bands that this type of telescope has. 223 * @return the receiver bands that this type of telescope has. 224 */ 225 public Set<ReceiverBand> getReceivers() 226 { 227 return getDelegate().getReceivers(); 228 } 229 230 /** 231 * Returns a list of all the combinations of two or more receivers that 232 * may be used by this telescope simultaneously. 233 * <p> 234 * Note that the returned list, and the sets contained therein, are 235 * copies of those held by this telescope, so changes made to them 236 * will not be reflected in this object.</p> 237 * 238 * @return a list of all the combinations of two or more receivers that 239 * may be used by this telescope simultaneously. 240 */ 241 public List<Set<ReceiverBand>> getValidReceiverCombinations() 242 { 243 return getDelegate().getValidReceiverCombinations(); 244 } 245 246 /** 247 * Returns a default tuning plan for the local oscillators of this 248 * telescope. Each of this telescope's receivers gets a separate 249 * set of local oscillator tunings. 250 * <p> 251 * The returned map has a {@code ReceiverBand} as its key and another 252 * map as its value. This second map has as its key the name of 253 * a local oscillator, and its value is the frequency to which that 254 * LO should be tuned.</p> 255 * 256 * @return a default tuning plan for this telescope. 257 */ 258 public Map<ReceiverBand, Map<String, Frequency>> getTuningPlan() 259 { 260 return TuningPlanLoader.getTuningPlanFor(this); 261 } 262 263 /** 264 * Creates and returns antenna electronics for this telescope. 265 * If this telescope has no electronics, or if its electronics cannot be 266 * successfully built, a {@link NullAntennaElectronics null-like} 267 * electronics will be built and returned. 268 * 269 * @return 270 * new antenna electronics for this telescope. The returned object will 271 * never be <i>null</i>, but could be an implementation of the 272 * <a href="http://en.wikipedia.org/wiki/Null_Object_pattern"> 273 * null-object-pattern</a>. 274 */ 275 public AntennaElectronics makeElectronics() 276 { 277 AntennaElectronics electronics; 278 279 if (antennaElectronicsClass == null) 280 { 281 electronics = null; 282 } 283 else if (antennaElectronicsClass.equals(NullAntennaElectronics.class)) 284 { 285 electronics = new NullAntennaElectronics(this); 286 } 287 else 288 { 289 try { 290 //This is the hoped-for situation 291 electronics = antennaElectronicsClass.newInstance(); 292 } 293 catch (InstantiationException ie) { 294 electronics = null; 295 } 296 catch (IllegalAccessException iae) { 297 electronics = null; 298 } 299 } 300 301 if (electronics == null) 302 electronics = new NullAntennaElectronics(TelescopeType.OTHER); 303 304 return electronics; 305 } 306 307 /** 308 * Returns a list of the backend processors available on this telescope. 309 * The term "backend" encompasses correlators, spectrometers, 310 * and any other electronic devices that can take as input the digital 311 * signals output by this telescope's {@link AntennaElectronics antenna 312 * electronics}. 313 * 314 * @return a list of the backend processors available on this telescope. 315 */ 316 public List<CorrelatorName> getBackendTypes() 317 { 318 return new ArrayList<CorrelatorName>(backends); 319 } 320 321 /** 322 * Returns the velocity of this telescope toward or away from an 323 * object in space at the given point in time. 324 * 325 * @param source 326 * an object in space. The radial velocity of this object is 327 * calculated relative to this telescope. 328 * 329 * @param dateTime 330 * the point in time at which the velocity is calculated. 331 * 332 * @return 333 * the radial velocity of this telescope toward or away from an 334 * object in space. 335 * 336 * @throws CoordinateConversionException 337 * if the position of the source cannot be converted to 338 * an equatorial RA / Dec position. 339 * 340 * @since 2008-07-29 341 */ 342 public LinearVelocity calcVelocityRelativeTo(Source source, 343 Date dateTime) 344 throws CoordinateConversionException 345 { 346 return source.calcVelocityRelativeTo(location, dateTime); 347 } 348 349 /** 350 * Returns a default telescope type. 351 * @return a default telescope type. 352 */ 353 public static TelescopeType getDefault() 354 { 355 return EVLA; 356 } 357 358 @Override 359 public String toString() 360 { 361 return name(); 362 } 363 364 /** 365 * Returns the telescope type represented by {@code text}. 366 * <p> 367 * For details about the transformation, see 368 * {@link EnumerationUtility#enumFromString(Class, String)}.</p> 369 * 370 * @param text a text representation of a telescope type. 371 * 372 * @return the telescope type represented by {@code text}. 373 */ 374 public static TelescopeType fromString(String text) 375 { 376 //Try standard approach first 377 TelescopeType result = 378 EnumerationUtility.getSharedInstance() 379 .enumFromString(TelescopeType.class, text); 380 381 //If no match found, search abbreviations 382 if (result == null) 383 { 384 text = StringUtil.getInstance().normalizeString(text); 385 for (TelescopeType tt : TelescopeType.values()) 386 { 387 if (text.equalsIgnoreCase(tt.getLegacyAbbreviation()) || 388 text.equalsIgnoreCase(tt.getLongName())) 389 { 390 result = tt; 391 break; 392 } 393 } 394 } 395 396 return result; 397 } 398 399 //============================================================================ 400 // RECEIVER SELECTION 401 //============================================================================ 402 403 /** 404 * Sets the class to use as a receiver selector. 405 * 406 * @param fullClassName the fully-qualified name of a class that implements 407 * {@link ReceiverSelector}. 408 */ 409 public void setReceiverSelectorType(String fullClassName) 410 { 411 getDelegate().setReceiverSelectorType(fullClassName); 412 } 413 414 /** 415 * Sets the class to use as a receiver selector. 416 * 417 * @param type a class the implements {@link ReceiverSelector}. If this 418 * value is <i>null</i> a default class will be used. 419 */ 420 public void setReceiverSelectorType(Class<? extends ReceiverSelector> type) 421 { 422 getDelegate().setReceiverSelectorType(type); 423 } 424 425 /** 426 * Does nothing; this telescope will always be its own provider. 427 */ 428 public void setProvider(ReceiverProvider dummy) { } 429 430 public List<ReceiverSelection> selectReceivers(ResourceSpecification specs) 431 { 432 return getDelegate().selectReceivers(specs); 433 } 434 435 public List<ReceiverSelection> selectReceivers(SkyFrequencySpecification specs) 436 { 437 return getDelegate().selectReceivers(specs); 438 } 439 440 public List<ReceiverSelection> selectReceivers(SpectralLineSpecification specs) 441 { 442 return getDelegate().selectReceivers(specs); 443 } 444 445 public List<ReceiverSelection> selectReceivers(PulsarSpecification specs) 446 { 447 return getDelegate().selectReceivers(specs); 448 } 449 450 public List<ReceiverSelection> selectReceivers(ObservingMode mode, 451 ResourceSpecification specs) 452 { 453 return getDelegate().selectReceivers(mode, specs); 454 } 455 456 public ReceiverBand selectBestReceiverFor(FrequencyRange targetRange) 457 { 458 return getDelegate().selectBestReceiverFor(targetRange); 459 } 460 461 //============================================================================ 462 // 463 //============================================================================ 464 /* 465 public static void main(String... args) throws Exception 466 { 467 for (edu.nrao.sss.electronics.DigitalSignal ds : 468 TelescopeType.EVLA.makeElectronics().execute()) 469 System.out.println(ds); 470 } 471 */ 472 /* 473 public static void main(String... args) throws Exception 474 { 475 for (TelescopeType tt : TelescopeType.values()) 476 { 477 System.out.print(tt.name() + " is located at "); 478 EarthPosition pos = tt.getLocation(); 479 System.out.print(pos.getLongitude().toStringDms()); 480 System.out.print(" longitude, "); 481 System.out.print(pos.getLatitude().toStringDms()); 482 System.out.print(" latitude, "); 483 System.out.print(pos.getDistance()); 484 System.out.print(" elevation, in the "); 485 System.out.print(pos.getTimeZone().getDisplayName()); 486 System.out.println(" zone."); 487 System.out.print("Valid Backends: "); 488 for (CorrelatorName c : tt.getBackendTypes()) 489 System.out.print(c+" "); 490 System.out.println(); 491 } 492 } 493 */ 494 } 495 496 /** Helper class for TelescopeType; back-formed from older public class. */ 497 class Telescope implements ReceiverSelector, ReceiverProvider 498 { 499 private static final Logger log = Logger.getLogger(Telescope.class); 500 501 private Set<ReceiverBand> receivers; 502 private List<Set<ReceiverBand>> validReceiverCombinations; 503 504 //This variable is not directly mutable, but will be updated if a client 505 //updates the receiverSelectorClass variable. 506 private ReceiverSelector receiverSelector; 507 508 private Class<? extends ReceiverSelector> receiverSelectorClass; 509 510 private static final Class<? extends ReceiverSelector> DEFAULT_SELECTOR_CLASS = 511 SimpleReceiverSelector.class; 512 513 /** Creates a new instance. */ 514 Telescope() 515 { 516 receivers = new HashSet<ReceiverBand>(); 517 validReceiverCombinations = new ArrayList<Set<ReceiverBand>>(); 518 receiverSelectorClass = DEFAULT_SELECTOR_CLASS; 519 } 520 521 //============================================================================ 522 // RECEIVERS 523 //============================================================================ 524 525 /** Does nothing; this telescope will always be its own provider. */ 526 public void setProvider(ReceiverProvider dummy) { } 527 528 /** 529 * Returns the complete collection of receivers available on antennas 530 * of this telescope. 531 * The returned set represents the union of receivers across all the 532 * antennas of this telescope. An individual antenna might not have all 533 * of the receivers in the returned set. 534 * <p> 535 * Note that the returned set is a <i>copy</i> of the one held internally 536 * by this telescope. This means that any changes made to the returned 537 * set will <i>not</i> be reflected in this object.</p> 538 * 539 * @return the collection of receivers available on antennas of this 540 * telescope. 541 */ 542 public Set<ReceiverBand> getReceivers() 543 { 544 EnumSet<ReceiverBand> result = EnumSet.noneOf(ReceiverBand.class); 545 result.addAll(receivers); 546 return result; 547 } 548 549 //Keep this non-public 550 void addReceiver(ReceiverBand newReceiver) 551 { 552 if (newReceiver != null) 553 receivers.add(newReceiver); 554 } 555 556 /** 557 * Returns the receiver with the given name, or <i>null</i> if this telescope 558 * has no such receiver. 559 * <p> 560 * The name search is both case-insensitive and tolerant of extraneous 561 * whitespace.</p> 562 * 563 * @param receiverName the name of the desired receiver. 564 * 565 * @return the receiver with the given name, or <i>null</i> if this telescope 566 * has no such receiver. 567 */ 568 public ReceiverBand getReceiver(String receiverName) 569 { 570 //Use the enumeration class to convert string to object 571 ReceiverBand result = ReceiverBand.fromString(receiverName); 572 573 if (result != null) 574 { 575 //If we don't hold that receiver, return null 576 if (!receivers.contains(result)) 577 result = null; 578 } 579 580 return result; 581 } 582 583 /** 584 * Returns a list of all the combinations of two or more receivers that 585 * may be used by this telescope simultaneously. 586 * <p> 587 * Note that the returned list, and the sets contained therein, are 588 * copies of those held by this telescope, so changes made to them 589 * will not be reflected in this object.</p> 590 * 591 * @return a list of all the combinations of two or more receivers that 592 * may be used by this telescope simultaneously. 593 */ 594 public List<Set<ReceiverBand>> getValidReceiverCombinations() 595 { 596 ArrayList<Set<ReceiverBand>> result = new ArrayList<Set<ReceiverBand>>(); 597 598 for (Set<ReceiverBand> validCombo : validReceiverCombinations) 599 result.add(new HashSet<ReceiverBand>(validCombo)); 600 601 return result; 602 } 603 604 /** 605 * Returns <i>true</i> if the given set of receivers may be used 606 * simultaneously by one antenna of this telescope. 607 * 608 * @param combination a set of receivers that a client would like to use 609 * simultaneously on one antenna. 610 * 611 * @return <i>true</i> if the given set of receivers may be used 612 * simultaneously by one antenna of this telescope. 613 * If {@code combination} is an empty set, the returned value 614 * will be <i>false</i>. 615 */ 616 public boolean isValidReceiverCombination(Set<ReceiverBand> combination) 617 { 618 for (Set<ReceiverBand> validCombo : validReceiverCombinations) 619 if (combination.equals(validCombo)) 620 return true; 621 622 return false; 623 } 624 625 //Keep this non-public 626 void addCombo(Set<ReceiverBand> newCombination) 627 { 628 if (newCombination != null) 629 validReceiverCombinations.add(newCombination); 630 } 631 632 //============================================================================ 633 // RECEIVER SELECTION 634 //============================================================================ 635 636 /** 637 * Sets the class to use as a receiver selector. 638 * 639 * @param fullClassName the fully-qualified name of a class that implements 640 * {@link ReceiverSelector}. 641 */ 642 @SuppressWarnings("unchecked") 643 public void setReceiverSelectorType(String fullClassName) 644 { 645 Class selectorClass; 646 647 try 648 { 649 selectorClass = Class.forName(fullClassName); 650 } 651 catch (Exception ex) 652 { 653 log.warn("Could not make ReceiverSelector class from " + fullClassName + 654 ". Will use " + DEFAULT_SELECTOR_CLASS.getCanonicalName() + 655 " instead. Exception:", ex); 656 selectorClass = DEFAULT_SELECTOR_CLASS; 657 } 658 659 //An unchecked (not type-safe) conversion 660 setReceiverSelectorType(selectorClass); 661 } 662 663 /** 664 * Sets the class to use as a receiver selector. 665 * 666 * @param type a class the implements {@link ReceiverSelector}. If this 667 * value is <i>null</i> a default class will be used. 668 */ 669 public void setReceiverSelectorType(Class<? extends ReceiverSelector> type) 670 { 671 if (type != receiverSelectorClass) 672 { 673 receiverSelectorClass = (type == null) ? DEFAULT_SELECTOR_CLASS : type; 674 receiverSelector = null; 675 } 676 } 677 678 /** Returns the receiver selector to use with this telescope. */ 679 ReceiverSelector getReceiverSelector() 680 { 681 if (receiverSelector == null) 682 receiverSelector = makeSelector(); 683 684 return receiverSelector; 685 } 686 687 /** Uses receiverSelectorClass to make new instance. */ 688 private ReceiverSelector makeSelector() 689 { 690 ReceiverSelector rs; 691 692 try 693 { 694 rs = receiverSelectorClass.newInstance(); 695 } 696 catch (Exception ex) 697 { 698 log.warn("Could not make new instance for ReceiverSelector class " + 699 receiverSelectorClass.getCanonicalName() + 700 ". Will try class " + DEFAULT_SELECTOR_CLASS.getCanonicalName() + 701 " instead. Exception: ", ex); 702 try 703 { 704 rs = DEFAULT_SELECTOR_CLASS.newInstance(); 705 } 706 catch (Exception ex2) 707 { 708 //Should never get here, but just in case... 709 log.error("Could not make new instance for ReceiverSelector class " + 710 DEFAULT_SELECTOR_CLASS.getCanonicalName() + 711 ". Exception: ", ex2); 712 rs = null; 713 } 714 } 715 716 rs.setProvider(this); 717 718 return rs; 719 } 720 721 public List<ReceiverSelection> selectReceivers(ResourceSpecification specs) 722 { 723 return getReceiverSelector().selectReceivers(specs); 724 } 725 726 public List<ReceiverSelection> selectReceivers(SkyFrequencySpecification specs) 727 { 728 return getReceiverSelector().selectReceivers(specs); 729 } 730 731 public List<ReceiverSelection> selectReceivers(SpectralLineSpecification specs) 732 { 733 return getReceiverSelector().selectReceivers(specs); 734 } 735 736 public List<ReceiverSelection> selectReceivers(PulsarSpecification specs) 737 { 738 return getReceiverSelector().selectReceivers(specs); 739 } 740 741 public List<ReceiverSelection> selectReceivers(ObservingMode mode, 742 ResourceSpecification specs) 743 { 744 return getReceiverSelector().selectReceivers(mode, specs); 745 } 746 747 public ReceiverBand selectBestReceiverFor(FrequencyRange targetRange) 748 { 749 return getReceiverSelector().selectBestReceiverFor(targetRange); 750 } 751 }