001 package edu.nrao.sss.model.resource; 002 003 import java.io.FileNotFoundException; 004 import java.io.Reader; 005 import java.io.Writer; 006 import java.util.HashMap; 007 import java.util.HashSet; 008 import java.util.Map; 009 import java.util.Set; 010 import java.util.SortedMap; 011 import java.util.TreeMap; 012 013 import javax.xml.bind.JAXBException; 014 import javax.xml.bind.annotation.XmlAttribute; 015 import javax.xml.bind.annotation.XmlElement; 016 import javax.xml.bind.annotation.XmlList; 017 import javax.xml.bind.annotation.XmlRootElement; 018 import javax.xml.stream.XMLStreamException; 019 020 import edu.nrao.sss.electronics.SignalTransferSwitch; 021 import edu.nrao.sss.measure.Frequency; 022 import edu.nrao.sss.util.JaxbUtility; 023 024 /** 025 * A generalized representation of the configuration of antenna electronics. 026 * Configuring the electronics system of an antenna usually means 027 * setting switch positions, tuning local oscillators, and setting 028 * the number of bits per sample for the output signals. 029 * <p> 030 * This class is meant to be able to represent the configuration information 031 * for a wide variety of antenna types. Instances of this class may be 032 * obtained from the {@link AntennaElectronics} class, which can return 033 * its current configuration in this form. Clients that will merely query 034 * a configuration need have no special knowledge of the specific 035 * electronics with which they are dealing. Clients that need to 036 * alter a configuration, however, will likely need to know something 037 * about the targeted electronics and about the conventions suggested 038 * by this class.</p> 039 * <h3 style="font-variant:small-caps"> 040 * <u>Suggested Conventions for Users of this Class</u></h3> 041 * <div style="margin-left:1.5em; margin-right:2em; text-align:justify;"> 042 * <p> 043 * In order to remain a fairly generic piece of code, this class makes 044 * heavy use of maps of name-value pairs. The name is always a string 045 * and is meant to be the name of a device, such as a local oscillator 046 * or switch. Concrete implementations of {@code AntennaElectronics} 047 * are expected to name all those LOs and switches that can be configured 048 * by an external user via this class. The value portion of a name-value 049 * pair varies depending on the device involved. Each device type will be 050 * covered below. First, though, we suggest a convention for embedded 051 * devices.</p> 052 * <p> 053 * <i><b>Convention for Naming Devices Held in Other Devices</b></i><br/> 054 * Sometimes a device such as a switch is held in an internal component 055 * of the overall antenna electronics. An internal component might be 056 * an up-converter, a down-converter, or some other device. In order to 057 * distinguish devices held directly by {@code AntennaElectronics} from 058 * those it holds indirectly via its internal components, we suggest 059 * using dot notation. For example, if the electronics wishes to 060 * expose a switch named <i>output</i> in its internal component named 061 * <i>downConverterX</i>, the name it should use when populating 062 * this class is <i>downConverterX.output</i>.</p> 063 * <p> 064 * <i><b>Value Objects for Local Oscillator Settings</b></i><br/> 065 * The {@link #getLoTunings()} method returns a map whose keys are 066 * the names of local oscillators. The values in the maps are the 067 * tuning frequencies for those local oscillators.</p> 068 * <p> 069 * <a name="conventionSwitchSettings"/> 070 * <i><b>Value Objects for Switch Settings</b></i><br/> 071 * The {@link #getSwitchPositions()} method returns a map whose keys 072 * are the names of switches. The values are themselves maps that 073 * give the active input and active output poles for the switch. 074 * The value map will either be empty, have one entry, or have two entries. 075 * It will never be <i>null</i>. 076 * The two valid keys for the value map are <tt>input</tt> and 077 * <tt>output</tt>, and the values of these keys represent the 078 * selection of a given input or output pole as the <i>active</i> 079 * input or output pole of that switch.</p> 080 * <p> 081 * <i><b>Value Objects for Transfer Switch Settings</b></i><br/> 082 * The {@link #getTransferSwitchPositions()} method returns a map whose keys 083 * are the names of {@link SignalTransferSwitch transfer switches}. 084 * The values in the maps are also strings and the convention is to use 085 * one of these two values: <i>clockwise</i> or <i>counterclockwise</i>. 086 * Also by convention, these values are <i>not</i> case sensitive.</p> 087 * <p> 088 * <i><b>Value Objects for Bits per Digital Samples</b></i><br/> 089 * The {@link #getBitsPerSample()} method returns a map whose keys are 090 * the names of digital samplers. The values in the maps are the 091 * number of bits per sample.</p> 092 * </div> 093 * <p> 094 * <b>Version Info:</b> 095 * <table style="margin-left:2em"> 096 * <tr><td>$Revision: 1709 $</td></tr> 097 * <tr><td>$Date: 2008-11-14 11:22:37 -0700 (Fri, 14 Nov 2008) $</td></tr> 098 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 099 * </table></p> 100 * 101 * @author David M. Harland 102 * @since 2008-01-09 103 */ 104 @XmlRootElement 105 public class AntennaElectronicsConfiguration 106 implements Cloneable 107 { 108 @XmlElement private TelescopeType telescope; 109 110 private Set<ReceiverBand> bands; 111 112 private SortedMap<String, Frequency> loTunings; 113 private SortedMap<String, Map<String, String>> switchPositions; 114 private SortedMap<String, String> transferSwitchPositions; 115 private SortedMap<String, Integer> bitsPerSample; 116 117 /** 118 * Creates a new empty configuration for the given telescope. 119 */ 120 public AntennaElectronicsConfiguration(TelescopeType telescope) 121 { 122 this(); 123 this.telescope = telescope; 124 } 125 126 /** 127 * This no-arg constructor is here primarily for use by persistence 128 * mechanisms such as JAXB and Hibernate. 129 */ 130 private AntennaElectronicsConfiguration() 131 { 132 bands = new HashSet<ReceiverBand>(); 133 loTunings = new TreeMap<String, Frequency>(); 134 switchPositions = new TreeMap<String, Map<String, String>>(); 135 transferSwitchPositions = new TreeMap<String, String>(); 136 bitsPerSample = new TreeMap<String, Integer>(); 137 } 138 139 /** 140 * Returns the telescope for which this configuration is intended. 141 * @return the telescope for which this configuration is intended. 142 */ 143 public TelescopeType getTelescope() 144 { 145 return telescope; 146 } 147 148 /** 149 * Returns the receiver bands for this configuration. 150 * <p> 151 * The returned set is the one held internally by this configuration, 152 * so changes made to it will have an effect on this object. 153 * The returned set is guaranteed to be non-<i>null</i>, but it 154 * may be empty. The most common situation is for there to be 155 * one element in the returned set.</p> 156 * 157 * @return the receiver bands for this configuration. 158 */ 159 @XmlList 160 public Set<ReceiverBand> getBands() 161 { 162 return bands; 163 } 164 //For JAXB 165 @SuppressWarnings("unused") 166 private void setBands(Set<ReceiverBand> replacementSet) 167 { 168 if (replacementSet != null) 169 bands = replacementSet; 170 else //null set -- use empty set instead 171 bands.clear(); 172 } 173 174 /** 175 * Returns a map of local oscillator tunings. 176 * The keys are the names of local oscillators and the values 177 * are their tunings. 178 * <p> 179 * The returned map is the one held internally by this configuration, 180 * so changes made to it will have an effect on this object. 181 * The returned map is guaranteed to be non-<i>null</i>, but it 182 * may be empty.</p> 183 * 184 * @return a map of local oscillator tunings. 185 */ 186 public SortedMap<String, Frequency> getLoTunings() 187 { 188 return loTunings; 189 } 190 191 /** 192 * Returns a map of switch positions. 193 * The keys are the names of switches and the values are themselves maps. 194 * The value map will either be empty, hold one entry, or hold two entries. 195 * It will never be <i>null</i>. 196 * The two valid keys for the value map are <tt>input</tt> and 197 * <tt>output</tt>, and the values of these keys represent the 198 * selection of a given input or output pole as the <i>active</i> 199 * input or output pole of that switch. 200 * <p> 201 * The returned map is the one held internally by this configuration, 202 * so changes made to it will have an effect on this object. 203 * The returned map is guaranteed to be non-<i>null</i>, but it 204 * may be empty.</p> 205 * 206 * @return a map of switch positions. 207 */ 208 public SortedMap<String, Map<String, String>> getSwitchPositions() 209 { 210 return switchPositions; 211 } 212 213 /** 214 * Returns a map of {@link SignalTransferSwitch transfer switch} positions. 215 * The keys are the names of switches and the values 216 * are their settings. A setting may be either the string 217 * <i>clockwise</i> or the string <i>counterclockwise</i>. 218 * Implementations of {@link AntennaElectronics} are to treat these values 219 * as case insensitive. 220 * <p> 221 * The returned map is the one held internally by this configuration, 222 * so changes made to it will have an effect on this object. 223 * The returned map is guaranteed to be non-<i>null</i>, but it 224 * may be empty.</p> 225 * 226 * @return a map of transfer switch positions. 227 */ 228 public SortedMap<String, String> getTransferSwitchPositions() 229 { 230 return transferSwitchPositions; 231 } 232 233 /** 234 * Returns a map of digital sampler settings. 235 * The keys are the names of digital samplers and the values 236 * are the number of bits per sample 237 * <p> 238 * The returned map is the one held internally by this configuration, 239 * so changes made to it will have an effect on this object. 240 * The returned map is guaranteed to be non-<i>null</i>, but it 241 * may be empty.</p> 242 * 243 * @return a map of digital sampler settings 244 */ 245 public SortedMap<String, Integer> getBitsPerSample() 246 { 247 return bitsPerSample; 248 } 249 250 //============================================================================ 251 // XML 252 //============================================================================ 253 254 /** 255 * Returns an XML representation of this configuration. 256 * @return an XML representation of this configuration. 257 * @throws JAXBException if anything goes wrong during the conversion to XML. 258 * @see #writeAsXmlTo(Writer) 259 */ 260 public String toXml() throws JAXBException 261 { 262 return JaxbUtility.getSharedInstance().objectToXmlString(this); 263 } 264 265 /** 266 * Writes an XML representation of this configuration to {@code writer}. 267 * @param writer the device to which XML is written. 268 * @throws JAXBException if anything goes wrong during the conversion to XML. 269 */ 270 public void writeAsXmlTo(Writer writer) throws JAXBException 271 { 272 JaxbUtility.getSharedInstance().writeObjectAsXmlTo(writer, this, null); 273 } 274 275 /** 276 * Creates a new configuration from the XML data in the given file. 277 * 278 * @param xmlFile the name of an XML file. This method will attempt to locate 279 * the file by using {@link Class#getResource(String)}. 280 * 281 * @return a new configuration from the XML data in the given file. 282 * 283 * @throws FileNotFoundException if the XML file cannot be found. 284 * 285 * @throws JAXBException if the schema file used (if any) is malformed, if 286 * the XML file cannot be read, or if the XML file is not 287 * schema-valid. 288 * 289 * @throws XMLStreamException if there is a problem opening the XML file, 290 * if the XML is not well-formed, or for some other 291 * "unexpected processing conditions". 292 */ 293 public static AntennaElectronicsConfiguration fromXml(String xmlFile) 294 throws JAXBException, XMLStreamException, FileNotFoundException 295 { 296 return 297 JaxbUtility.getSharedInstance() 298 .xmlFileToObject(xmlFile, 299 AntennaElectronicsConfiguration.class); 300 } 301 302 /** 303 * Creates a new configuration based on the XML data read from 304 * {@code reader}. 305 * 306 * @param reader the source of the XML data. 307 * If this value is <i>null</i>, <i>null</i> is returned. 308 * 309 * @return a new configuration based on the XML data read from 310 * {@code reader}. 311 * 312 * @throws XMLStreamException if the XML is not well-formed, 313 * or for some other "unexpected processing conditions". 314 * 315 * @throws JAXBException if anything else goes wrong during the 316 * transformation. 317 */ 318 public static AntennaElectronicsConfiguration fromXml(Reader reader) 319 throws JAXBException, XMLStreamException 320 { 321 return 322 JaxbUtility.getSharedInstance() 323 .readObjectAsXmlFrom(reader, 324 AntennaElectronicsConfiguration.class, 325 null); 326 } 327 328 //To deal with maps in JAXB one has to jump through a few hoops. 329 //The code below does just that. The basic strategy is to present 330 //the maps to JAXB as an array of objects. These objects contain 331 //the keys and values of the map entries. That is, if we have 332 //a variable of type Map<K,V>, we create a new object, X, that 333 //has properties of type K and V. We then make methods that 334 //get and set X[]. 335 336 //---------------------------------------------------------------------------- 337 // Transfer switches 338 //---------------------------------------------------------------------------- 339 340 @XmlElement(name="transferSwitch") 341 @SuppressWarnings("unused") 342 private TransferSwitch[] getXmlTransferSwitch() 343 { 344 TransferSwitch[] xmlEntries = new TransferSwitch[transferSwitchPositions.size()]; 345 346 int i=0; 347 for (String switchName : transferSwitchPositions.keySet()) 348 { 349 xmlEntries[i++] = 350 new TransferSwitch(switchName, 351 transferSwitchPositions.get(switchName)); 352 } 353 354 return xmlEntries; 355 } 356 357 @SuppressWarnings("unused") 358 private void setXmlTransferSwitch(TransferSwitch[] xmlEntries) 359 { 360 //TODO: Are we guaranteed by JAXB to get all the transferSwitch elements at once? 361 // If not, clearing the list is not going to work. 362 // We could create a wrapper element called "tranferSwitches" that 363 // has a sequence of transferSwitch elements. That should guarantee 364 // all elements coming at once. 365 transferSwitchPositions.clear(); 366 367 for (TransferSwitch ts : xmlEntries) 368 { 369 transferSwitchPositions.put(ts.name, ts.orientation); 370 } 371 } 372 373 private static class TransferSwitch 374 { 375 @XmlAttribute String orientation; 376 @XmlAttribute String name; 377 378 TransferSwitch() { } 379 TransferSwitch(String n, String v) { name=n; orientation=v; } 380 } 381 382 //---------------------------------------------------------------------------- 383 // Plain switches 384 //---------------------------------------------------------------------------- 385 386 @XmlElement(name="switch") 387 @SuppressWarnings("unused") 388 private NameStringValue[] getXmlSwitch() 389 { 390 NameStringValue[] xmlEntries = new NameStringValue[switchPositions.size()]; 391 392 int i=0; 393 for (String switchName : switchPositions.keySet()) 394 { 395 xmlEntries[i++] = 396 new NameStringValue(switchName, 397 switchPositions.get(switchName).get("input"), 398 switchPositions.get(switchName).get("output")); 399 } 400 401 return xmlEntries; 402 } 403 404 @SuppressWarnings("unused") 405 private void setXmlSwitch(NameStringValue[] xmlEntries) 406 { 407 //TODO: See TODO in setXmlTransferSwitch. 408 switchPositions.clear(); 409 410 for (NameStringValue s : xmlEntries) 411 { 412 Map<String, String> value = new HashMap<String, String>(); 413 414 if (s.inputPole != null) 415 value.put("input", s.inputPole); 416 417 if (s.outputPole != null) 418 value.put("output", s.outputPole); 419 420 switchPositions.put(s.name, value); 421 } 422 } 423 424 private static class NameStringValue 425 { 426 @XmlAttribute String outputPole; 427 @XmlAttribute String inputPole; 428 @XmlAttribute String name; 429 430 NameStringValue() { } 431 NameStringValue(String n, String in, String out) 432 { 433 name=n; inputPole=in; outputPole=out; 434 } 435 } 436 437 //---------------------------------------------------------------------------- 438 // Digital samplers 439 //---------------------------------------------------------------------------- 440 441 @XmlElement(name="sampler") 442 @SuppressWarnings("unused") 443 private Sampler[] getXmlSampler() 444 { 445 Sampler[] xmlEntries = new Sampler[bitsPerSample.size()]; 446 447 int i=0; 448 for (String samplerName : bitsPerSample.keySet()) 449 { 450 xmlEntries[i++] = new Sampler(samplerName,bitsPerSample.get(samplerName)); 451 } 452 453 return xmlEntries; 454 } 455 456 @SuppressWarnings("unused") 457 private void setXmlSampler(Sampler[] xmlEntries) 458 { 459 //TODO: See TODO in setXmlTransferSwitch. 460 bitsPerSample.clear(); 461 462 for (Sampler s : xmlEntries) 463 { 464 bitsPerSample.put(s.name, s.bitsPerSample); 465 } 466 } 467 468 //@XmlType(name="sampler") 469 private static class Sampler 470 { 471 @XmlAttribute Integer bitsPerSample; 472 @XmlAttribute String name; 473 474 Sampler() { } 475 Sampler(String n, Integer b) { name=n; bitsPerSample=b; } 476 } 477 478 //---------------------------------------------------------------------------- 479 // Local oscillators 480 //---------------------------------------------------------------------------- 481 482 @XmlElement(name="oscillator") 483 @SuppressWarnings("unused") 484 private Oscillator[] getXmlOscillator() 485 { 486 Oscillator[] xmlEntries = new Oscillator[loTunings.size()]; 487 488 int i=0; 489 for (String loName : loTunings.keySet()) 490 { 491 xmlEntries[i++] = new Oscillator(loName,loTunings.get(loName).toString()); 492 } 493 494 return xmlEntries; 495 } 496 497 @SuppressWarnings("unused") 498 private void setXmlOscillator(Oscillator[] xmlEntries) 499 { 500 //TODO: See TODO in setXmlTransferSwitch. 501 loTunings.clear(); 502 503 for (Oscillator lo : xmlEntries) 504 { 505 loTunings.put(lo.name, Frequency.parse(lo.tuning)); 506 } 507 } 508 509 private static class Oscillator 510 { 511 @XmlAttribute String tuning; 512 @XmlAttribute String name; 513 514 Oscillator() { } 515 Oscillator(String n, String t) { name=n; tuning=t; } 516 } 517 518 //============================================================================ 519 // 520 //============================================================================ 521 522 /** 523 * Returns a copy of this configuration. 524 * <p> 525 * If anything goes wrong during the cloning procedure, 526 * a {@code RuntimeException} will be thrown.</p> 527 */ 528 @Override 529 public AntennaElectronicsConfiguration clone() 530 { 531 AntennaElectronicsConfiguration clone = null; 532 533 try 534 { 535 //This line takes care of the primitive & immutable fields properly 536 clone = (AntennaElectronicsConfiguration)super.clone(); 537 538 clone.bands = new HashSet<ReceiverBand>(this.bands); 539 clone.loTunings = new TreeMap<String, Frequency>(this.loTunings); 540 clone.switchPositions = new TreeMap<String, Map<String,String>>(this.switchPositions); 541 clone.transferSwitchPositions = new TreeMap<String, String>(this.transferSwitchPositions); 542 clone.bitsPerSample = new TreeMap<String, Integer>(this.bitsPerSample); 543 544 } 545 catch (Exception ex) 546 { 547 throw new RuntimeException(ex); 548 } 549 550 return clone; 551 } 552 553 /** Returns <i>true</i> if {@code o} is equal to this configuration. */ 554 @Override 555 public boolean equals(Object o) 556 { 557 //Quick exit if o is this 558 if (o == this) 559 return true; 560 561 //Quick exit if o is null 562 if (o == null) 563 return false; 564 565 //Quick exit if classes are different 566 if (!o.getClass().equals(this.getClass())) 567 return false; 568 569 //A safe cast if we got this far 570 AntennaElectronicsConfiguration other = (AntennaElectronicsConfiguration)o; 571 572 return 573 other.telescope.equals( this.telescope ) && 574 other.bands.equals( this.bands ) && 575 other.loTunings.equals( this.loTunings ) && 576 other.switchPositions.equals( this.switchPositions ) && 577 other.transferSwitchPositions.equals(this.transferSwitchPositions) && 578 other.bitsPerSample.equals( this.bitsPerSample ); 579 580 //Note the comparison of the bands Set is OK because 581 //the elements are immutable. 582 } 583 584 /** Returns a hash code value for this configuration. */ 585 @Override 586 public int hashCode() 587 { 588 //Taken from the Effective Java book by Joshua Bloch. 589 //The constants 17 & 37 are arbitrary & carry no meaning. 590 int result = 17; 591 592 //You MUST keep this method in sync w/ the equals method 593 594 result = 37 * result + telescope.hashCode(); 595 result = 37 * result + bands.hashCode(); 596 result = 37 * result + loTunings.hashCode(); 597 result = 37 * result + switchPositions.hashCode(); 598 result = 37 * result + transferSwitchPositions.hashCode(); 599 result = 37 * result + bitsPerSample.hashCode(); 600 601 return result; 602 } 603 604 //============================================================================ 605 // 606 //============================================================================ 607 /* 608 //This is here for quick & dirty testing 609 public static void main(String[] args) 610 { 611 edu.nrao.sss.model.resource.evla.EvlaAntennaElectronics evla = 612 new edu.nrao.sss.model.resource.evla.EvlaAntennaElectronics(); 613 614 evla.configureFor(ReceiverBand.EVLA_X); 615 616 AntennaElectronicsConfiguration config = evla.getConfiguration(); 617 618 try 619 { 620 config.writeAsXmlTo(new java.io.PrintWriter(System.out)); 621 } 622 catch (JAXBException ex) 623 { 624 System.out.println("Trouble w/ config.toXml. Msg:"); 625 System.out.println(ex.getMessage()); 626 ex.printStackTrace(); 627 628 System.out.println("Attempting to write XML w/out schema verification:"); 629 JaxbUtility.getSharedInstance().setLookForDefaultSchema(false); 630 try 631 { 632 config.writeAsXmlTo(new java.io.PrintWriter(System.out)); 633 } 634 catch (JAXBException ex2) 635 { 636 System.out.println("Still had trouble w/ config.toXml. Msg:"); 637 System.out.println(ex.getMessage()); 638 ex.printStackTrace(); 639 } 640 } 641 } 642 */ 643 }