001 package edu.nrao.sss.model.resource.vla; 002 003 import java.awt.event.ActionEvent; 004 import java.math.BigDecimal; 005 import java.math.MathContext; 006 import java.util.ArrayList; 007 import java.util.Collection; 008 import java.util.Date; 009 import java.util.HashSet; 010 import java.util.List; 011 import java.util.Set; 012 013 import javax.xml.bind.annotation.XmlAccessType; 014 import javax.xml.bind.annotation.XmlAccessorType; 015 import javax.xml.bind.annotation.XmlList; 016 import javax.xml.bind.annotation.XmlRootElement; 017 018 import static edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ; 019 import static edu.nrao.sss.model.resource.vla.VlaConfigurationValidator.MIN_AC_LOW_FREQ; 020 021 import edu.nrao.sss.astronomy.CoordinateConversionException; 022 import edu.nrao.sss.astronomy.DopplerTracker; 023 import edu.nrao.sss.electronics.DigitalSignal; 024 import edu.nrao.sss.measure.Frequency; 025 import edu.nrao.sss.measure.FrequencyRange; 026 import edu.nrao.sss.measure.FrequencyUnits; 027 import edu.nrao.sss.model.resource.AntennaElectronics; 028 import edu.nrao.sss.model.resource.CorrelatorConfiguration; 029 import edu.nrao.sss.model.resource.CorrelatorName; 030 import edu.nrao.sss.model.resource.ResourceSpecification; 031 import edu.nrao.sss.model.resource.ReceiverBand; 032 import edu.nrao.sss.model.resource.TelescopeType; 033 import edu.nrao.sss.model.source.Source; 034 import edu.nrao.sss.util.StringUtil; 035 036 /** 037 * The configuration of the VLA correlator. 038 * <p> 039 * This class was created without a lot of care. It is meant to be used 040 * for a short period of time, primarily to support Ka observing on the 041 * "interim" system.</p> 042 * <p> 043 * <b>UPDATE</b>: the code in here has become progressively worse. 044 * It is a dangerous class to use unless you have insider knowledge. 045 * This class is planned for destruction in 2010.</p> 046 * <p> 047 * <b>Another UPDATE</b>: Yup, this is the worst crap i've written in 048 * a decade. Recently tried to effect a simple change in behavior, and 049 * found what appeared to be the right place to change. Took a few attempts 050 * before doing it right. This class cannot be killed soon enough.</p> 051 * <p> 052 * <b>Version Info:</b> 053 * <table style="margin-left:2em"> 054 * <tr><td>$Revision: 2271 $</td></tr> 055 * <tr><td>$Date: 2009-04-28 12:02:17 -0600 (Tue, 28 Apr 2009) $</td></tr> 056 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 057 * </table></p> 058 * 059 * @author David M. Harland 060 * @since 2008-03-14 061 */ 062 @XmlRootElement 063 @XmlAccessorType(XmlAccessType.FIELD) 064 public class VlaConfiguration 065 extends CorrelatorConfiguration 066 { 067 private static final StringUtil sUtil = StringUtil.getInstance(); 068 069 //============================================================================ 070 // NEWER MODEL 071 //============================================================================ 072 073 public VlaConfiguration(AntennaElectronics signalSrc) 074 { 075 super(signalSrc); 076 initOldStyleVars(); 077 } 078 079 //This is here only for persistence mechanisms 080 @SuppressWarnings("unused") 081 private VlaConfiguration() 082 { 083 initOldStyleVars(); 084 } 085 086 public CorrelatorName getName() { return CorrelatorName.VLA; } 087 088 /* (non-Javadoc) 089 * @see CorrelatorConfiguration#makeBasebandFrom(DigitalSignal) 090 */ 091 protected VlaBasebandPair makeBasebandFrom(DigitalSignal ds) 092 { 093 return null; 094 } 095 096 /* (non-Javadoc) 097 * @see CorrelatorConfiguration#makeBasebandFrom(DigitalSignal) 098 */ 099 protected VlaBasebandPair makeBasebandFrom(DigitalSignal ds1, 100 DigitalSignal ds2) 101 { 102 VlaBasebandPair newBbp = 103 (ds1 == null || ds2 == null) ? null : new VlaBasebandPair(ds1, ds2); 104 105 if (newBbp != null) 106 newBbp.setContainer(this); 107 108 return newBbp; 109 } 110 111 //This is called when the antenna electronics' execute method is called. 112 public void actionPerformed(ActionEvent event) 113 { 114 //TODO code me 115 } 116 117 /** 118 * <i>This method is not yet supported.</i> 119 */ 120 public void configureFrom(ResourceSpecification scienceView) 121 { 122 throw new UnsupportedOperationException("not yet programmed"); //TODO code me 123 } 124 125 //============================================================================ 126 // COMPATIBILITY WITH OLDER MODEL 127 //============================================================================ 128 129 private Frequency centralFrequencyAC; 130 private Frequency centralFrequencyBD; 131 132 private boolean centralFrequencyACIsSkyFrequency; 133 private boolean centralFrequencyBDIsSkyFrequency; 134 135 private BigDecimal integrationTimeInSeconds; 136 137 private CorrelatorMode correlatorMode; 138 139 private BandwidthCode bwCodeIfA; 140 private BandwidthCode bwCodeIfB; 141 private BandwidthCode bwCodeIfC; 142 private BandwidthCode bwCodeIfD; 143 144 @XmlList 145 private Set<ProcessingType> spectralProcessing = new HashSet<ProcessingType>(); 146 147 private void initOldStyleVars() 148 { 149 AntennaElectronics src = getSignalSource(); 150 if (src != null) 151 { 152 Set<ReceiverBand> bands = src.getActiveReceivers(); 153 154 if (!bands.isEmpty()) 155 { 156 Frequency center = bands.iterator().next().getNominalRange().getCenterFrequency(); 157 centralFrequencyAC = center; 158 } 159 } 160 161 if (centralFrequencyAC == null) 162 centralFrequencyAC = new Frequency(); 163 164 centralFrequencyBD = centralFrequencyAC.clone(); 165 166 centralFrequencyACIsSkyFrequency = true; 167 centralFrequencyBDIsSkyFrequency = true; 168 169 correlatorMode = CorrelatorMode.CONTINUUM; 170 171 bwCodeIfA = BandwidthCode.ZERO; 172 bwCodeIfB = BandwidthCode.ZERO; 173 bwCodeIfC = BandwidthCode.ZERO; 174 bwCodeIfD = BandwidthCode.ZERO; 175 176 spectralProcessing = new HashSet<ProcessingType>(); 177 178 // Using the set method to set the default to 3.3s because the set method 179 // will automatically choose the closest valid integration time and I don't 180 // know the exact value out to the n'th decimal point of the 3.3s setting. 181 setIntegrationTime(new BigDecimal("3.3")); 182 } 183 184 /** 185 * A convenience method for setting all the old-style parameters at once. 186 * See the individual <tt>set<i>Xxx</i></tt> methods for details. NOTE: This 187 * assumes your center frequencies are SKY frequencies! 188 */ 189 public void setAll(Frequency centFreqAC, 190 Frequency centFreqBD, 191 BandwidthCode bwAC, 192 BandwidthCode bwBD, 193 CorrelatorMode corrMode, 194 Set<ProcessingType> processingOptions, 195 BigDecimal integrationSeconds) 196 { 197 setCentralFrequency(IFPair.AC, centFreqAC); 198 setCentralFrequency(IFPair.BD, centFreqBD); 199 setBandwidthCodes(bwAC, bwBD); 200 setCorrelatorMode(corrMode); 201 setSpectralProcessing(processingOptions); 202 setIntegrationTime(integrationSeconds); 203 } 204 205 //---------------------------------------------------------------------------- 206 // Frequency 207 //---------------------------------------------------------------------------- 208 209 /** 210 * Sets one of the two central frequencies that can be held by this 211 * configuration. TODO: This method assumes that there is only 1 receiver 212 * band selected in the AntennaElectronics' active receiver set. 213 * 214 * @param ifPair 215 * the IF pair for which the central frequency is being set. 216 * @param central 217 * the central frequency. To use only one central frequency, 218 * set the other to a value of zero. 219 * @param isSkyFrequency 220 * True if central is a sky frequency, false if it is a 221 * rest frequency. 222 * @throws IllegalArgumentException 223 * if either parameter is <i>null</i> or if {@code central} is not within 224 * the range of the selected receiver. 225 */ 226 public void setCentralFrequency(IFPair ifPair, Frequency central, boolean isSkyFrequency) 227 { 228 if (central == null) 229 throw new IllegalArgumentException("Cannot set NULL central frequency."); 230 231 if (ifPair == null) 232 throw new IllegalArgumentException("Cannot use NULL ifPair to set central frequency."); 233 234 if (ifPair.equals(IFPair.AC)) 235 { 236 centralFrequencyAC = central.clone(); 237 centralFrequencyACIsSkyFrequency = isSkyFrequency; 238 } 239 240 else if (ifPair.equals(IFPair.BD)) 241 { 242 centralFrequencyBD = central.clone(); 243 centralFrequencyBDIsSkyFrequency = isSkyFrequency; 244 } 245 246 else 247 throw new RuntimeException("PROGRAMMER ERROR: did not expect ifPair = " + ifPair); 248 } 249 250 /** 251 * calls setCentralFrequency(ifPair, central, true); 252 */ 253 public void setCentralFrequency(IFPair ifPair, Frequency central) 254 { 255 setCentralFrequency(ifPair, central, true); 256 } 257 258 /** 259 * Returns true if the central frequency for the specified IFPair was 260 * specified as a Sky Frequency, false if it was specified as a Rest 261 * Frequency. 262 */ 263 public boolean isSkyFrequency(IFPair ifPair) 264 { 265 if (ifPair.equals(IFPair.AC)) return centralFrequencyACIsSkyFrequency; 266 else if (ifPair.equals(IFPair.BD)) return centralFrequencyBDIsSkyFrequency; 267 else throw new RuntimeException("PROGRAMMER ERROR: did not expect ifPair = " + ifPair); 268 } 269 270 /** 271 * Returns a copy of one of the two central frequencies 272 * that can be held by this configuration. 273 * The returned frequency will never be <i>null</i> but could be zero. 274 * <p> 275 * This central frequency may be either a rest or sky frequency; 276 * use {@link #isSkyFrequency(IFPair)} to determine which it is. 277 * See also {@link #getCentralSkyFrequency(IFPair, Source, Date)}.</p> 278 * 279 * @param ifPair 280 * the IF pair for which the central frequency is being sought. 281 * @return 282 * the central frequency for the given IF pair. 283 * @throws IllegalArgumentException 284 * if {@code ifPair} is <i>null</i>. 285 */ 286 public Frequency getCentralFrequency(IFPair ifPair) 287 { 288 if (ifPair == null) 289 throw new IllegalArgumentException("Cannot use NULL ifPair to fetch central frequency."); 290 291 if (ifPair.equals(IFPair.AC)) return centralFrequencyAC.clone(); 292 else if (ifPair.equals(IFPair.BD)) return centralFrequencyBD.clone(); 293 else 294 throw new RuntimeException("PROGRAMMER ERROR: did not expect ifPair = " + ifPair); 295 } 296 297 /** 298 * @deprecated do not use; see {@link #getCentralSkyFrequency(IFPair, DopplerTracker, Date)} 299 */ 300 public Frequency getCentralSkyFrequency(IFPair ifPair, 301 Source source, Date dateTime) 302 throws CoordinateConversionException 303 { 304 Frequency centerFreq = getCentralFrequency(ifPair); 305 306 if (!isSkyFrequency(ifPair)) 307 { 308 centerFreq = 309 source.calcShiftedFrequency(centerFreq, 310 TelescopeType.EVLA.getLocation(), dateTime); 311 } 312 313 return centerFreq; 314 } 315 316 /** 317 * Returns a central sky frequency for the given IF pair. 318 * The {@code source} and {@code dateTime} are used only if the stored 319 * frequency is a rest frequency. 320 * 321 * @param ifPair 322 * the IF pair for which the central sky frequency is being sought. 323 * 324 * @param dt 325 * a Doppler tracker that will provide information on source position, 326 * source velocity, and earth velocity. 327 * If this value is <i>null</i> Doppler tracking will <i>not</i> 328 * be performed. 329 * 330 * @param dateTime 331 * the date and time of the observation. 332 * This parameter is used if this configuration holds a rest frequency. 333 * The velocity of the source relative to the EVLA is calculated using 334 * this point in time. 335 * If this value is <i>null</i> the current system time will be used. 336 * 337 * @return 338 * the central sky frequency for the given IF pair. 339 * 340 * @throws IllegalArgumentException 341 * if {@code ifPair} is <i>null</i>. 342 * 343 * @throws CoordinateConversionException 344 * if the position of the source cannot be converted to 345 * an equatorial RA / Dec position. 346 * 347 * @since 2008-07-29 348 */ 349 public Frequency getCentralSkyFrequency(IFPair ifPair, 350 DopplerTracker dt, Date dateTime) 351 throws CoordinateConversionException 352 { 353 Frequency centerFreq = getCentralFrequency(ifPair); 354 355 if (dt != null && !isSkyFrequency(ifPair)) 356 { 357 if (dateTime == null) 358 dateTime = new Date(); 359 360 centerFreq = dt.calculateShiftedFrequency(centerFreq, dateTime); 361 } 362 363 return centerFreq; 364 } 365 366 /** 367 * Returns the bandwidth used for this configuration for the given IF pair. 368 * @param ifPair 369 * the IF pair (A/C or B/D) for which the bandwidth is requested. 370 * @return 371 * the bandwidth used for this configuration for the given IF pair. 372 */ 373 public Frequency getBandwidth(IFPair ifPair) 374 { 375 BandwidthCode bwCode = getBandwidthCode(ifPair.getIfCodes().get(0)); 376 377 IFMode ifMode = correlatorMode.getCorrespondingIFMode(); 378 379 return (ifMode == null) ? bwCode.getBandwidthForContinuum() 380 : bwCode.getBandwidthForSpectralLine(ifMode, spectralProcessing); 381 } 382 383 public Frequency getBandwidthNominal(IFPair ifPair) 384 { 385 BandwidthCode bwCode = getBandwidthCode(ifPair.getIfCodes().get(0)); 386 387 return bwCode.getBandwidthNominal(); 388 } 389 390 /** 391 * Returns the sky frequency range for this configuration for the given 392 * IF pair. 393 * @param ifPair 394 * the IF pair (A/C or B/D) for which the bandwidth is requested. 395 * @return 396 * the frequency range used for this configuration for the given IF pair. 397 */ 398 public FrequencyRange getFrequencyRange(IFPair ifPair) 399 { 400 Frequency c = getCentralFrequency(ifPair).normalize(); 401 Frequency w = getBandwidth(ifPair); 402 403 return new FrequencyRange().setCenterAndWidth(c, w); 404 } 405 406 //List of receiver/IF-pairs that have SSLO = centerFreq PLUS bw/2 407 //DANGER: 408 // This list ASSUMES: 409 // 1. 8-bit sampling 410 // 2. Use of LO-2 path for B/D of K, Ka, & Q 411 // 3. Use of LO-1 and LO-2 paths for Ku 412 private static final List<String> RCVR_IF_PAIR_ADD = new ArrayList<String>(); 413 static 414 { 415 RCVR_IF_PAIR_ADD.add(ReceiverBand.EVLA_X.name() + "." + IFPair.AC.name()); 416 RCVR_IF_PAIR_ADD.add(ReceiverBand.EVLA_X.name() + "." + IFPair.BD.name()); 417 RCVR_IF_PAIR_ADD.add(ReceiverBand.EVLA_K.name() + "." + IFPair.BD.name()); 418 RCVR_IF_PAIR_ADD.add(ReceiverBand.EVLA_Ka.name() + "." + IFPair.BD.name()); 419 RCVR_IF_PAIR_ADD.add(ReceiverBand.EVLA_Q.name() + "." + IFPair.BD.name()); 420 } 421 422 /** 423 * Returns an array of text that can be used in the LoIfSetup constructor. 424 * 425 * @param acTracker 426 * a doppler tracker for the A/C IF pair. 427 * 428 * @param bdTracker 429 * a doppler tracker for the B/D IF pair. 430 * 431 * @param when 432 * the point in time to use for calculating doppler shifts. 433 * 434 * @return 435 * the parameters to use in the LoIfConstructor plus one other value that 436 * tells clients whether or not this method swapped the A/C and B/D pairs. 437 * The returned array has a length of five, and the parameters are in this 438 * order: 439 * <ol start="0"> 440 * <li>Frequency name of the band, e.g. "33GHz".</li> 441 * <li>SSLO A/C in MHz.</li> 442 * <li>SSLO B/D in MHz.</li> 443 * <li>The T303 path variable. Value = "0", "1", or "2".</li> 444 * <li>Value = "swapped" or "not swapped".</li> 445 * </ol> 446 * 447 * @throws CoordinateConversionException 448 * see {@link #getCentralSkyFrequency(IFPair, DopplerTracker, Date)}. 449 */ 450 public String[] calcLoIfSetupParams(DopplerTracker acTracker, DopplerTracker bdTracker, 451 Date when) 452 throws CoordinateConversionException 453 { 454 boolean useAC = correlatorMode.uses(IFPair.AC) && !correlatorMode.equals(CorrelatorMode.PB); 455 boolean useBD = correlatorMode.uses(IFPair.BD) && !correlatorMode.equals(CorrelatorMode.PA); 456 457 //Cover all bases; will occur only if someone screws up CorrelatorMode class 458 if (!useAC && !useBD) 459 { 460 throw new RuntimeException("PROGRAMMER ERROR: Neither A/C nor B/D being used. " + 461 "Corr mode = " + correlatorMode); 462 } 463 464 ReceiverBand band = signalSource.getActiveReceivers().first(); 465 466 //Determine center sky frequencies, using doppler if approp, and half bandwidths 467 Frequency skyFreqCtrAC = null, skyFreqCtrBD = null; 468 Frequency halfBwAC = null, halfBwBD = null; 469 470 if (useAC) //using A/C and possibly B/D 471 { 472 skyFreqCtrAC = getCentralSkyFrequency(IFPair.AC, acTracker, when); 473 halfBwAC = getBandwidthNominal(IFPair.AC).divideBy("2.0"); 474 475 skyFreqCtrBD = useBD ? getCentralSkyFrequency(IFPair.BD, bdTracker, when) : skyFreqCtrAC.clone(); 476 halfBwBD = useBD ? getBandwidthNominal(IFPair.BD).divideBy("2.0") : halfBwAC.clone(); 477 } 478 else if (useBD) //using B/D, not using AC 479 { 480 skyFreqCtrBD = getCentralSkyFrequency(IFPair.BD, bdTracker, when); 481 halfBwBD = getBandwidthNominal(IFPair.BD).divideBy("2.0"); 482 483 skyFreqCtrAC = skyFreqCtrBD.clone(); 484 halfBwAC = halfBwBD.clone(); 485 } 486 //case of !A/C & !B/D already covered w/ runtime exception, above 487 488 Frequency skyFreqLowAC = skyFreqCtrAC.clone().subtract(halfBwAC); 489 Frequency skyFreqLowBD = skyFreqCtrBD.clone().subtract(halfBwBD); 490 491 //Introduced for EVL-901, 2009-Apr-27 492 if (useBD && !useAC && band.equals(ReceiverBand.EVLA_Ka)) 493 { 494 skyFreqLowAC = new Frequency("35.0", FrequencyUnits.GIGAHERTZ); 495 skyFreqCtrAC = skyFreqLowAC.clone().add(halfBwAC); 496 } 497 498 //Determine SSLOs and T303 param. 499 //NOTE: code below ASSUMES 8-bit samplers and VLA correlator 500 Frequency ssloAC = null, ssloBD = null; 501 int t303Path = -1; 502 503 boolean swappedIFs = false; 504 505 switch (band) 506 { 507 case EVLA_X: 508 t303Path = 0; 509 ssloAC = skyFreqCtrAC.add(halfBwAC); 510 ssloBD = skyFreqCtrBD.add(halfBwBD); 511 break; 512 513 case EVLA_Ku: 514 t303Path = 2; 515 ssloAC = skyFreqCtrAC.subtract(halfBwAC); 516 ssloBD = skyFreqCtrBD.subtract(halfBwBD); 517 break; 518 519 case EVLA_Ka: 520 //A/C freq cannot be < 32GHz 521 if (skyFreqLowAC.compareTo(MIN_AC_LOW_FREQ) < 0) 522 { 523 //If both IFs are < min, set AC to its minimum allowed value 524 if (skyFreqLowBD.compareTo(MIN_AC_LOW_FREQ) < 0) 525 { 526 skyFreqCtrAC.set(MIN_AC_LOW_FREQ.getValue(), MIN_AC_LOW_FREQ.getUnits()); 527 skyFreqCtrAC.add(halfBwAC); 528 } 529 else //only A/C is below min; B/D is at or over 530 { 531 //If not using conversion path, swap. 532 //(If using conversion path, logic in cases K & Q below will swap anyway.) 533 //An example is a request for A/C=31GHz, B/D=33GHz. 534 if (!useConversionPath(skyFreqLowAC, skyFreqLowBD, band)) 535 { 536 Frequency swap = skyFreqCtrAC; 537 skyFreqCtrAC = skyFreqCtrBD; 538 skyFreqCtrBD = swap; 539 540 swap = halfBwAC; 541 halfBwAC = halfBwBD; 542 halfBwBD = swap; 543 544 swappedIFs = true; 545 } 546 } 547 } 548 //INTENTIONAL FALL-THROUGH HERE; no "break" statement 549 case EVLA_K: 550 case EVLA_Q: 551 if (useConversionPath(skyFreqLowAC, skyFreqLowBD, band)) 552 { 553 //Need higher freq to be A/C 554 if (skyFreqCtrBD.compareTo(skyFreqCtrAC) > 0) 555 { 556 Frequency swap = skyFreqCtrAC; 557 skyFreqCtrAC = skyFreqCtrBD; 558 skyFreqCtrBD = swap; 559 560 swap = halfBwAC; 561 halfBwAC = halfBwBD; 562 halfBwBD = swap; 563 564 swappedIFs = true; 565 } 566 567 t303Path = 1; 568 ssloAC = skyFreqCtrAC.subtract(halfBwAC); 569 ssloBD = skyFreqCtrBD.add(halfBwBD); 570 } 571 else 572 { 573 t303Path = 0; 574 ssloAC = skyFreqCtrAC.subtract(halfBwAC); 575 ssloBD = skyFreqCtrBD.subtract(halfBwBD); 576 } 577 break; 578 579 default: 580 t303Path = 0; 581 ssloAC = skyFreqCtrAC.subtract(halfBwAC); 582 ssloBD = skyFreqCtrBD.subtract(halfBwBD); 583 } 584 585 //Put into ready-to-use text form 586 String[] result = new String[5]; 587 588 result[0] = band.getFrequencyName(); 589 result[1] = sUtil.formatNoScientificNotation(ssloAC.toUnits(MEGAHERTZ), 1, 10); 590 result[2] = sUtil.formatNoScientificNotation(ssloBD.toUnits(MEGAHERTZ), 1, 10); 591 result[3] = Integer.toString(t303Path); 592 result[4] = swappedIFs ? "swapped" : "not swapped"; 593 594 return result; 595 } 596 597 //Updated based on conversation w/ Ken S. 13-Jan-2009. 598 //Ken made changes to LoIfSetup. He suggests that having a bias toward 599 //using the conversion path is safer. To that end we will now use the 600 //copy path only when the IF pairs are quite close together. 601 //Updated again based on Ken S. email 02-Feb-2009. 602 //For Ka, use conversion path if either IF is < 32GHz, no matter 603 //how close the IF pairs are together. 604 private boolean useConversionPath(Frequency acLowSky, Frequency bdLowSky, 605 ReceiverBand band) 606 { 607 //Quick exit for Ka if either IF < minAC 608 if (band.equals(ReceiverBand.EVLA_Ka) && 609 (acLowSky.compareTo(MIN_AC_LOW_FREQ) < 0 || bdLowSky.compareTo(MIN_AC_LOW_FREQ) < 0)) 610 return true; 611 612 //Normal logic: if IFs are close together, don't need conversion path 613 final double bandwidthProxy = 50.0; //MHz 614 final double maxSpan = 3000.0; //3.0GHz 615 616 FrequencyRange lowEdgeSpan = new FrequencyRange(acLowSky, bdLowSky); 617 618 double span = lowEdgeSpan.getWidth().toUnits(MEGAHERTZ).doubleValue() + bandwidthProxy; 619 620 return span >= maxSpan; 621 } 622 623 /** @deprecated Do not use; 624 * see {@link #calcLoIfSetupParams(DopplerTracker, DopplerTracker, Date)} instead. 625 */ 626 public Frequency getSSLO(IFPair ifPair, DopplerTracker dt, Date dateTime) 627 throws CoordinateConversionException 628 { 629 Frequency sslo = getCentralSkyFrequency(ifPair, dt, dateTime); 630 Frequency halfBw = getBandwidth(ifPair).divideBy("2.0"); 631 632 String keyText = ""; 633 634 if (signalSource != null) 635 keyText = signalSource.getActiveReceivers().first().name() + "." + ifPair.name(); 636 637 if (RCVR_IF_PAIR_ADD.contains(keyText)) 638 sslo.add(halfBw); 639 else 640 sslo.subtract(halfBw); 641 642 return sslo; 643 } 644 /** @deprecated Do not use; see {@link #getSSLO(IFPair, DopplerTracker, Date)} instead. 645 */ 646 public Frequency getSSLO(IFPair ifPair) 647 { 648 Frequency sslo = getCentralFrequency(ifPair); 649 Frequency halfBw = getBandwidth(ifPair).divideBy("2.0"); 650 651 String keyText = ""; 652 653 if (signalSource != null) 654 keyText = signalSource.getActiveReceivers().first().name() + "." + ifPair.name(); 655 656 if (RCVR_IF_PAIR_ADD.contains(keyText)) 657 sslo.add(halfBw); 658 else 659 sslo.subtract(halfBw); 660 661 return sslo; 662 } 663 664 //---------------------------------------------------------------------------- 665 // Integration Time 666 //---------------------------------------------------------------------------- 667 private static final List<BigDecimal> INT_TIMES = new ArrayList<BigDecimal>(); 668 private static final BigDecimal DIVISOR = new BigDecimal("3.0"); 669 static 670 { 671 // Changed first item from BigDecimal.TEN to the following because I need 672 // 10.0 for comparisons later, not 10, and I can NOT use compareTo instead 673 // of equals, I have no control over that code. -- BWT, 2008-08-28 674 INT_TIMES.add(new BigDecimal("10.0")); 675 INT_TIMES.add(new BigDecimal("25.0" ).divide(DIVISOR, MathContext.DECIMAL128)); 676 INT_TIMES.add(new BigDecimal("20.0" ).divide(DIVISOR, MathContext.DECIMAL128)); 677 INT_TIMES.add(new BigDecimal("5.0")); 678 INT_TIMES.add(new BigDecimal("10.0" ).divide(DIVISOR, MathContext.DECIMAL128)); 679 INT_TIMES.add(new BigDecimal( "5.0" ).divide(DIVISOR, MathContext.DECIMAL128)); 680 INT_TIMES.add(new BigDecimal( "2.5" ).divide(DIVISOR, MathContext.DECIMAL128)); 681 INT_TIMES.add(new BigDecimal( "1.25").divide(DIVISOR, MathContext.DECIMAL128)); 682 } 683 684 /** 685 * Returns a list of valid integration times for the VLA correlator. 686 * The unit of time for the returned values is <i>seconds</i>. 687 * @return a list of valid integration times for the VLA correlator. 688 */ 689 public List<BigDecimal> getValidIntegrationTimes() 690 { 691 return new ArrayList<BigDecimal>(INT_TIMES); 692 } 693 694 /** 695 * Sets a new integration time for this configuration. 696 * 697 * @param seconds 698 * a new integration time for this configuration. 699 * The value should be chosen from the list of 700 * {@link #getValidIntegrationTimes() valid values}. 701 * 702 * @throws IllegalArgumentException 703 * if {@code seconds} is not one of the valid values from 704 * {@link #getValidIntegrationTimes()}. 705 */ 706 public void setIntegrationTime(BigDecimal seconds) 707 { 708 if (seconds == null) 709 throw new IllegalArgumentException("NULL is not a valid integration time."); 710 711 integrationTimeInSeconds = null; //Helps w/ loop, below 712 713 //This logic relies on the fact that the list of valid values is sorted 714 //from highest to lowest. 715 BigDecimal smaller = INT_TIMES.get(0); 716 if (seconds.compareTo(smaller) >= 0) 717 { 718 integrationTimeInSeconds = smaller; 719 } 720 else 721 { 722 int count = INT_TIMES.size(); 723 for (int i=1; i < count; i++) 724 { 725 BigDecimal larger = smaller; //index = i-1 726 smaller = INT_TIMES.get(i); 727 if (seconds.compareTo(smaller) >= 0) 728 { 729 double distToLarger = larger.subtract(seconds).doubleValue(); 730 double distToSmaller = seconds.subtract(smaller).doubleValue(); 731 732 integrationTimeInSeconds = 733 (distToLarger <= distToSmaller) ? larger : smaller; 734 735 break; 736 } 737 } 738 if (integrationTimeInSeconds == null) 739 integrationTimeInSeconds = smaller; 740 } 741 } 742 743 /** 744 * Returns a copy of the integration time for this configuration. 745 * @return a copy of the integration time for this configuration. 746 */ 747 public BigDecimal getIntegrationTime() 748 { 749 return integrationTimeInSeconds; 750 } 751 752 //---------------------------------------------------------------------------- 753 // Correlator Modes 754 //---------------------------------------------------------------------------- 755 756 /** 757 * Sets the correlator mode for this configuration. 758 * 759 * @param newMode 760 * the new correlator mode for this configuration. 761 * 762 * @throws IllegalArgumentException 763 * if {@code newMode} is <i>null</i>. 764 */ 765 public void setCorrelatorMode(CorrelatorMode newMode) 766 { 767 if (newMode == null) 768 throw new IllegalArgumentException("Cannot set NULL correlator mode."); 769 770 correlatorMode = newMode; 771 } 772 773 /** 774 * Returns the correlator mode for this configuration. 775 * @return the correlator mode for this configuration. 776 */ 777 public CorrelatorMode getCorrelatorMode() { return correlatorMode; } 778 779 //---------------------------------------------------------------------------- 780 // Bandwidth Codes 781 //---------------------------------------------------------------------------- 782 783 /** 784 * Sets the bandwidth code to use for the given IF. 785 * 786 * @param ifCode 787 * the IF for which {@code bandCode} will be applied. 788 * 789 * @param bwCode 790 * the bandwidth code to use for the given IF code. 791 * The bandwidth code determines bandwidth and the maximum number of 792 * spectral channels for the given IF. 793 * 794 * @throws IllegalArgumentException 795 * if either parameter is <i>null</i>. 796 */ 797 public void setBandwidthCode(IFCode ifCode, BandwidthCode bwCode) 798 { 799 if (bwCode == null) 800 throw new IllegalArgumentException("Cannot set NULL bandwidth code."); 801 802 if (ifCode == null) 803 throw new IllegalArgumentException("Cannot use NULL ifCode to set bandwidth code."); 804 805 if (IFCode.A.equals(ifCode)) bwCodeIfA = bwCode; 806 else if (IFCode.B.equals(ifCode)) bwCodeIfB = bwCode; 807 else if (IFCode.C.equals(ifCode)) bwCodeIfC = bwCode; 808 else if (IFCode.D.equals(ifCode)) bwCodeIfD = bwCode; 809 else 810 throw new RuntimeException("PROGRAMMER ERROR: did not expect ifCode = " + ifCode); 811 } 812 813 /** 814 * Sets the bandwidth codes for all four IFs at once. 815 * 816 * @param ifA the bandwidth code to use for IF A. 817 * @param ifB the bandwidth code to use for IF B. 818 * @param ifC the bandwidth code to use for IF C. 819 * @param ifD the bandwidth code to use for IF D. 820 * 821 * @throws IllegalArgumentException 822 * if any of the parameters are <i>null</i>. 823 */ 824 public void setBandwidthCodes(BandwidthCode ifA, BandwidthCode ifB, 825 BandwidthCode ifC, BandwidthCode ifD) 826 { 827 bwCodeIfA = checkForNull(ifA, "A"); 828 bwCodeIfB = checkForNull(ifB, "B"); 829 bwCodeIfC = checkForNull(ifC, "C"); 830 bwCodeIfD = checkForNull(ifD, "D"); 831 } 832 833 /** 834 * Sets the bandwidth codes for all four IFs at once, setting A & C to 835 * one value and B & D to the other. 836 * @see #setBandwidthCodes(BandwidthCode, BandwidthCode, BandwidthCode, BandwidthCode) 837 */ 838 public void setBandwidthCodes(BandwidthCode ifsAC, BandwidthCode ifsBD) 839 { 840 setBandwidthCodes(ifsAC, ifsBD, ifsAC, ifsBD); 841 } 842 843 private BandwidthCode checkForNull(BandwidthCode bwc, String ifCode) 844 { 845 if (bwc == null) 846 throw new IllegalArgumentException("NULL is not a valid bandwidth code for IF " + 847 ifCode + "."); 848 return bwc; 849 } 850 851 /** 852 * Returns the bandwidth code used for the given IF. 853 * 854 * @param ifCode 855 * the IF for which the returned bandwidth code is used. 856 * @return 857 * the bandwidth code used for the given IF. 858 * @throws IllegalArgumentException 859 * if {@code ifCode} is <i>null</i>. 860 */ 861 public BandwidthCode getBandwidthCode(IFCode ifCode) 862 { 863 if (ifCode == null) 864 throw new IllegalArgumentException("Cannot use NULL ifCode to fetch bandwidth code."); 865 866 if (IFCode.A.equals(ifCode)) return bwCodeIfA; 867 else if (IFCode.B.equals(ifCode)) return bwCodeIfB; 868 else if (IFCode.C.equals(ifCode)) return bwCodeIfC; 869 else if (IFCode.D.equals(ifCode)) return bwCodeIfD; 870 else 871 throw new RuntimeException("PROGRAMMER ERROR: did not expect ifCode = " + ifCode); 872 } 873 874 //---------------------------------------------------------------------------- 875 // Spectral Processing 876 //---------------------------------------------------------------------------- 877 878 /** 879 * Returns the collection of spectral processing options used by this 880 * configuration. 881 * The returned set is the one held internally by this configuration, so 882 * any changes made to it will change this configurations options. 883 * 884 * @return 885 * the collection of spectral processing options used by this configuration. 886 */ 887 public Set<ProcessingType> getSpectralProcessing() 888 { 889 return spectralProcessing; 890 } 891 892 /** 893 * Sets this configuration's spectral processing options. 894 * This object will hold a reference to the parameter, so any changes 895 * made to the set passed to this method after the method call will have 896 * an effect on this configuration. The one exception is for a parameter 897 * value of <i>null</i>. If <i>null</i> is received, this method will 898 * create a new empty set to use instead. 899 * 900 * @param options 901 * new spectral processing options for this configuration. 902 */ 903 public void setSpectralProcessing(Set<ProcessingType> options) 904 { 905 spectralProcessing = 906 (options == null) ? new HashSet<ProcessingType>() : options; 907 } 908 909 //This pair of methods converts Set<ProcessingType> to/from single string 910 //and is used w/ Hibernate. 911 @SuppressWarnings("unused") 912 private String getPersistentSpecOpts() 913 { 914 Collection<String> strings = new HashSet<String>(); 915 916 for (ProcessingType pt : spectralProcessing) 917 strings.add(pt.name()); 918 919 return sUtil.fromCollection(strings, " | "); 920 } 921 922 @SuppressWarnings("unused") 923 private void setPersistentSpecOpts(String text) 924 { 925 spectralProcessing.clear(); 926 927 if (text != null) 928 { 929 Collection<String> ptNames = sUtil.toCollection(text, " | ", null); 930 931 for (String ptName : ptNames) 932 spectralProcessing.add(ProcessingType.valueOf(ptName)); 933 } 934 } 935 936 //============================================================================ 937 // 938 //============================================================================ 939 940 /** 941 * Sets the central frequency and bandwidth of the unused IF pair, if any, 942 * to that of the IF pair that is being used. This is convenient for 943 * both validation and for script generation. 944 */ 945 public void tidyUp() 946 { 947 //See if we have an unused IF pair 948 if (correlatorMode.getIfPairCount() == 1) 949 { 950 //If we have A/C, set B/D values 951 if (correlatorMode.getCode().contains("A") || 952 correlatorMode.getCode().contains("C")) 953 { 954 centralFrequencyBD = centralFrequencyAC.clone(); 955 956 bwCodeIfB = bwCodeIfA; 957 bwCodeIfD = bwCodeIfC; 958 } 959 //If we have B/D, set A/C values 960 else 961 { 962 //Special situation for Ka band 963 ReceiverBand band = null; 964 965 AntennaElectronics src = getSignalSource(); 966 if (src != null) 967 { 968 Set<ReceiverBand> bands = src.getActiveReceivers(); 969 970 if (!bands.isEmpty()) 971 band = bands.iterator().next(); 972 } 973 974 if (band != null && band.equals(ReceiverBand.EVLA_Ka)) 975 centralFrequencyAC = new Frequency("33.0"); //~middle of band 976 else 977 centralFrequencyAC = centralFrequencyBD.clone(); 978 979 bwCodeIfA = bwCodeIfB; 980 bwCodeIfC = bwCodeIfD; 981 } 982 } 983 } 984 985 //============================================================================ 986 // 987 //============================================================================ 988 989 @Override 990 public VlaConfiguration clone() 991 { 992 VlaConfiguration clone = null; 993 994 try 995 { 996 //This line takes care of the primitive & immutable fields properly 997 clone = (VlaConfiguration)super.clone(); 998 999 clone.centralFrequencyAC = this.centralFrequencyAC.clone(); 1000 clone.centralFrequencyBD = this.centralFrequencyBD.clone(); 1001 1002 clone.spectralProcessing = 1003 new HashSet<ProcessingType>(this.spectralProcessing); 1004 1005 //All other properties are immutable 1006 } 1007 catch (Exception ex) 1008 { 1009 throw new RuntimeException(ex); 1010 } 1011 1012 return clone; 1013 } 1014 1015 /** Returns <i>true</i> if {@code o} is equal to this configuration. */ 1016 @Override 1017 public boolean equals(Object o) 1018 { 1019 //Quick exit if o is this 1020 if (o == this) 1021 return true; 1022 1023 //Quick exit if super class finds differences 1024 if (!super.equals(o)) 1025 return false; 1026 1027 //A safe cast if we got this far 1028 VlaConfiguration other = (VlaConfiguration)o; 1029 1030 return 1031 other.correlatorMode.equals(this.correlatorMode) && 1032 other.bwCodeIfA.equals(this.bwCodeIfA) && 1033 other.bwCodeIfB.equals(this.bwCodeIfB) && 1034 other.bwCodeIfC.equals(this.bwCodeIfC) && 1035 other.bwCodeIfD.equals(this.bwCodeIfD) && 1036 other.spectralProcessing.equals(this.spectralProcessing) && 1037 other.integrationTimeInSeconds.equals(this.integrationTimeInSeconds) && 1038 other.centralFrequencyAC.equals(this.centralFrequencyAC) && 1039 other.centralFrequencyBD.equals(this.centralFrequencyBD); 1040 1041 //Note the comparison of the spectralProcessing Set is OK because 1042 //the elements are immutable. 1043 } 1044 1045 /** Returns a hash code value for this configuration. */ 1046 @Override 1047 public int hashCode() 1048 { 1049 //Taken from the Effective Java book by Joshua Bloch. 1050 //The constants 17 & 37 are arbitrary & carry no meaning. 1051 int result = super.hashCode(); 1052 1053 //You MUST keep this method in sync w/ the equals method 1054 1055 result = 37 * result + correlatorMode.hashCode(); 1056 result = 37 * result + bwCodeIfA.hashCode(); 1057 result = 37 * result + bwCodeIfB.hashCode(); 1058 result = 37 * result + bwCodeIfC.hashCode(); 1059 result = 37 * result + bwCodeIfD.hashCode(); 1060 result = 37 * result + spectralProcessing.hashCode(); 1061 result = 37 * result + integrationTimeInSeconds.hashCode(); 1062 result = 37 * result + centralFrequencyAC.hashCode(); 1063 result = 37 * result + centralFrequencyBD.hashCode(); 1064 1065 return result; 1066 } 1067 1068 //============================================================================ 1069 // 1070 //============================================================================ 1071 /* 1072 //Integration time 1073 public static void main(String... args) throws Exception 1074 { 1075 VlaConfiguration vla = 1076 (VlaConfiguration)CorrelatorConfiguration.makeFor(CorrelatorName.VLA, 1077 edu.nrao.sss.model.resource.TelescopeType.EVLA.makeElectronics()); 1078 1079 //Display of integration times 1080 for (BigDecimal time : vla.getValidIntegrationTimes()) 1081 System.out.println(time); 1082 1083 System.out.println(); 1084 1085 for (BigDecimal time : vla.getValidIntegrationTimes()) 1086 System.out.println(time.setScale(3, java.math.RoundingMode.HALF_UP)); 1087 1088 //Setting int times 1089 double s = 1.0/3.0; 1090 for (int i=1; i <= 31; i++) 1091 { 1092 double seconds = s * i; 1093 vla.setIntegrationTime(new BigDecimal(seconds)); 1094 System.out.println("Set to " + seconds + ", but is now " + 1095 vla.getIntegrationTime().setScale(3, java.math.RoundingMode.HALF_UP)); 1096 } 1097 } 1098 */ 1099 /* 1100 //SSLO 1101 public static void main(String... args) throws Exception 1102 { 1103 AntennaElectronics antElec = 1104 edu.nrao.sss.model.resource.TelescopeType.EVLA.makeElectronics(); 1105 1106 VlaConfiguration vla = 1107 (VlaConfiguration)CorrelatorConfiguration.makeFor(CorrelatorName.VLA, antElec); 1108 1109 for (ReceiverBand band : edu.nrao.sss.model.resource.TelescopeType.EVLA.getReceivers()) 1110 { 1111 antElec.configureFor(band); 1112 Frequency cf = band.getFrequencyRange().getCenterFrequency(); 1113 vla.setCentralFrequency(IFPair.AC, cf); 1114 vla.setCentralFrequency(IFPair.BD, cf); 1115 System.out.println(band +": cf = "+cf+", SSLO A/C = "+vla.getSSLO(IFPair.AC)+", SSLO B/D = "+vla.getSSLO(IFPair.BD)); 1116 } 1117 } 1118 */ 1119 }