001 package edu.nrao.sss.electronics; 002 003 import java.math.BigDecimal; 004 005 import edu.nrao.sss.measure.Frequency; 006 import edu.nrao.sss.measure.FrequencyRange; 007 008 /** 009 * A signal path that begins with a local oscillator and contains 010 * an optional multiplier and optional output filter. 011 * <p> 012 * <b>Version Info:</b> 013 * <table style="margin-left:2em"> 014 * <tr><td>$Revision: 1693 $</td></tr> 015 * <tr><td>$Date: 2008-11-06 11:56:39 -0700 (Thu, 06 Nov 2008) $</td></tr> 016 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 017 * </table></p> 018 * 019 * @author David M. Harland 020 * @since 2007-11-07 021 */ 022 public class LocalOscillatorPath //TODO method comments for whole class 023 implements SignalSource 024 { 025 private SignalSource signalSource; 026 private LocalOscillator oscillator; 027 private SignalMultiplier multiplier; 028 private SignalFilter filter; 029 030 /** 031 * Creates a new local oscillator path. 032 * 033 * @param localOsc 034 * this path's source of signal. This is usually a 035 * {@link LocalOscillator}, but does not have to be. 036 * A value of <i>null</i> is permitted, but will render this 037 * object nearly useless, as most of its methods will return 038 * <i>null</i>. 039 * 040 * @param loMultiplier 041 * the amount by which the signal from {@code localOsc} should 042 * be multiplied. A value of <i>null</i> indicates that no 043 * multiplication should occur. 044 * 045 * @param outputFilter 046 * the filter through which the multiplied signal (if any 047 * multiplication occurs) is passed. A value of <i>null</i> 048 * indicates that no filtering should occur. 049 */ 050 public LocalOscillatorPath(SignalSource localOsc, 051 BigDecimal loMultiplier, 052 FrequencyRange outputFilter) 053 { 054 //TODO err if loMult <= 0.0 055 056 signalSource = localOsc; //OK if null 057 058 oscillator = 059 (localOsc instanceof LocalOscillator) ? (LocalOscillator)signalSource 060 : null; 061 multiplier = 062 loMultiplier == null ? null : new SignalMultiplier(null, loMultiplier); 063 064 filter = 065 outputFilter == null ? null : SignalFilter.make(null, outputFilter); 066 } 067 068 /** 069 * Creates a new local oscillator path. 070 * 071 * @param localOsc 072 * this path's source of signal. This is usually a 073 * {@link LocalOscillator}, but does not have to be. 074 * A value of <i>null</i> is permitted, but will render this 075 * object nearly useless, as most of its methods will return 076 * <i>null</i>. 077 * 078 * @param loMultiplier 079 * the amount by which the signal from {@code localOsc} should 080 * be multiplied. 081 * 082 * @param outputFilter 083 * the filter through which the multiplied signal (if any 084 * multiplication occurs) is passed. A value of <i>null</i> 085 * indicates that no filtering should occur. 086 */ 087 public LocalOscillatorPath(SignalSource localOsc, 088 int loMultiplier, 089 FrequencyRange outputFilter) 090 { 091 this(localOsc, new BigDecimal(loMultiplier), outputFilter); 092 } 093 094 /** 095 * Returns the value to which this path's source of signal should be 096 * tuned in order to move one frequency to another. This method 097 * takes into account the multiplier and filter on this path. 098 * It attempts to reach {@code to} by subtracting {@code from} from 099 * its multiplied signal. 100 * <p><i> 101 * (TODO: not yet handling filter. What if desired tuning doesn't 102 * pass through filter? Exception? Return null? Return 0.0Hz?)</i></p> 103 * <p><i> 104 * (TODO: same issue if subtraction can not accomplish goal.)</i></p> 105 * 106 * @param from 107 * the frequency to be shifted. 108 * 109 * @param to 110 * the result of shifting {@code from} using this path's LO 111 * and multiplier. 112 * 113 * @return the frequency to which this path's source of signal (usually 114 * a local oscillator) should be tuned in order to convert 115 * {@code from} to {@code to}. 116 */ 117 public Frequency getLOFreqToMoveCenterViaSubtraction(Frequency from, 118 Frequency to) 119 { 120 //At this point we are calculating the path output 121 Frequency loTuning = to.clone().add(from); 122 123 //Can pathOutput pass through filter? 124 //TODO handle outside-of-filter condition 125 126 //Now we turn path output into LO tuning 127 loTuning.divideBy(multiplier.getMultiplicationFactor()); 128 129 return loTuning; 130 } 131 132 133 /** 134 * Returns the value to which this path's source of signal should be 135 * tuned in order to move one frequency to another. This method 136 * takes into account the multiplier and filter on this path. 137 * It attempts to reach {@code to} by adding its multiplied signal 138 * to {@code from}. 139 * <p><i> 140 * (TODO: not yet handling filter. What if desired tuning doesn't 141 * pass through filter? Exception? Return null? Return 0.0Hz?)</i></p> 142 * <p><i> 143 * (TODO: same issue if addition can not accomplish goal.)</i></p> 144 * 145 * @param from 146 * the frequency to be shifted. 147 * 148 * @param to 149 * the result of shifting {@code from} using this path's LO 150 * and multiplier. 151 * 152 * @return the frequency to which this path's source of signal (usually 153 * a local oscillator) should be tuned in order to convert 154 * {@code from} to {@code to}. 155 */ 156 public Frequency getLOFreqToMoveCenterViaAddition(Frequency from, 157 Frequency to) 158 { 159 //At this point we are calculating the path output 160 Frequency loTuning = to.clone().subtract(from); 161 162 //Can pathOutput pass through filter? 163 //TODO handle outside-of-filter condition 164 165 //Now we turn path output into LO tuning 166 loTuning.divideBy(multiplier.getMultiplicationFactor()); 167 168 return loTuning; 169 } 170 171 /** 172 * Sets this path's local oscillator to a frequency that will convert 173 * {@code from} to {@code to} via subtraction. 174 * <p> 175 * This method accomplishes its task by first calling 176 * {@link #getLOFreqToMoveCenterViaSubtraction(Frequency, Frequency)}, 177 * so all the caveats for that method apply here. 178 * In addition, if this path's source of signal is not an LO, 179 * no tuning can be performed.</p> 180 * 181 * @param from 182 * the frequency to be shifted. 183 * 184 * @param to 185 * the result of shifting {@code from} using this path's LO 186 * and multiplier. 187 */ 188 public void setLOToMoveCenterViaSubtraction(Frequency from, Frequency to) 189 { 190 if (oscillator != null) 191 oscillator.tuneTo(getLOFreqToMoveCenterViaSubtraction(from, to)); 192 } 193 194 /** 195 * Sets this path's local oscillator to a frequency that will convert 196 * {@code from} to {@code to} via addition. 197 * <p> 198 * This method accomplishes its task by first calling 199 * {@link #getLOFreqToMoveCenterViaAddition(Frequency, Frequency)}, 200 * so all the caveats for that method apply here. 201 * In addition, if this path's source of signal is not an LO, 202 * no tuning can be performed.</p> 203 * 204 * @param from 205 * the frequency to be shifted. 206 * 207 * @param to 208 * the result of shifting {@code from} using this path's LO 209 * and multiplier. 210 */ 211 public void setLOToMoveCenterViaAddition(Frequency from, Frequency to) 212 { 213 if (oscillator != null) 214 oscillator.tuneTo(getLOFreqToMoveCenterViaAddition(from, to)); 215 } 216 217 /** 218 * Returns the signal produced by this local oscillator path. 219 * This is normally a narrow-band signal that was produced by an LO, 220 * optionally multiplied, and optionally filtered. 221 * <p> 222 * The returned value will be <i>null</i> if this path has no source, 223 * or if its source produces a <i>null</i> signal.</p> 224 * 225 * @return the signal produced by this local oscillator path. 226 */ 227 public Signal getSignal() 228 { 229 Signal signal = null; 230 231 if (signalSource != null) 232 { 233 signal = signalSource.getSignal(); 234 235 if (signal != null) 236 { 237 if (multiplier != null) 238 signal = multiplier.multiply(signal); 239 240 if (filter != null) 241 signal = filter.filter(signal); 242 } 243 } 244 245 return signal; 246 } 247 248 /** 249 * Returns the multiplication factor used on the local oscillator's signal. 250 * 251 * @return the multiplier used by this LO path. 252 * 253 * @since 2008-11-06 254 */ 255 public BigDecimal getMultiplier() 256 { 257 return multiplier == null ? BigDecimal.ONE : multiplier.getMultiplicationFactor(); 258 } 259 260 /** 261 * Returns the broadest possible range the signal from for this LO path. 262 * Note that the LO attached to this path might not actually be able 263 * to achieve the lower or upper most values if it has discrete 264 * tuning steps. 265 * 266 * @return the broadest possible range the signal from for this LO path. 267 * 268 * @since 2008-11-05 269 */ 270 public FrequencyRange getOutputRange() 271 { 272 //Infinite range to start 273 FrequencyRange output = new FrequencyRange(); 274 275 //If we have an LO, use its range 276 if (oscillator != null) 277 output = oscillator.getTunableRange(); 278 279 //Use multiplier, if we have one 280 if (multiplier != null && !output.getWidth().isInfinite()) 281 output.multiplyBy(multiplier.getMultiplicationFactor()); 282 283 //If there is a filter, intersect it with the current range. 284 //If there is no overlap of these ranges, the output will have zero width. 285 if (filter != null) 286 output.intersectWith(filter.getPassBand()); 287 288 return output; 289 } 290 291 //============================================================================ 292 // 293 //============================================================================ 294 /* 295 public static void main(String[] args) throws Exception 296 { 297 Frequency low = new Frequency(11904.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ); 298 Frequency high = new Frequency(20352.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ); 299 Frequency step = new Frequency( 256.0, edu.nrao.sss.measure.FrequencyUnits.MEGAHERTZ); 300 301 LocalOscillator locOsc = new LocalOscillator(new FrequencyRange(low,high),step); 302 303 FrequencyRange band = new FrequencyRange(new Frequency(18.0), new Frequency(26.0)); 304 305 Frequency newCenter = new Frequency(10.0); 306 307 LocalOscillatorPath loPath = new LocalOscillatorPath(locOsc, 2.0, null); 308 309 loPath.setLOToMoveCenterViaSubtraction(band.getCenterFrequency(), newCenter); 310 311 System.out.println("LO is tuned to " + locOsc.getCurrentTuning()+"\n"); 312 313 Signal signal = new Signal(band, edu.nrao.sss.astronomy.PolarizationType.L); 314 System.out.println("INPUT"); 315 System.out.println(signal); 316 SignalMixer mixer = SignalMixer.makeSubtractiveMixer(); 317 mixer.getLocalOscillatorInputPipe().connectInputTo(loPath); 318 signal = mixer.mix(signal); 319 System.out.println("MIXED OUTPUT"); 320 System.out.println(signal); 321 322 System.out.println("CENTERS"); 323 System.out.println(" Desired: " + newCenter); 324 System.out.println(" Actual: " + signal.getCurrentRange().getCenterFrequency()); 325 } 326 */ 327 }