001 package edu.nrao.sss.electronics; 002 003 import edu.nrao.sss.math.MathUtil; 004 import edu.nrao.sss.measure.Frequency; 005 import edu.nrao.sss.measure.FrequencyRange; 006 007 /** 008 * A filter that operates on the current frequencies of {@link Signal signals}. 009 * Filters can only narrow the range of frequencies of the signals 010 * that pass through them. Filters perform no other transformations. 011 * When a filter narrows the current frequency range of a signal, it 012 * also narrows the proxied range in a consistent manner. 013 * <p> 014 * <b>Version Info:</b> 015 * <table style="margin-left:2em"> 016 * <tr><td>$Revision: 1241 $</td></tr> 017 * <tr><td>$Date: 2008-04-25 14:37:37 -0600 (Fri, 25 Apr 2008) $</td></tr> 018 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 019 * </table></p> 020 * 021 * @author David M. Harland 022 * @since 2007-10-24 023 */ 024 public class SignalFilter 025 implements SignalProcessor, SignalSource 026 { 027 private String name; 028 029 private FrequencyRange passBand; 030 031 private Signal inputSignal; 032 private Signal outputSignal; 033 034 private SignalPipe inputPipe; 035 private SignalPipe outputPipe; 036 037 //============================================================================ 038 // OBJECT CREATION 039 //============================================================================ 040 041 /** Makes a new filter with the given pass band. */ 042 private SignalFilter(String deviceName, FrequencyRange allowableFrequencies) 043 { 044 name = (deviceName == null) ? allowableFrequencies.toString() 045 : deviceName; 046 047 passBand = allowableFrequencies; 048 inputSignal = null; 049 outputSignal = null; 050 inputPipe = SignalPipe.makeAndWeldOutputTo(this); 051 outputPipe = SignalPipe.makeAndWeldInputTo(this); 052 } 053 054 /** 055 * Creates and returns a new filter that blocks all frequencies 056 * below {@code minFreqGHz}. 057 * 058 * @param deviceName 059 * an optional name for this filter. If this value is <i>null</i> 060 * a default name will be used in its place. 061 * 062 * @param minFreqGHz 063 * the lowest frequency, in gigahertz, that may pass through this filter. 064 * 065 * @return a new high pass filter. 066 */ 067 public static SignalFilter makeHighPass(String deviceName, String minFreqGHz) 068 { 069 return new SignalFilter(deviceName, 070 new FrequencyRange(new Frequency(minFreqGHz), 071 new Frequency(MathUtil.POSITIVE_INFINITY))); 072 } 073 074 /** 075 * Creates and returns a new filter that blocks all frequencies 076 * above {@code maxFreqGHz}. 077 * 078 * @param deviceName 079 * an optional name for this filter. If this value is <i>null</i> 080 * a default name will be used in its place. 081 * 082 * @param maxFreqGHz 083 * the highest frequency, in gigahertz, that may pass through this filter. 084 * 085 * @return a new low pass filter. 086 */ 087 public static SignalFilter makeLowPass(String deviceName, String maxFreqGHz) 088 { 089 return new SignalFilter(deviceName, 090 new FrequencyRange(new Frequency("0.0"), 091 new Frequency(maxFreqGHz))); 092 } 093 094 /** 095 * Creates and returns a new filter that blocks all frequencies 096 * outside the given range. 097 * 098 * @param deviceName 099 * an optional name for this filter. If this value is <i>null</i> 100 * a default name will be used in its place. 101 * 102 * @param minFreqGHz 103 * the lowest frequency, in gigahertz, that may pass through this filter. 104 * 105 * @param maxFreqGHz 106 * the highest frequency, in gigahertz, that may pass through this filter. 107 * 108 * @return a new filter. 109 */ 110 public static SignalFilter make(String deviceName, 111 String minFreqGHz, String maxFreqGHz) 112 { 113 return new SignalFilter(deviceName, 114 new FrequencyRange(new Frequency(minFreqGHz), 115 new Frequency(maxFreqGHz))); 116 } 117 118 /** 119 * Creates and returns a new filter that blocks all frequencies 120 * outside the given range. 121 * 122 * @param deviceName 123 * an optional name for this filter. If this value is <i>null</i> 124 * a default name will be used in its place. 125 * 126 * @param passingRange 127 * the range of frequencies that may pass through this filter. 128 * The endpoints of this range may both pass through this filter. 129 * 130 * @return a new filter. 131 */ 132 public static SignalFilter make(String deviceName, 133 FrequencyRange passingRange) 134 { 135 return new SignalFilter(deviceName, passingRange.clone()); 136 } 137 138 //============================================================================ 139 // 140 //============================================================================ 141 142 /** 143 * Returns the range of frequencies that may pass through this filter. 144 * @return the range of frequencies that may pass through this filter. 145 */ 146 public FrequencyRange getPassBand() 147 { 148 return passBand.clone(); 149 } 150 151 /** 152 * Returns the input pipe of this device. 153 * A typical usage pattern for this method is:<pre> 154 * myFilter.getInputPipe().connectInputTo(mySource);</pre> 155 * 156 * @return the input pipe of this device. 157 */ 158 public SignalPipe getInputPipe() 159 { 160 return inputPipe; 161 } 162 163 /** 164 * Returns the output pipe of this device. 165 * A typical usage pattern for this method is:<pre> 166 * myFilter.getOutputPipe().connectOutputTo(myProcessor);</pre> 167 * 168 * @return the output pipe of this device. 169 */ 170 public SignalPipe getOutputPipe() 171 { 172 return outputPipe; 173 } 174 175 /** 176 * Returns a copy of the input signal most recently {@link #execute() 177 * processed} by this device. 178 * If this processor has never been executed, or if the input signal 179 * it processed most recently was <i>null</i>, the returned value will 180 * be <i>null</i>. 181 * 182 * @return the input signal most recently sent into this filter. 183 */ 184 public Signal getMostRecentInput() 185 { 186 return inputSignal == null ? null : inputSignal.clone(); 187 } 188 189 /** 190 * Returns a copy of the output signal most recently {@link #execute() 191 * produced} by this device. 192 * If this processor has never been executed, or if the input signal 193 * it processed most recently was <i>null</i>, the returned value will 194 * be <i>null</i>. 195 * 196 * @return the output signal most recently produced by this filter. 197 */ 198 public Signal getMostRecentOutput() 199 { 200 return outputSignal == null ? null : outputSignal.clone(); 201 } 202 203 /** 204 * Erases this device's memory of its most recent execution. 205 * @see #getMostRecentInput() 206 * @see #getMostRecentOutput() 207 * @see #getSignal() 208 */ 209 public void eraseSignalMemory() 210 { 211 inputSignal = null; 212 outputSignal = null; 213 } 214 215 //============================================================================ 216 // INTERFACE SignalSource 217 //============================================================================ 218 219 /** 220 * Returns a copy of the signal produced by the most recent {@link #execute() 221 * execution} of this device. 222 * If this device has never been run, or if the input signal 223 * it processed most recently was <i>null</i>, the returned value will 224 * be <i>null</i>. 225 */ 226 public Signal getSignal() 227 { 228 return outputSignal == null ? null : outputSignal.clone(); 229 } 230 231 //============================================================================ 232 // INTERFACE SignalProcessor AND SUPPORT THEREOF 233 //============================================================================ 234 235 /** 236 * Runs this device on the signal received from its input pipe and 237 * then executes the processor connected to its output pipe, if any. 238 * Both the input signal and the output produced by filtering it are 239 * remembered by this filter and are available via the 240 * {@link #getMostRecentInput()} and {@link #getMostRecentOutput()} methods. 241 */ 242 public void execute() 243 { 244 updateOutputSignal(); 245 246 outputPipe.execute(); 247 } 248 249 public void executeUpTo(SignalProcessor firstUnexecutedDevice) 250 { 251 //Quick exit if execution should not occur 252 if (firstUnexecutedDevice == this) 253 return; 254 255 updateOutputSignal(); 256 257 //Tell text element of chain to do its work 258 outputPipe.executeUpTo(firstUnexecutedDevice); 259 } 260 261 private void updateOutputSignal() 262 { 263 //Do the work and remember results 264 inputSignal = inputPipe.getSignal(); 265 266 if (inputSignal != null) 267 { 268 outputSignal = filter(inputSignal); 269 270 if (!outputSignal.getDevicePath().endsWith(name)) 271 outputSignal.appendToDevicePath(name); 272 } 273 else 274 { 275 outputSignal = null; 276 } 277 } 278 279 public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice) 280 { 281 inputPipe.executeFromStartOfChainUpTo(firstUnexecutedDevice); 282 } 283 284 /** 285 * Creates and returns a new signal that is the result of applying this 286 * filter to the given input signal. 287 * <p> 288 * Note that the {@code input} signal and this filter are 289 * not affected by calls to this method. In other words this filter 290 * will not remember the input signal sent in or the output 291 * signal produced. This is in contrast to a call to the 292 * {@link #execute()} methods.</p> 293 * 294 * @param input 295 * the signal to be filtered. 296 * 297 * @return a new signal that is the result of applying this filter to 298 * {@code input}. 299 */ 300 public Signal filter(Signal input) 301 { 302 Signal output = input.clone(); 303 304 FrequencyRange currRange = input.getCurrentRange(); 305 306 //If whole range cannot pass through filter, take intersection. 307 //If whole range does pass, we just return clone of input. 308 if (!passBand.contains(currRange)) 309 { 310 FrequencyRange newRange = currRange.clone().intersectWith(passBand); 311 312 //If there is NO intersection, the entire signal is blocked and will 313 //be made null-like. 314 if (newRange.getWidth().getValue().signum() == 0) 315 { 316 output.nullify(); 317 } 318 //If there is some intersection, create new proxied range by mapping 319 //low and high frequencies of new current range. 320 else 321 { 322 FrequencyRange newProxiedRange = new FrequencyRange 323 ( 324 input.getProxiedFrequencyFor(newRange.getLowFrequency()), 325 input.getProxiedFrequencyFor(newRange.getHighFrequency()) 326 ); 327 328 output.transform(newRange, newProxiedRange, false); 329 } 330 } 331 332 return output; 333 } 334 }