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 device that samples a signal to create a {@link DigitalSignal}. 010 * <p> 011 * <b>Version Info:</b> 012 * <table style="margin-left:2em"> 013 * <tr><td>$Revision: 1269 $</td></tr> 014 * <tr><td>$Date: 2008-05-05 14:39:39 -0600 (Mon, 05 May 2008) $</td></tr> 015 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 016 * </table></p> 017 * 018 * @author David M. Harland 019 * @since 2007-11-02 020 */ 021 public class SignalSampler 022 implements SignalProcessor, SignalSource 023 { 024 private String name; 025 026 private int bitsPerSample; 027 private Frequency samplingRate; 028 //TODO should there be an acceptable-input-range variable? 029 030 private SignalMixer virtualMixer; 031 032 private Signal inputSignal; 033 private DigitalSignal outputSignal; 034 035 private SignalPipe inputPipe; 036 private SignalPipe outputPipe; 037 038 /** 039 * Creates a new sampler that produces signals with the given properties. 040 * 041 * @param deviceName 042 * an optional name for this sampler. If this value is <i>null</i> 043 * a default name will be used in its place. 044 * 045 * @param numberOfBitsPerSample 046 * the number of bits in one sample of a digital signal produced by 047 * this device. 048 * 049 * @param samplesPerSecond 050 * the number of samples of a signal taken per second by this device. 051 */ 052 public SignalSampler(String deviceName, 053 int numberOfBitsPerSample, Frequency samplesPerSecond) 054 { 055 name = (deviceName == null) ? 056 numberOfBitsPerSample + "-bit-at-" + samplesPerSecond : deviceName; 057 058 bitsPerSample = numberOfBitsPerSample; 059 samplingRate = samplesPerSecond.clone(); 060 virtualMixer = SignalMixer.makeSubtractiveMixer(null); 061 inputSignal = null; 062 outputSignal = null; 063 inputPipe = SignalPipe.makeAndWeldOutputTo(this); 064 outputPipe = SignalPipe.makeAndWeldInputTo(this); 065 066 resetMixerLO(); 067 } 068 069 /** Call this whenever samplingRate changes. */ 070 private void resetMixerLO() 071 { 072 virtualMixer.getLocalOscillatorInputPipe().connectInputTo 073 ( 074 new SignalSource() 075 { 076 public Signal getSignal() 077 { 078 return new Signal(samplingRate); 079 } 080 } 081 ); 082 } 083 084 /** 085 * Sets the name of this device. 086 * <p> 087 * If {@code newName} is <i>null</i> or the empty string 088 * (<tt>""</tt>), the request to change the name will be 089 * denied and the current name will remain in place.</p> 090 * 091 * @param newName 092 * an optional name for this sampler. 093 */ 094 public void setName(String newName) 095 { 096 if (newName != name && newName.length() > 0) 097 name = newName; 098 } 099 100 /** 101 * Returns the name of this device. 102 * @return the name of this device. 103 */ 104 public String getName() 105 { 106 return name; 107 } 108 109 /** 110 * Returns the input pipe of this device. 111 * A typical usage pattern for this method is:<pre> 112 * mySampler.getInputPipe().connectInputTo(mySource);</pre> 113 * 114 * @return the input pipe of this device. 115 */ 116 public SignalPipe getInputPipe() 117 { 118 return inputPipe; 119 } 120 121 /** 122 * Returns the output pipe of this device. 123 * A typical usage pattern for this method is:<pre> 124 * mySampler.getOutputPipe().connectOutputTo(myProcessor);</pre> 125 * 126 * @return the output pipe of this device. 127 */ 128 public SignalPipe getOutputPipe() 129 { 130 return outputPipe; 131 } 132 133 /** 134 * Returns a copy of the input signal most recently {@link #execute() 135 * processed} by this device. 136 * If this processor has never been executed, or if the input signal 137 * it processed most recently was <i>null</i>, the returned value will 138 * be <i>null</i>. 139 * 140 * @return the input signal most recently sent into this sampler. 141 */ 142 public Signal getMostRecentInput() 143 { 144 return inputSignal == null ? null : inputSignal.clone(); 145 } 146 147 /** 148 * Returns a copy of the output signal most recently {@link #execute() 149 * produced} by this device. 150 * If this processor has never been executed, or if the input signal 151 * it processed most recently was <i>null</i>, the returned value will 152 * be <i>null</i>. 153 * 154 * @return the output signal most recently produced by this sampler. 155 */ 156 public DigitalSignal getMostRecentOutput() 157 { 158 return outputSignal == null ? null : outputSignal.clone(); 159 } 160 161 /** 162 * Erases this device's memory of its most recent execution. 163 * @see #getMostRecentInput() 164 * @see #getMostRecentOutput() 165 * @see #getSignal() 166 */ 167 public void eraseSignalMemory() 168 { 169 inputSignal = null; 170 outputSignal = null; 171 } 172 173 //============================================================================ 174 // INTERFACE SignalSource 175 //============================================================================ 176 177 /** 178 * Returns a copy of the signal produced by the most recent {@link #execute() 179 * execution} of this device. 180 * If this device has never been run, or if the input signal 181 * it processed most recently was <i>null</i>, the returned value will 182 * be <i>null</i>. 183 * <p> 184 * The returned signal may be safely cast to a {@code DigitalSignal}.</p> 185 */ 186 public Signal getSignal() 187 { 188 return outputSignal == null ? null : outputSignal.clone(); 189 } 190 191 //============================================================================ 192 // INTERFACE SignalProcessor AND SUPPORT THEREOF 193 //============================================================================ 194 195 /** 196 * Runs this device on the signal received from its input pipe and 197 * then executes the processor connected to its output pipe, if any. 198 * Both the input signal and the output produced by sampling it are 199 * remembered by this sampler and are available via the 200 * {@link #getMostRecentInput()} and {@link #getMostRecentOutput()} methods. 201 */ 202 public void execute() 203 { 204 updateOutputSignal(); 205 206 outputPipe.execute(); 207 } 208 209 public void executeUpTo(SignalProcessor firstUnexecutedDevice) 210 { 211 //Quick exit if execution should not occur 212 if (firstUnexecutedDevice == this) 213 return; 214 215 updateOutputSignal(); 216 217 //Tell text element of chain to do its work 218 outputPipe.executeUpTo(firstUnexecutedDevice); 219 } 220 221 private void updateOutputSignal() 222 { 223 //Do the work and remember results 224 inputSignal = inputPipe.getSignal(); 225 226 if (inputSignal != null) 227 { 228 outputSignal = sample(inputSignal); 229 230 if (!outputSignal.getDevicePath().endsWith(name)) 231 outputSignal.appendToDevicePath(name); 232 } 233 else 234 { 235 outputSignal = null; 236 } 237 } 238 239 public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice) 240 { 241 inputPipe.executeFromStartOfChainUpTo(firstUnexecutedDevice); 242 } 243 244 /** 245 * Creates and returns a new signal that is the result of applying this 246 * sampler to the given input signal. 247 * <p> 248 * This sampler alters the analog signal in the same way that a mixer 249 * connected to a local oscillator that is tuned to a frequency equal 250 * to that of the sampling frequency of this mixer would.</p> 251 * <p> 252 * Note that the {@code input} signal and this sampler are 253 * not affected by calls to this method. In other words this sampler 254 * will not remember the input signal sent in or the output 255 * signal produced. This is in contrast to a call to the 256 * {@link #execute()} methods.</p> 257 * 258 * @param input 259 * the signal to be sampled. 260 * 261 * @return a new digital signal that is the result of applying this sampler to 262 * {@code input}. 263 */ 264 public DigitalSignal sample(Signal input) 265 { 266 DigitalSignal output = null; 267 268 if (input != null) 269 { 270 Signal temp = null; 271 272 FrequencyRange inputRange = input.getCurrentRange(); 273 if (inputRange.getLowFrequency().getValue().compareTo(BigDecimal.ZERO)==0 && 274 inputRange.getHighFrequency().getValue().compareTo(BigDecimal.ZERO)==0) 275 { 276 temp = input.clone(); //A null-like frequency 277 } 278 else 279 { 280 temp = virtualMixer.mix(input); 281 } 282 output = new DigitalSignal(temp, samplingRate, bitsPerSample); 283 } 284 285 return output; 286 } 287 }