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.XmlElement; 007 import javax.xml.bind.annotation.XmlTransient; 008 import javax.xml.bind.annotation.XmlType; 009 010 import org.apache.log4j.Logger; 011 012 import edu.nrao.sss.electronics.Signal; 013 import edu.nrao.sss.measure.Frequency; 014 import edu.nrao.sss.measure.FrequencyRange; 015 import edu.nrao.sss.util.Identifiable; 016 017 /** 018 * A partial implementation of a {@link CorrelatorBaseband}. 019 * <p> 020 * <b>Version Info:</b> 021 * <table style="margin-left:2em"> 022 * <tr><td>$Revision: 2289 $</td></tr> 023 * <tr><td>$Date: 2009-05-07 16:13:41 -0600 (Thu, 07 May 2009) $</td></tr> 024 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 025 * </table></p> 026 * 027 * @author David M. Harland 028 * @since 2008-03-06 029 */ 030 @XmlType(propOrder={"bandwidth" 031 }) 032 public abstract class CorrelatorBasebandAbs 033 implements CorrelatorBaseband 034 { 035 private static final Logger LOG = Logger.getLogger(CorrelatorBasebandAbs.class); 036 037 private Long id; //A unique identifier for the persistence layer. 038 039 @XmlElement protected Frequency bandwidth; 040 041 private BandwidthHelper bwHelper; 042 protected List<CorrelatorSubbandAbs> subbands; 043 044 /** Helps create a new correlator baseband. */ 045 protected CorrelatorBasebandAbs() 046 { 047 id = Identifiable.UNIDENTIFIED; 048 bandwidth = new Frequency("0.0"); 049 bwHelper = new BandwidthHelper(this); 050 subbands = new ArrayList<CorrelatorSubbandAbs>(); 051 } 052 053 //============================================================================ 054 // IDENTIFICATION 055 //============================================================================ 056 057 /* (non-Javadoc) 058 * @see edu.nrao.sss.util.Identifiable#getId() 059 */ 060 public Long getId() { return id; } 061 062 @SuppressWarnings("unused") 063 private void setId(Long id) { this.id = id; } 064 065 /* (non-Javadoc) 066 * @see edu.nrao.sss.model.resource.CorrelatorBaseband#clearId() 067 */ 068 public void clearId() 069 { 070 id = Identifiable.UNIDENTIFIED; 071 072 for (CorrelatorSubbandAbs sb : subbands) 073 sb.clearId(); 074 } 075 076 public boolean isPair() { return !isSinglet(); } 077 078 //============================================================================ 079 // CONTAINER 080 //============================================================================ 081 082 /** 083 * Returns the correlator configuration that holds this baseband. 084 * It is possible for the returned value to be <i>null</i>. 085 */ 086 protected abstract CorrelatorConfiguration getContainer(); 087 088 /** 089 * Sets the correlator configuration to which this baseband belongs. 090 * The {@code newContainer} is allowed to be <i>null</i>. 091 */ 092 protected abstract void setContainer(CorrelatorConfiguration newContainer); 093 094 //============================================================================ 095 // FREQUENCY 096 //============================================================================ 097 098 /* (non-Javadoc) 099 * @see CorrelatorBaseband#getAllowableBandwidthFor(Frequency) 100 */ 101 public Frequency getAllowableBandwidthFor(Frequency frequency) 102 { 103 return bwHelper.getAllowableBandwidthFor(frequency); 104 } 105 106 /* (non-Javadoc) 107 * @see CorrelatorBaseband#getAllowableBandwidthClosestTo(Frequency) 108 */ 109 public Frequency getAllowableBandwidthClosestTo(Frequency frequency) 110 { 111 return bwHelper.getAllowableBandwidthClosestTo(frequency); 112 } 113 114 /* (non-Javadoc) 115 * @see CorrelatorBaseband#setBandwidth(Frequency) 116 */ 117 public void setBandwidth(Frequency newWidth) 118 { 119 if (newWidth == null) 120 throw new IllegalArgumentException( 121 "Cannot set bandwidth of baseband to NULL."); 122 123 bandwidth = getAllowableBandwidthClosestTo(newWidth); 124 } 125 126 /** 127 * Returns a copy of the bandwidth of this baseband. 128 * @return a copy of the bandwidth of this baseband. 129 */ 130 @XmlTransient 131 public Frequency getBandwidth() 132 { 133 return bandwidth.clone(); 134 } 135 136 /** 137 * Returns the original frequency range represented by {@code signal} and 138 * the smaller of the signal's bandwidth and the bandwidth of this baseband. 139 * 140 * @param signal 141 * a signal that may have been manipulated in such a way that its frequency 142 * range is no longer the same as it had been when the signal was created. 143 * 144 * @return 145 * the original frequency range represented by {@code signal}. 146 */ 147 protected FrequencyRange getProxiedRange(Signal signal) 148 { 149 FrequencyRange proxied = signal.getProxiedRange(); 150 Frequency signalBw = proxied.getWidth(); 151 152 //If the bandwidth of this baseband and the proxy signal are the 153 //same, just get the proxied range from the input. Otherwise we 154 //need to construct the range ourself. 155 if (!bandwidth.equals(signalBw)) 156 { 157 Frequency bw = (signalBw.compareTo(bandwidth) <= 0) ? signalBw : bandwidth; 158 159 //We use the terms "left" and "right", below, because the proxied 160 //frequency range and the baseband may be flipped with respect to 161 //direction of increasing frequencies. E.g., a baseband of 162 //0 - 2 GHz might represent 44 - 42 GHz, in that order. If we're 163 //using a baseband bandwidth != signal band width, we need to 164 //know whether the 0 GHz, or left, frequency of the BB represents the 165 //low or the high of the signal. 166 Frequency left, right; 167 168 if (signal.proxiedRangeIsReversed()) 169 { 170 left = proxied.getHighFrequency(); 171 right = left.clone().subtract(bw); 172 } 173 else //"normal" 174 { 175 left = proxied.getLowFrequency(); 176 right = left.clone().add(bw); 177 } 178 179 proxied = new FrequencyRange(left, right); 180 } 181 182 return proxied; 183 } 184 185 //============================================================================ 186 // SUBBAND 187 //============================================================================ 188 189 public int getSubbandCount() 190 { 191 return subbands.size(); 192 } 193 194 public List<CorrelatorSubband> getSubbands() 195 { 196 return new ArrayList<CorrelatorSubband>(subbands); 197 } 198 199 /** 200 * The {@link CorrelatorBaseband#addSubband(CorrelatorSubband)} method 201 * of classes that extend this one should call this method. 202 * This method will: 203 * <ol> 204 * <li>Ensure that this baseband does not yet hold its maximum number 205 * of subbands. If it does, an {@code IllegalArgumentException} 206 * is thrown.</li> 207 * <li>Remove {@code newSubband} from its former baseband, if any.</li> 208 * <li>Inform {@code newSubband} that <i>this</i> is its new baseband.</li> 209 * <li>Add {@code newSubband} to this baseband's collection of subbands.</li> 210 * </ol> 211 * The main duty of an extending class is to ensure that the type of subband 212 * passed to it is of an acceptable type. For example, an <tt>XyzBaseband</tt> 213 * may want to accept only <tt>XyzSubband</tt> instances. 214 * 215 * @param newSubband 216 * a new subband for this baseband. 217 */ 218 protected void addNewSubband(CorrelatorSubbandAbs newSubband) 219 { 220 if (getSubbandCount() >= getMaxSubbandCount()) 221 throw new IllegalArgumentException("Baseband " + getName() + 222 " already has its maximum of " + getMaxSubbandCount() + 223 " subbands. This subband was not added."); 224 225 //If the incoming subband is associated with a different baseband, 226 //break that connection first. 227 CorrelatorBasebandAbs oldBb = newSubband.getContainer(); 228 if (oldBb != null) 229 oldBb.removeSubband(newSubband); 230 231 newSubband.setContainer(this); 232 233 subbands.add(newSubband); 234 } 235 236 public boolean removeSubband(CorrelatorSubband unwantedSubband) 237 { 238 if (unwantedSubband instanceof CorrelatorSubbandAbs) 239 return removeOldSubband((CorrelatorSubbandAbs)unwantedSubband); 240 else 241 return false; 242 } 243 244 protected boolean removeOldSubband(CorrelatorSubbandAbs unwantedSubband) 245 { 246 boolean removed = subbands.remove(unwantedSubband); 247 248 if (removed) 249 unwantedSubband.setContainer(null); 250 251 return removed; 252 } 253 254 public CorrelatorSubbandAbs removeSubbandAt(int index) 255 { 256 CorrelatorSubbandAbs removedSubband = null; 257 258 if (index >= 0 && index < subbands.size()) 259 { 260 removedSubband = subbands.remove(index); 261 removedSubband.setContainer(null); 262 } 263 264 return removedSubband; 265 } 266 267 public int removeAllSubbands() 268 { 269 int oldCount = subbands.size(); 270 271 subbands.clear(); 272 273 return oldCount; 274 } 275 276 @SuppressWarnings("unused") //Used by Hibernate 277 private void setSubbandList(List<CorrelatorSubbandAbs> replacementList) 278 { 279 LOG.debug("Setting subband list. Size = "+(replacementList==null?0:replacementList.size())); 280 281 subbands = 282 replacementList == null ? new ArrayList<CorrelatorSubbandAbs>() 283 : replacementList; 284 } 285 @SuppressWarnings("unused") //Used by Hibernate 286 private List<CorrelatorSubbandAbs> getSubbandList() 287 { 288 LOG.debug("Getting subband list; subbands.size = "+subbands.size()); 289 return subbands; 290 } 291 292 /** 293 * Called after subbands were created from a persistent store, 294 * such as a database or XMl file. 295 */ 296 protected void createdSubbandsFromPersistentStore() 297 { 298 LOG.debug("Setting container of subbands; subbands.size = "+subbands.size()); 299 300 //Because of the way we have the Hibernate mappings configured, we must 301 //call the method below and not simply set the variable. 302 for (CorrelatorSubbandAbs s : subbands) 303 s.setContainer(this); 304 } 305 306 //============================================================================ 307 // 308 //============================================================================ 309 310 @Override 311 public String toString() 312 { 313 StringBuilder buff = new StringBuilder(); 314 final String PAIR_SEP = "; "; 315 final String NV_SEP = " = "; 316 317 buff.append("name").append(NV_SEP).append(getName()).append(PAIR_SEP); 318 buff.append("polarizations").append(NV_SEP).append(getPolarizations()).append(PAIR_SEP); 319 buff.append("bb.BW").append(NV_SEP).append(getBandwidth()).append(PAIR_SEP); 320 buff.append("proxiedRange").append(NV_SEP).append(getProxiedRange()).append(PAIR_SEP); 321 322 return buff.toString(); 323 } 324 325 /** 326 * Returns a copy of this baseband. 327 * <p> 328 * If anything goes wrong during the cloning procedure, 329 * a {@code RuntimeException} will be thrown.</p> 330 */ 331 @Override 332 public CorrelatorBasebandAbs clone() 333 { 334 CorrelatorBasebandAbs clone = null; 335 336 try 337 { 338 //This line takes care of the primitive & immutable fields properly 339 clone = (CorrelatorBasebandAbs)super.clone(); 340 341 //We do NOT want the clone to have the same ID as the original. 342 //The ID is here for the persistence layer; it is in charge of 343 //setting IDs. To help it, we put the clone's ID in the uninitialized 344 //state. 345 clone.id = Identifiable.UNIDENTIFIED; 346 347 clone.bandwidth = this.bandwidth.clone(); 348 clone.bwHelper = new BandwidthHelper(clone); 349 350 //Clone individual subbands and use method to add to clone's list 351 clone.subbands = new ArrayList<CorrelatorSubbandAbs>(); 352 for (CorrelatorSubbandAbs sb : this.subbands) 353 clone.addNewSubband(sb.clone()); 354 } 355 catch (Exception ex) 356 { 357 throw new RuntimeException(ex); 358 } 359 360 return clone; 361 } 362 363 /** 364 * Returns <i>true</i> if <tt>o</tt> is equal to this baseband. 365 * <p> 366 * Most, but not all, public attributes take place in the comparison 367 * Those that do not are:</p> 368 * <ol> 369 * <li>The ID.</li> 370 * </ol> 371 */ 372 @Override 373 //TODO do we want value equality? 374 public boolean equals(Object o) 375 { 376 //Quick exit if o is this 377 if (o == this) 378 return true; 379 380 //Quick exit if o is null 381 if (o == null) 382 return false; 383 384 //Quick exit if classes are different 385 if (!o.getClass().equals(this.getClass())) 386 return false; 387 388 //A safe cast if we got this far 389 CorrelatorBasebandAbs other = (CorrelatorBasebandAbs)o; 390 391 //Attributes that we INTENTIONALLY DO NOT COMPARE: 392 // id, bwHelper 393 394 return 395 other.bandwidth.equals(this.bandwidth) && 396 other.subbands.equals(this.subbands); 397 } 398 399 /** Returns a hash code value for this baseband. */ 400 @Override 401 //TODO remove? 402 public int hashCode() 403 { 404 //Taken from the Effective Java book by Joshua Bloch. 405 //The constants 17 & 37 are arbitrary & carry no meaning. 406 int result = 17; 407 408 //You MUST keep this method in sync w/ the equals method 409 410 result = 37 * result + bandwidth.hashCode(); 411 result = 37 * result + subbands.hashCode(); 412 413 return result; 414 } 415 }