001 package edu.nrao.sss.model.resource.evla; 002 003 import java.util.ArrayList; 004 import java.util.List; 005 import java.util.Map; 006 import java.util.Set; 007 import java.util.SortedSet; 008 import java.util.TreeSet; 009 010 import javax.xml.bind.annotation.XmlAttribute; 011 import javax.xml.bind.annotation.XmlElement; 012 import javax.xml.bind.annotation.XmlRootElement; 013 import javax.xml.bind.annotation.XmlTransient; 014 015 import ca.nrc.widar.jaxb.vci.AutoCorrSubset; 016 import ca.nrc.widar.jaxb.vci.BlbPair; 017 import ca.nrc.widar.jaxb.vci.BlbSingle; 018 import ca.nrc.widar.jaxb.vci.CorrelationType; 019 import ca.nrc.widar.jaxb.vci.MaxMinPackType; 020 import ca.nrc.widar.jaxb.vci.PolProducts; 021 import ca.nrc.widar.jaxb.vci.Pp; 022 import ca.nrc.widar.jaxb.vci.ProductPacking; 023 import ca.nrc.widar.jaxb.vci.StationPacking; 024 025 import static edu.nrao.sss.astronomy.StokesParameter.LR; 026 import static edu.nrao.sss.astronomy.StokesParameter.RL; 027 028 import edu.nrao.sss.astronomy.PolarizationType; 029 import edu.nrao.sss.astronomy.StokesParameter; 030 import edu.nrao.sss.measure.TimeDuration; 031 import edu.nrao.sss.model.resource.CorrelationProductGroupAbs; 032 import edu.nrao.sss.model.resource.CorrelatorBaseband; 033 import edu.nrao.sss.model.resource.CorrelatorSubband; 034 import edu.nrao.sss.util.LookupTable; 035 036 /** 037 * A group of WIDAR correlation products that share certain features. 038 * <p> 039 * The VCI element to which this class is mapped is 040 * {@link ca.nrc.widar.jaxb.vci.PolProducts}.</p> 041 * <p> 042 * <b>Version Info:</b> 043 * <table style="margin-left:2em"> 044 * <tr><td>$Revision: 2298 $</td></tr> 045 * <tr><td>$Date: 2009-05-13 16:26:11 -0600 (Wed, 13 May 2009) $</td></tr> 046 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 047 * </table></p> 048 * 049 * @author David M. Harland 050 * @since 2008-11-19 051 */ 052 @XmlRootElement(name="group") 053 public class WidarCorrelationProductGroup 054 extends CorrelationProductGroupAbs 055 { 056 private WidarIntegrationTime integrationTime; 057 private int recircFactor; 058 059 //Cached so that we don't always have to navigate the containment 060 //hierarchy, but not persisted. The only method that should use 061 //this variable directly is getBlbpPool(). 062 transient private BlbpPool blbpPool; 063 064 /** 065 * Creates a new correlation product group for the given WIDAR subband. 066 * 067 * @param container 068 * the subband that contains this correlation product group. 069 * 070 * @throws IllegalArgumentException 071 * if {@code container} is <i>null</i>. 072 */ 073 WidarCorrelationProductGroup(WidarSubband container) 074 { 075 super(); 076 077 if (container == null) 078 throw new IllegalArgumentException( 079 "WidarCorrelationProductGroup may not be created for NULL subband."); 080 081 init(container); 082 } 083 084 @SuppressWarnings("unused") //Used by frameworks such as jaxb & hibernate 085 private WidarCorrelationProductGroup() 086 { 087 super(); 088 init(null); 089 } 090 091 private void init(WidarSubband container) 092 { 093 setContainer(container); 094 095 integrationTime = new WidarIntegrationTime(this); 096 recircFactor = 1; 097 098 setVciDefaults(); 099 } 100 101 WidarSubband getSubband() 102 { 103 return (WidarSubband)getContainer(); 104 } 105 106 void setSubband(WidarSubband container) 107 { 108 setContainer(container); 109 } 110 111 //============================================================================ 112 // RECIRCULATION 113 //============================================================================ 114 115 private static final LookupTable<Integer, Integer> VALID_RF_7_BIT_RQ = 116 new LookupTable<Integer, Integer>(); 117 static 118 { 119 for (int i=1; i <= 128; i*=2) 120 VALID_RF_7_BIT_RQ.put(i, i); 121 } 122 private static final LookupTable<Integer, Integer> VALID_RF_4_BIT_RQ = 123 new LookupTable<Integer, Integer>(); 124 static 125 { 126 for (int i=1; i <= 256; i*=2) 127 VALID_RF_4_BIT_RQ.put(i, i); 128 } 129 130 /** 131 * Returns the largest allowable recirculation factor for this group of 132 * correlation products. 133 * The returned value depends on the requantization and bandwidth of the 134 * containing subband. 135 * 136 * @return 137 * the largest allowable recirculation factor for this group of 138 * correlation products. 139 */ 140 public int getMaximumRecirculationFactor() 141 { 142 int normalMax = getRecircFactorTable().get(Integer.MAX_VALUE); 143 int specialMax = normalMax; 144 145 CorrelatorSubband sb = getContainer(); 146 147 if (sb != null) 148 specialMax = sb.getMaximumBandwidth().dividedBy(sb.getBandwidth()).intValue(); 149 150 return Math.min(normalMax, Math.max(1, specialMax)); 151 } 152 153 /** 154 * Returns the collection of valid recirculation factor values for 155 * this group of correlation products. The values in this collection 156 * depend on the requantization and bandwidth of the containing subband. 157 * 158 * @return 159 * the valid recirculation factors for this group of 160 * correlation products. 161 */ 162 public SortedSet<Integer> getAllowableRecirculationFactors() 163 { 164 SortedSet<Integer> factors = new TreeSet<Integer>(); 165 166 int maxRF = getMaximumRecirculationFactor(); 167 168 LookupTable<Integer, Integer> factorTable = getRecircFactorTable(); 169 170 for (int rf : factorTable.getKeySet()) 171 { 172 if (rf <= maxRF) factors.add(rf); 173 else break; 174 } 175 176 return factors; 177 } 178 179 /** 180 * Sets the recirculation factor for this group of correlation products. 181 * A value of <tt>1</tt> (one) represents <i>no recirculation</i>. 182 * <p> 183 * Recirculation is a means of producing more spectral channels 184 * per polarization product.</p> 185 * 186 * @param newFactor 187 * the new recirculation factor for this group of correlation products. 188 * The minimum value is <tt>1</tt> and the maximum value is 189 * give by {@link #getMaximumRecirculationFactor()}. 190 * All valid values in between the limits are integral powers 191 * of two. If {@code newFactor} is not a valid value, this 192 * method will set the recirculation factor to the largest valid 193 * value that is less than {@code newFactor} (except for the case 194 * where {@code newFactor} is less than the minimum, in which case 195 * the minimum valid value will be used). 196 */ 197 @XmlAttribute(required=false) 198 public void setRecirculationFactor(int newFactor) 199 { 200 int recirculation = 1; 201 202 if (newFactor > 1) 203 { 204 //We don't rely solely on table for max because there is a specialMax 205 //calc that can override the table values. 206 int max = getMaximumRecirculationFactor(); 207 208 recirculation = 209 (newFactor >= max) ? max : getRecircFactorTable().get(newFactor); 210 } 211 212 if (recircFactor != recirculation) 213 { 214 recircFactor = recirculation; 215 needToRecalcChannels = true; 216 } 217 } 218 219 /** 220 * Returns the recirculation factor for this group of correlation products. 221 * @return the recirculation factor for this group of correlation products. 222 * @see #setRecirculationFactor(int) 223 */ 224 public int getRecirculationFactor() 225 { 226 return recircFactor; 227 } 228 229 private LookupTable<Integer, Integer> getRecircFactorTable() 230 { 231 CorrelatorSubband sb = getContainer(); 232 233 int rq = (sb == null) ? 4 : sb.getRequantization(); 234 235 return rq == 4 ? VALID_RF_4_BIT_RQ : VALID_RF_7_BIT_RQ; 236 } 237 238 //============================================================================ 239 // INTEGRATION TIME 240 //============================================================================ 241 242 /** 243 * Sets the total integration time for the products of this group. 244 * <p> 245 * Note that this object will not hold a reference to 246 * <tt>totalDuration</tt>, so clients may continue to use it without 247 * affecting this object.</p> 248 * 249 * @param totalDuration 250 * the total integration duration for the products of this group. 251 */ 252 @XmlTransient 253 public void setTotalIntegrationTime(TimeDuration totalDuration) 254 { 255 integrationTime.setTotalIntegrationTime(totalDuration); 256 } 257 258 /** 259 * Returns the total integration time for the products of this group. 260 * <p> 261 * The returned duration is not referenced internally, so clients may 262 * alter it without affecting this object.</p> 263 * 264 * @return the total integration time for the products of this group. 265 */ 266 public TimeDuration getTotalIntegrationTime() 267 { 268 return integrationTime.getTotalIntegrationTime(); 269 } 270 271 /** 272 * Returns the minimum total integration time for this product. 273 * <p> 274 * The returned duration is not referenced internally, so clients may 275 * alter it without affecting this object.</p> 276 * 277 * @return the minimum total integration time for this product. 278 */ 279 public TimeDuration getMinimumTotalIntegrationTime() 280 { 281 return integrationTime.getSmallestMinimumHardwareIntegrationTime(); 282 } 283 284 /** 285 * Returns the integration time for this group. 286 * This method can be used when a client wants fine grained control 287 * over the components of the total integration time. Many clients 288 * will prefer to use the convenience methods 289 * <ul> 290 * <li>{@link #getTotalIntegrationTime()}</li> 291 * <li>{@link #setTotalIntegrationTime(TimeDuration)}</li> 292 * <li>{@link #getMinimumTotalIntegrationTime()}</li> 293 * </ul> 294 * <p> 295 * The object returned is the one held internally by this group, 296 * so changes made to it <i>will</i> be reflected herein.</p> 297 * 298 * @return 299 * the integration time object held internally by this group. 300 */ 301 public WidarIntegrationTime getIntegrationTime() 302 { 303 return integrationTime; 304 } 305 306 @XmlElement 307 @SuppressWarnings("unused") //For JAXB & Hibernate 308 private void setIntegrationTime(WidarIntegrationTime newTime) 309 { 310 if (newTime != null) 311 integrationTime = newTime; 312 } 313 314 //============================================================================ 315 // CORRELATOR RESOURCES (BLBPs) 316 //============================================================================ 317 318 //Fetches and caches the BLBP pool 319 private BlbpPool getBlbpPool() 320 { 321 WidarSubband sb = getSubband(); 322 323 if (blbpPool == null && sb != null) 324 { 325 WidarBaseband bb = sb.getBaseband(); 326 327 if (bb != null) 328 { 329 EvlaWidarConfiguration bbContainer = bb.getEvlaWidarConfiguration(); 330 331 if (bbContainer != null) 332 blbpPool = bbContainer.getBaselineBoardPool(); 333 } 334 } 335 336 return blbpPool; 337 } 338 339 /** 340 * Returns a list of the number of BLBPs that may be owned by this group. 341 * Often the returned list will contain the integers zero through the number 342 * of unowned BLBPs. However, depending on the properties of this group, 343 * not all integral values in that range may be valid, and the maximum number 344 * might be something less than the total available. 345 * <p> 346 * The returned list is sorted from lowest to highest.</p> 347 * 348 * @return 349 * a list containing the valid numbers of BLBPs for the this group. 350 */ 351 public List<Integer> getValidBlbpCounts() 352 { 353 List<Integer> valid; 354 355 BlbpPool pool = getBlbpPool(); 356 357 if (pool == null) 358 { 359 valid = new ArrayList<Integer>(); 360 valid.add(0); 361 } 362 else 363 { 364 valid = pool.getValidBlbpCounts(this); 365 } 366 367 return valid; 368 } 369 370 /** 371 * Attempts to allocate the given number of BLBPs to this group. 372 * Note that <tt>newTotalCount</tt> is the <i>total</i> number of BLBPs 373 * to be allocated to this group, not an <i>additional</i> number. 374 * 375 * @param newTotalCount 376 * the desired number of baseline board pairs to assign to this group. 377 * 378 * @return 379 * the total number of BLBPs actually allocated to this group. 380 * This number will be less than or equal to <tt>newTotalCount</tt>, 381 * unless <tt>newCount</tt> is less than zero, in which case the 382 * returned value will be zero. 383 */ 384 public int setBlbpCount(int newTotalCount) 385 { 386 int actualNewTotalCount = 0; 387 388 BlbpPool pool = getBlbpPool(); 389 390 if (pool != null) 391 { 392 int oldTotalCount = pool.getBlbpsOwnedBy(this); 393 394 if (newTotalCount > oldTotalCount) 395 { 396 int added = 397 pool.allocateAdditionalPairsTo(this, newTotalCount-oldTotalCount); 398 399 actualNewTotalCount = oldTotalCount + added; 400 } 401 else if (newTotalCount < oldTotalCount) 402 { 403 int removed = 404 pool.reclaimPairsFrom(this, oldTotalCount-newTotalCount); 405 406 actualNewTotalCount = oldTotalCount - removed; 407 } 408 else //old == new 409 actualNewTotalCount = oldTotalCount; 410 411 if (actualNewTotalCount != oldTotalCount) 412 needToRecalcChannels = true; 413 } 414 415 return actualNewTotalCount; 416 } 417 418 /** 419 * Returns the current number of baseline board pairs allocated to this group. 420 * Note that it is possible for the returned value to be an illegal value. 421 * This can happen because the underlying pool of BLBPs may not be aware 422 * of recent changes to this group or its containing subband and baseband. 423 * Use {@link #fixBlbpCount()} to simultaneously adjust the allocated number 424 * to a legal value and obtain that number. 425 * 426 * @return 427 * the current number of BLBPs allocated to this group. 428 */ 429 @XmlTransient //The BlbpPool will be persisted elsewhere and hold these allocations 430 public int getBlbpCount() 431 { 432 BlbpPool pool = getBlbpPool(); 433 434 return pool == null ? 0 : pool.getBlbpsOwnedBy(this); 435 } 436 437 /** 438 * Adjusts, if necessary, the number of BLBPs allocated to this group 439 * and returns the new value. 440 * 441 * @return 442 * the number of BLBPs allocated to this group after ensuring the 443 * current number was legal for this group. 444 */ 445 public int fixBlbpCount() 446 { 447 return setBlbpCount(getBlbpCount()); 448 } 449 450 //============================================================================ 451 // CORRELATION PRODUCTS 452 //============================================================================ 453 454 //TODO Think about what will happen in the case where someone set up 4 products 455 // but then later split the containing baseband into singlets. We need 456 // to ensure we don't have impossible products (eg, RxL for a BB w/ only R) 457 458 private static final int CHANNELS_PER_BLBP_BUNCH = 256; 459 private static final int SUBBAND_CHANNEL_MAX = 64 * 1024; 460 private static final int SUBBAND_CHANNEL_SUPER_MAX = 256 * 1024; 461 462 @Override 463 protected void recalculateChannels() 464 { 465 BlbpPool pool = getBlbpPool(); 466 467 //Make sure we have only legal correlations in channels map 468 removeDisallowedProducts(); 469 470 int productCount = channels.size(); 471 472 //Do the calculations only if we have polarization products to 473 //which we can distribute the channels. 474 if (productCount > 0 && pool != null) 475 { 476 //Make sure our # of BLBPs is valid 477 int blbps = fixBlbpCount(); 478 479 //Make sure recirculation and bandwidth are compatible. 480 //Keep bandwidth and adjust recirc if necessary. 481 setRecirculationFactor(recircFactor); 482 483 //Atomic unit of BLBPs (1 or 4). Integer divide. 484 int blbpBunches = blbps / pool.getBlbpIncrement(this); 485 486 //Total number of channels for this group, to be spread over products 487 int totalChannels = blbpBunches * CHANNELS_PER_BLBP_BUNCH * recircFactor; 488 489 //Make sure we didn't violate maximum 490 //TODO need to refresh knowledge re: max channels for 7-bit RQ 491 int max = (productCount > blbpBunches) ? SUBBAND_CHANNEL_MAX 492 : SUBBAND_CHANNEL_SUPER_MAX; 493 if (totalChannels > max) 494 totalChannels = max; 495 496 //Spread channels over existing products. 497 //For 1, 2, or 4 products, spread evenly. 498 if (productCount == 1 || productCount == 2 || productCount == 4) 499 { 500 //Since we already know totalChannels is a multiple of 256, 501 //this division by 1, 2, or 4 will produce an integer. 502 int channelsPerProduct = totalChannels / productCount; 503 504 for (StokesParameter sp : channels.keySet()) 505 channels.put(sp, channelsPerProduct); 506 } 507 //For 3 products divide in ratio 1:1:2, where the products receiving 508 //fewer channels are either both parallel or both cross products. 509 else if (productCount == 3) 510 { 511 int chX1 = totalChannels / 4; 512 int chX2 = 2 * chX1; 513 514 Set<StokesParameter> products = channels.keySet(); 515 516 boolean chX2ForParallel = products.contains(RL) && products.contains(LR); 517 518 for (StokesParameter sp : channels.keySet()) 519 { 520 switch (sp) 521 { 522 case LR: //intentional fall-through 523 case RL: 524 channels.put(sp, chX2ForParallel ? chX1 : chX2); 525 break; 526 527 case LL: //intentional fall-through 528 case RR: 529 channels.put(sp, chX2ForParallel ? chX2 : chX1); 530 break; 531 532 default: 533 throw new RuntimeException("PROGRAMMER ERROR: Unexpected polarization product "+sp); 534 } 535 } 536 } 537 } 538 539 //Even if we did not do the calc, we clear this flag 540 needToRecalcChannels = false; 541 } 542 543 //============================================================================ 544 // VCI ELEMENTS 545 //============================================================================ 546 547 //VCI element PolProducts 548 private AutoCorrSubset autoCorrSubset; 549 private List<BlbPair> blbPair; 550 // blbProdIntegration - part of this class already 551 private List<BlbSingle> blbSingle; 552 // pp - part of this class already 553 private ProductPacking productPacking; 554 private StationPacking stationPacking; 555 556 557 //TODO need to handle blbPair and blbSingle differently. 558 // Need to derive from getBlbpCount. 559 560 561 private void setVciDefaults() 562 { 563 //VCI element PolProducts 564 overrideAutoCorr = false; 565 autoCorrSubset = null; 566 autoCorrCache = VciJaxbUtil.makeAutoCorrSubset(); 567 568 blbPair = new ArrayList<BlbPair>(); 569 blbSingle = new ArrayList<BlbSingle>(); 570 productPacking = new ProductPacking(); 571 stationPacking = new StationPacking(); 572 573 productPacking.setAlgorithm(MaxMinPackType.MAX_PACK); 574 stationPacking.setAlgorithm(MaxMinPackType.MAX_PACK); 575 } 576 577 /** 578 * Returns a Virtual Correlator Interface (VCI) representation of this object. 579 * 580 * @return a VCI representation of this object. 581 */ 582 public PolProducts toVci() 583 { 584 PolProducts vciObj = new PolProducts(); 585 586 //Modeled as VCI elements 587 vciObj.setAutoCorrSubset(VciJaxbUtil.clone(autoCorrSubset)); 588 vciObj.setProductPacking(VciJaxbUtil.clone(productPacking)); 589 vciObj.setStationPacking(VciJaxbUtil.clone(stationPacking)); 590 591 VciJaxbUtil.copyBlbPairs (blbPair, vciObj.getBlbPair()); 592 VciJaxbUtil.copyBlbSingles(blbSingle, vciObj.getBlbSingle()); 593 594 //Mapped from SSS objects 595 vciObj.setBlbProdIntegration(integrationTime.toVci()); 596 597 int p = 0; 598 List<Pp> vciPps = vciObj.getPp(); 599 vciPps.clear(); 600 for (Map.Entry<StokesParameter, Integer> corrProd : channels.entrySet()) 601 { 602 Pp vciPp = new Pp(); 603 604 //TODO below gives unique w/in group; need unique w/in subband? 605 vciPp.setId(++p); 606 vciPp.setCorrelation(getVciCorrType(corrProd.getKey())); 607 vciPp.setSpectralChannels(corrProd.getValue()); 608 609 vciPps.add(vciPp); 610 } 611 612 return vciObj; 613 } 614 615 //---------------------------------------------------------------------------- 616 // Direct access to some VCI elements 617 //---------------------------------------------------------------------------- 618 619 private boolean overrideAutoCorr; 620 private AutoCorrSubset autoCorrCache; 621 622 /** 623 * Tells this group to override, or restore, auto correlation subset defaults. 624 * 625 * @param override 626 * instruction to override, or restore, auto correlation subset defaults. 627 * 628 * @return 629 * <i>null</i> if {@code override} is <i>false</i>. 630 * If {@code override} is <i>true</i>, returns the <tt>AutoCorrSubset</tt> 631 * held internally by this group. This means clients can operate 632 * directly on the returned object and impact this group. 633 */ 634 public AutoCorrSubset overrideDefaultAutoCorrSubset(boolean override) 635 { 636 //Changing state? 637 if (override != overrideAutoCorr) 638 { 639 overrideAutoCorr = override; 640 641 if (override) //user-specified 642 { 643 autoCorrSubset = autoCorrCache; 644 } 645 else //default setting 646 { 647 autoCorrCache = VciJaxbUtil.clone(autoCorrSubset); 648 autoCorrSubset = null; 649 } 650 } 651 652 return autoCorrSubset; 653 } 654 655 /** 656 * Returns the auto correlation configuration, if any. 657 * If this group is not using this feature, <i>null</i> is returned. 658 * <p> 659 * This is a VCI property. 660 * The returned object was generated from VCI XML schema elements and 661 * is the actual instance held internally by this baseband.</p> 662 * 663 * @return the auto correlation configuration, if any. 664 * 665 * @see #overrideDefaultAutoCorrSubset(boolean) 666 */ 667 @XmlElement(namespace="http://www.nrc.ca/namespaces/widar") 668 public AutoCorrSubset getAutoCorrSubset() { return autoCorrSubset; } 669 670 //This method is for persistence frameworks such as JAXB & Hibernate 671 @SuppressWarnings("unused") 672 private void setAutoCorrSubset(AutoCorrSubset newSubset) 673 { 674 autoCorrSubset = newSubset; 675 overrideAutoCorr = (newSubset != null); 676 } 677 678 @XmlElement(namespace="http://www.nrc.ca/namespaces/widar") 679 public void setProductPacking(ProductPacking newPacking) 680 { 681 if (newPacking != null) 682 productPacking = newPacking; 683 } 684 public ProductPacking getProductPacking() { return productPacking; } 685 686 @XmlTransient 687 public void setProductPackingAlgorithm(MaxMinPackType newAlg) 688 { 689 if (newAlg != null) 690 productPacking.setAlgorithm(newAlg); 691 } 692 public MaxMinPackType getProductPackingAlgorithm() 693 { 694 return productPacking.getAlgorithm(); 695 } 696 697 @XmlElement(namespace="http://www.nrc.ca/namespaces/widar") 698 public void setStationPacking(StationPacking newPacking) 699 { 700 if (newPacking != null) 701 stationPacking = newPacking; 702 } 703 public StationPacking getStationPacking() { return stationPacking; } 704 705 @XmlTransient 706 public void setStationPackingAlgorithm(MaxMinPackType newAlg) 707 { 708 if (newAlg != null) 709 stationPacking.setAlgorithm(newAlg); 710 } 711 public MaxMinPackType getStationPackingAlgorithm() 712 { 713 return stationPacking.getAlgorithm(); 714 } 715 716 /** Returns the VCI version of correlation product type. */ 717 private CorrelationType getVciCorrType(StokesParameter stokes) 718 { 719 CorrelatorBaseband bb = null; 720 721 WidarSubband sb = getSubband(); 722 if (sb != null) 723 bb = sb.getBaseband(); 724 725 boolean aIsR = true; 726 727 //We allow construction of subbands outside of basebands, so it is possible 728 //for bb to be null. In common usage, though, this will not be the case. 729 if (bb != null) 730 { 731 List<PolarizationType> polarizations = bb.getPolarizations(); 732 733 PolarizationType ptA = polarizations.get(0); 734 PolarizationType ptB = polarizations.size() < 2 ? null : polarizations.get(1); 735 736 aIsR = ptA.equals(PolarizationType.R); 737 738 if (ptB != null && !ptB.getOpposite().equals(ptA)) 739 throw new IllegalStateException("Expected ptA (" + ptA + ") and ptB ("+ ptB + 740 ") to be opposite polarizations."); 741 } 742 743 switch (stokes) 744 { 745 case RR: return aIsR ? CorrelationType.A_A : CorrelationType.B_B; 746 case RL: return aIsR ? CorrelationType.A_B : CorrelationType.B_A; 747 case LR: return aIsR ? CorrelationType.B_A : CorrelationType.A_B; 748 case LL: return aIsR ? CorrelationType.B_B : CorrelationType.A_A; 749 750 default: 751 throw new RuntimeException( 752 "PROGRAMMER ERROR: Unexpected StokesParameter found = "+ stokes); 753 } 754 } 755 756 //============================================================================ 757 // 758 //============================================================================ 759 760 @Override 761 protected void copyInto(CorrelationProductGroupAbs other) 762 { 763 if (other instanceof WidarCorrelationProductGroup) 764 copyInto((WidarCorrelationProductGroup)other); 765 } 766 767 private void copyInto(WidarCorrelationProductGroup other) 768 { 769 super.copyInto(other); 770 771 other.integrationTime = this.integrationTime.makeCopyFor(other); 772 other.recircFactor = this.recircFactor; 773 774 other.overrideAutoCorr = this.overrideAutoCorr; 775 other.autoCorrSubset = VciJaxbUtil.clone(this.autoCorrSubset); 776 other.autoCorrCache = VciJaxbUtil.clone(this.autoCorrCache); 777 778 other.productPacking = VciJaxbUtil.clone(this.productPacking); 779 other.stationPacking = VciJaxbUtil.clone(this.stationPacking); 780 781 other.blbPair.clear(); 782 for (BlbPair blbp : this.blbPair) 783 other.blbPair.add(VciJaxbUtil.clone(blbp)); 784 785 other.blbSingle.clear(); 786 for (BlbSingle blb : this.blbSingle) 787 other.blbSingle.add(VciJaxbUtil.clone(blb)); 788 } 789 790 /** 791 * Creates a copy of this product group. 792 * The new copy is identical to this one, except: 793 * <ol> 794 * <li>Its subband is {@code otherSubband}.</li> 795 * <li>Its ID has been cleared.</li> 796 * </ol> 797 * Note that this method does <i>not</i> tell {@code otherSubband} about 798 * the new group. The link forged in this method is one-way; clients 799 * are responsible for the link in the other direction. 800 */ 801 WidarCorrelationProductGroup makeCopyFor(WidarSubband otherSubband) 802 { 803 WidarCorrelationProductGroup newGroup = 804 new WidarCorrelationProductGroup(otherSubband); 805 806 this.copyInto(newGroup); 807 808 return newGroup; 809 } 810 811 @Override 812 public boolean equals(Object o) 813 { 814 //Quick exit if o is this object 815 if (o == this) 816 return true; 817 818 //Not equal if super class says not equal 819 if (!super.equals(o)) 820 return false; 821 822 //Super class tested for Class equality, so cast is safe 823 WidarCorrelationProductGroup other = (WidarCorrelationProductGroup)o; 824 825 //Intentionally NOT comparing: subband, blbpPool 826 827 //SSS properties 828 if (other.recircFactor != this.recircFactor) 829 return false; 830 831 if (!other.integrationTime.equals(this.integrationTime)) 832 return false; 833 834 //VCI properties 835 if (other.overrideAutoCorr != this.overrideAutoCorr) 836 return false; 837 838 if (overrideAutoCorr && 839 !VciJaxbUtil.testEquality(other.autoCorrSubset, this.autoCorrSubset)) 840 return false; 841 842 if (!VciJaxbUtil.testEquality(other.productPacking, this.productPacking) || 843 !VciJaxbUtil.testEquality(other.stationPacking, this.stationPacking)) 844 return false; 845 846 return true; 847 } 848 849 @Override 850 public int hashCode() 851 { 852 //Taken from the Effective Java book by Joshua Bloch. 853 //The constant 37 is arbitrary & carries no meaning. 854 int result = 37 * super.hashCode(); 855 856 //SSS properties 857 result = 37 * result + recircFactor; 858 result = 37 * result + integrationTime.hashCode(); 859 860 //VCI properties 861 if (overrideAutoCorr) 862 result = 37 * result + VciJaxbUtil.makeHashCode(autoCorrSubset); 863 864 result = 37 * result + VciJaxbUtil.makeHashCode(productPacking); 865 result = 37 * result + VciJaxbUtil.makeHashCode(stationPacking); 866 867 return result; 868 } 869 870 //============================================================================ 871 // 872 //============================================================================ 873 /* 874 public static void main(String... args) throws Exception 875 { 876 } 877 */ 878 }