001 package edu.nrao.sss.model.resource; 002 003 import java.util.ArrayList; 004 import java.util.List; 005 006 import javax.xml.bind.annotation.XmlAttribute; 007 import javax.xml.bind.annotation.XmlElement; 008 import javax.xml.bind.annotation.XmlTransient; 009 import javax.xml.bind.annotation.XmlType; 010 011 import org.apache.log4j.Logger; 012 013 import edu.nrao.sss.math.MathUtil; 014 import edu.nrao.sss.measure.Frequency; 015 import edu.nrao.sss.measure.FrequencyRange; 016 import edu.nrao.sss.util.Identifiable; 017 018 /** 019 * A partial implementation of a {@link CorrelatorSubband}. 020 * <p> 021 * <b>Version Info:</b> 022 * <table style="margin-left:2em"> 023 * <tr><td>$Revision: 2289 $</td></tr> 024 * <tr><td>$Date: 2009-05-07 16:13:41 -0600 (Thu, 07 May 2009) $</td></tr> 025 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 026 * </table></p> 027 * 028 * @author David M. Harland 029 * @since 2008-03-11 030 */ 031 @XmlType(propOrder={"name", "freqRange","requantizationBits" 032 }) 033 public abstract class CorrelatorSubbandAbs 034 implements CorrelatorSubband 035 { 036 private static final Logger LOG = Logger.getLogger(CorrelatorSubbandAbs.class); 037 038 private Long id; //A unique identifier for the persistence layer. 039 040 private CorrelatorBasebandAbs baseband; 041 042 @XmlAttribute(required=false) 043 protected String name; 044 045 @XmlElement(name="frequencyRange") 046 protected FrequencyRange freqRange; 047 048 @XmlAttribute(required=true) 049 protected int requantizationBits; 050 051 protected List<CorrelationProductGroupAbs> productGroups; 052 053 private BandwidthHelper bwHelper; 054 055 /** Helps create a new correlator subband. */ 056 protected CorrelatorSubbandAbs() 057 { 058 id = Identifiable.UNIDENTIFIED; 059 name = null; 060 freqRange = new FrequencyRange(); 061 baseband = null; 062 productGroups = new ArrayList<CorrelationProductGroupAbs>(); 063 bwHelper = new BandwidthHelper(this); 064 } 065 066 //============================================================================ 067 // IDENTIFICATION 068 //============================================================================ 069 070 /* (non-Javadoc) 071 * @see edu.nrao.sss.util.Identifiable#getId() 072 */ 073 public Long getId() { return id; } 074 075 @SuppressWarnings("unused") 076 private void setId(Long id) { this.id = id; } 077 078 public void clearId() 079 { 080 id = Identifiable.UNIDENTIFIED; 081 082 for (CorrelationProductGroupAbs group : productGroups) 083 group.clearId(); 084 } 085 086 @XmlTransient 087 public void setName(String newName) 088 { 089 name = newName; //null is a permitted value 090 } 091 092 public String getName() 093 { 094 return name == null ? "" : name; 095 } 096 097 //============================================================================ 098 // FREQUENCY 099 //============================================================================ 100 101 /* (non-Javadoc) 102 * @see CorrelatorBaseband#getAllowableBandwidthFor(Frequency) 103 */ 104 public Frequency getAllowableBandwidthFor(Frequency frequency) 105 { 106 return bwHelper.getAllowableBandwidthFor(frequency); 107 } 108 109 /* (non-Javadoc) 110 * @see CorrelatorBaseband#getAllowableBandwidthClosestTo(Frequency) 111 */ 112 public Frequency getAllowableBandwidthClosestTo(Frequency frequency) 113 { 114 return bwHelper.getAllowableBandwidthClosestTo(frequency); 115 } 116 117 /* (non-Javadoc) 118 * @see CorrelatorSubband#setBandwidth(Frequency) 119 */ 120 @XmlTransient 121 public void setBandwidth(Frequency newWidth) 122 { 123 if (newWidth == null) 124 throw new IllegalArgumentException( 125 "Cannot set bandwidth of subband to NULL."); 126 127 //Force to a legal value 128 newWidth = getAllowableBandwidthClosestTo(newWidth); 129 130 //The frequency range class will normally put the center where requested. 131 //However, if the low frequency of the range would be negative, the 132 //freq range class will move the low freq to zero and the center to BW/2. 133 freqRange.setCenterAndWidth(freqRange.getCenterFrequency(), newWidth); 134 135 //If the current center and old width had the high end of this subband 136 //at or near upper edge of baseband, we may need to move the center 137 //to accommodate the new bw so that we don't spill over upper BB freq. 138 Frequency maxCenter = getMaximumCentralFrequency(); 139 if (getCentralFrequency().compareTo(maxCenter) > 0) 140 freqRange.setCenterAndWidth(maxCenter, newWidth); 141 } 142 143 /* (non-Javadoc) 144 * @see edu.nrao.sss.model.resource.HasBandwidth#getBandwidth() 145 */ 146 public Frequency getBandwidth() { return freqRange.getWidth(); } 147 148 /* (non-Javadoc) 149 * @see CorrelatorSubband#setCentralFrequency(Frequency) 150 */ 151 @XmlTransient 152 public void setCentralFrequency(Frequency newCenter) 153 { 154 if (newCenter == null) 155 throw new IllegalArgumentException( 156 "Cannot set center of subband to NULL."); 157 158 //Cap new center at max 159 Frequency maxCenter = getMaximumCentralFrequency(); 160 if (newCenter.compareTo(maxCenter) > 0) 161 newCenter = maxCenter; 162 163 //Don't need to test against min because the freq range class 164 //will guard against a negative low freq by moving the center 165 //up to BW/2. 166 freqRange.setCenterAndWidth(newCenter, freqRange.getWidth()); 167 } 168 169 /* (non-Javadoc) 170 * @see CorrelatorSubband#getCentralFrequency() 171 */ 172 public Frequency getCentralFrequency() 173 { 174 return freqRange.getCenterFrequency(); 175 } 176 177 public Frequency getMinimumCentralFrequency() 178 { 179 return freqRange.getWidth().divideBy("2.0"); 180 } 181 182 public Frequency getMaximumCentralFrequency() 183 { 184 Frequency maxCenter = new Frequency(MathUtil.POSITIVE_INFINITY); 185 186 if (baseband != null) 187 { 188 Frequency halfBw = freqRange.getWidth().divideBy("2.0"); 189 maxCenter = baseband.getBandwidth().subtract(halfBw); 190 } 191 192 return maxCenter; 193 } 194 195 /* (non-Javadoc) 196 * @see CorrelatorSubband#getFrequencyRange() 197 */ 198 public FrequencyRange getFrequencyRange() { return freqRange.clone(); } 199 200 /* (non-Javadoc) 201 * @see edu.nrao.sss.model.resource.CorrelatorSubband#getProxiedRange() 202 */ 203 public FrequencyRange getProxiedRange() 204 { 205 FrequencyRange answer; 206 207 //If we have a baseband, use the subband's location in the baseband 208 //and the baseband's representation of the original frequency range 209 //to determine what part of that range this subband represents. 210 if (baseband != null) 211 { 212 FrequencyRange bbProxiedRange = baseband.getProxiedRange(); 213 214 Frequency sbProxyCenter = freqRange.getCenterFrequency(); 215 216 //We need to know whether increasing baseband frequencies represent 217 //increasing or decreasing proxied frequencies. 218 if (baseband.proxiedRangeIsReversed()) 219 sbProxyCenter = bbProxiedRange.getHighFrequency().subtract(sbProxyCenter); 220 else 221 sbProxyCenter.add(bbProxiedRange.getLowFrequency()); 222 223 answer = new FrequencyRange().setCenterAndWidth(sbProxyCenter, 224 freqRange.getWidth()); 225 } 226 else //no baseband, just use subband's nominal range 227 { 228 answer = freqRange.clone(); 229 } 230 231 return answer; 232 } 233 234 /* (non-Javadoc) 235 * @see CorrelatorSubband#setCentralProxiedFrequency(Frequency) 236 */ 237 //@Override 238 public void setCentralProxiedFrequency(Frequency newSkyCenter) 239 { 240 Frequency center; 241 242 if (baseband != null) 243 { 244 FrequencyRange bbProxiedRange = baseband.getProxiedRange(); 245 246 //We need to know whether increasing baseband frequencies represent 247 //increasing or decreasing proxied frequencies. 248 if (baseband.proxiedRangeIsReversed()) 249 center = bbProxiedRange.getHighFrequency().subtract(newSkyCenter); 250 else 251 center = newSkyCenter.clone().subtract(bbProxiedRange.getLowFrequency()); 252 } 253 else 254 { 255 center = newSkyCenter; 256 } 257 258 setCentralFrequency(center); 259 } 260 261 //============================================================================ 262 // QUANTIZATION 263 //============================================================================ 264 265 /* (non-Javadoc) 266 * @see CorrelatorSubband#getRequantization() 267 */ 268 public int getRequantization() { return requantizationBits; } 269 270 //============================================================================ 271 // CORRELATION PRODUCT GROUPS 272 //============================================================================ 273 274 /** 275 * Creates and returns a new product group that could be used with this 276 * subband. 277 */ 278 abstract protected CorrelationProductGroupAbs makeCorrelationProductGroup(); 279 280 /** 281 * Returns this subband's collection of correlation product groups. 282 * <p> 283 * The returned collection is guaranteed to be non-null, but it may be empty. 284 * The returned collection is a copy of the one held internally, so 285 * changes to the structure of the collection by clients will not impact 286 * this object. The members of the collection, however, are those referenced 287 * by this subband, so changes to them will be reflected herein.</p> 288 * 289 * @return this subband's collection of correlation product groups. 290 */ 291 public List<CorrelationProductGroup> getCorrelationProductGroups() 292 { 293 return new ArrayList<CorrelationProductGroup>(productGroups); 294 } 295 296 /** 297 * Creates a new correlation product group, adds it to this subband, and 298 * returns it. 299 * 300 * @return a new correlation product group of this subband. 301 */ 302 public CorrelationProductGroupAbs addNewCorrelationProductGroup() 303 { 304 CorrelationProductGroupAbs newGroup = makeCorrelationProductGroup(); 305 306 productGroups.add(newGroup); 307 308 return newGroup; 309 } 310 311 /** 312 * Removes a correlation product group from this subband. 313 * 314 * @param unwantedGroup 315 * a correlation product group to be removed from this subband. 316 */ 317 public void removeCorrelationProductGroup(CorrelationProductGroup unwantedGroup) 318 { 319 productGroups.remove(unwantedGroup); 320 } 321 322 @SuppressWarnings("unused") //Used by Hibernate 323 private void setProdGrpList(List<CorrelationProductGroupAbs> replacementList) 324 { 325 LOG.debug("Setting productGroup list. Size = "+(replacementList==null?0:replacementList.size())); 326 327 productGroups = 328 replacementList == null ? new ArrayList<CorrelationProductGroupAbs>() 329 : replacementList; 330 } 331 @SuppressWarnings("unused") //Used by Hibernate 332 private List<CorrelationProductGroupAbs> getProdGrpList() 333 { 334 LOG.debug("Getting productGroup list; productGroups.size = "+productGroups.size()); 335 return productGroups; 336 } 337 338 /** 339 * Called after product groups were created from a persistent store, 340 * such as a database or XMl file. 341 */ 342 protected void createdProdGrpsFromPersistentStore() 343 { 344 LOG.debug("Setting container of productGroups; productGroups.size = "+productGroups.size()); 345 346 //Because of the way we have the Hibernate mappings configured, we must 347 //call the method below and not simply set the variable. 348 for (CorrelationProductGroupAbs p : productGroups) 349 p.setContainer(this); 350 } 351 352 //============================================================================ 353 // BASEBAND 354 //============================================================================ 355 356 /* (non-Javadoc) 357 * @see CorrelatorSubband#getBaseband() 358 */ 359 public CorrelatorBaseband getBaseband() { return baseband; } 360 361 /** Primarily for use by persistence mechanism. */ 362 protected void setContainer(CorrelatorBasebandAbs newContainer) 363 { 364 baseband = newContainer; 365 } 366 367 /** Primarily for use by persistence mechanism. */ 368 protected CorrelatorBasebandAbs getContainer() 369 { 370 return baseband; 371 } 372 373 //============================================================================ 374 // VALIDATION 375 //============================================================================ 376 377 //============================================================================ 378 // 379 //============================================================================ 380 381 /** 382 * Returns a copy of this subband. 383 * <p> 384 * The returned subband is <i>nearly</i>, but not quite, a deep copy of this 385 * subband. Properties that are not copied:</p> 386 * <ol> 387 * <li><b>id</b> - this is left in the <tt>UNDEFINED</tt> state.</li> 388 * <li><b>baseband</b> - this value will be <i>null</i>.</li> 389 * </ol> 390 * <p> 391 * If anything goes wrong during the cloning procedure, 392 * a {@code RuntimeException} will be thrown.</p> 393 */ 394 @Override 395 public CorrelatorSubbandAbs clone() 396 { 397 CorrelatorSubbandAbs clone = null; 398 399 try 400 { 401 //This line takes care of the primitive & immutable fields properly 402 clone = (CorrelatorSubbandAbs)super.clone(); 403 404 //We do NOT want the clone to have the same ID as the original. 405 //The ID is here for the persistence layer; it is in charge of 406 //setting IDs. To help it, we put the clone's ID in the uninitialized 407 //state. 408 clone.id = Identifiable.UNIDENTIFIED; 409 410 clone.baseband = null; 411 clone.freqRange = this.freqRange.clone(); 412 413 clone.productGroups = new ArrayList<CorrelationProductGroupAbs>(); 414 for (CorrelationProductGroupAbs thisGroup : this.productGroups) 415 { 416 CorrelationProductGroupAbs newGroup = makeCorrelationProductGroup(); 417 thisGroup.copyInto(newGroup); 418 clone.productGroups.add(newGroup); 419 } 420 } 421 catch (Exception ex) 422 { 423 throw new RuntimeException(ex); 424 } 425 426 return clone; 427 } 428 429 /** 430 * Returns <i>true</i> if <tt>o</tt> is equal to this subband. 431 * <p> 432 * Most, but not all, public attributes take place in the comparison 433 * Those that do not are:</p> 434 * <ol> 435 * <li>The ID.</li> 436 * <li>The containing baseband.</li> 437 * </ol> 438 */ 439 @Override 440 public boolean equals(Object o) 441 { 442 //Quick exit if o is this 443 if (o == this) 444 return true; 445 446 //Quick exit if o is null 447 if (o == null) 448 return false; 449 450 //Quick exit if classes are different 451 if (!o.getClass().equals(this.getClass())) 452 return false; 453 454 //A safe cast if we got this far 455 CorrelatorSubbandAbs other = (CorrelatorSubbandAbs)o; 456 457 //Attributes that we INTENTIONALLY DO NOT COMPARE: 458 // id, baseband, bwHelper 459 460 //Simple properties 461 if ((other.name == null && this.name != null) || 462 (other.name != null && this.name == null)) 463 return false; 464 465 if (other.name != null && !other.name.equals(this.name)) 466 return false; 467 468 if (other.requantizationBits != this.requantizationBits || 469 !other.freqRange.equals(this.freqRange)) 470 return false; 471 472 //Correlation products. This is an expensive test, so save for last. 473 if (other.productGroups.size() != this.productGroups.size()) 474 { 475 return false; 476 } 477 else //same # of product groups 478 { 479 for (CorrelationProductGroupAbs thisGroup : this.productGroups) 480 { 481 boolean found = false; 482 483 for (CorrelationProductGroupAbs otherGroup : other.productGroups) 484 { 485 if (otherGroup.equals(thisGroup)) 486 { 487 found = true; 488 break; 489 } 490 } 491 492 if (!found) 493 return false; 494 } 495 } 496 497 //No differences found 498 return true; 499 } 500 501 /** Returns a hash code value for this subband. */ 502 @Override 503 public int hashCode() 504 { 505 //Taken from the Effective Java book by Joshua Bloch. 506 //The constants 17 & 37 are arbitrary & carry no meaning. 507 int result = 17; 508 509 //You MUST keep this method in sync w/ the equals method 510 511 if (name != null) 512 result = 37 * result + name.hashCode(); 513 514 result = 37 * result + requantizationBits; 515 result = 37 * result + freqRange.hashCode(); 516 result = 37 * result + productGroups.hashCode(); 517 518 return result; 519 } 520 }