001 package util.wavelet;
002 import java.util.List;
003 import static java.lang.Math.*;
004
005 /** A wrapper for an array of signal values and boundary conditions check.
006 * @author John Talbot
007 */
008 public class SimpleSignal {
009
010 /** Signal value array. */
011 float[] values;
012
013 /**
014 * Extrapolation methodology for computing signal values for out of range positions.
015 * Default is mirror boundary.
016 */
017 BoundaryCondition boundaryCondition = BoundaryCondition.Mirror;
018
019 /**
020 * Construct a signal using the given array of signal values and default boundary conditions.
021 * @param values an array of signal values
022 */
023 public SimpleSignal(float[] values) {
024 this.values = values;
025 }
026
027 /**
028 * Construct a signal using the given array of signal values and boundary conditions.
029 * @param values an array of signal values
030 * @param boundaryCondition the boundary condition
031 */
032 public SimpleSignal(float[] values, BoundaryCondition boundaryCondition) {
033 this(values);
034 this.boundaryCondition = boundaryCondition;
035 }
036
037 /**
038 * Copy constructor.
039 * @param signal to be cloned
040 */
041 public SimpleSignal(SimpleSignal signal) {
042 this.boundaryCondition = signal.getBoundaryCondition();
043 values = new float[signal.size()];
044 System.arraycopy(signal.values, 0, values, 0, signal.size());
045 }
046
047 /**
048 * Construct a zero filled signal using the size and default boundary conditions.
049 * @param size the size of the signal
050 */
051 public SimpleSignal(int size) {
052 this(new float[size]);
053 }
054
055 /**
056 * Construct a zero filled signal using the size and a boundary condition.
057 * @param size the size of the signal
058 * @param boundaryCondition the boundary condition
059 */
060 public SimpleSignal(int size, BoundaryCondition boundaryCondition) {
061 this(size);
062 this.boundaryCondition = boundaryCondition;
063 }
064
065 /**
066 * Convolve this signal with a kernel. Not efficient enough for atrous DWT.
067 * For symmetric convolutions use only odd sized kernels.
068 * @param kernel the convolution kernel
069 */
070 public SimpleSignal convolve(SimpleSignal kernel) {
071 int offset = kernel.size() / 2;
072 for (int i = 0; i < size(); i++) {
073 float sum = 0;
074 for (int j = 0; j < kernel.size(); j++)
075 sum += getValue(i + j - offset) * kernel.getValue(j);
076 values[i] = sum;
077 }
078 return this;
079 }
080
081 /**
082 * Get the boundary condition (extrapolation methodology) for this signal.
083 * @return the boundary condition;
084 */
085 public BoundaryCondition getBoundaryCondition() {
086 return boundaryCondition;
087 }
088
089 /**
090 * Set the boundary condition (extrapolation methodology) for this signal.
091 * @param boundaryCondition the boundary condition
092 */
093 public void setBoundaryCondition(BoundaryCondition boundaryCondition) {
094 this.boundaryCondition = boundaryCondition;
095 }
096
097 /**
098 * Get the size of this signal.
099 * @return size of the signal
100 */
101 public int size() {
102 return values.length;
103 }
104
105 /**
106 * Get the signal value at the given position and deal with boundary conditions when position is out of range.
107 * @param position the position
108 * @return value at the given position or extrapolated value if position is out of range
109 * @see #setValue
110 */
111 public float getValue(int position) {
112 return values[boundaryCondition.test(position, size())];
113 }
114
115 /**
116 * Set the signal value at the given position and deal with boundary conditions when position is out of range.
117 * Precondition : 0 ≥ <code>index</code> ≤ size().
118 * @param position the position
119 * @param signalValue the value to set the signal to
120 * @see #getValue
121 */
122 public void setValue(int position, float signalValue) {
123 values[boundaryCondition.test(position, size())] = signalValue;
124 }
125
126 /**
127 * Get a subset of the signal betwen the given start and end positions. The returned signal
128 * has the same size as the original signal but with values set to zero for out-of-range positions.
129 * @param start position where the subset starts
130 * @param end position where the subset ends
131 * @return the subset of this signal in the given range
132 */
133 public SimpleSignal getSubset(int start, int end) {
134 SimpleSignal subset = new SimpleSignal(size());
135 for (int i = start; i < end; i++) subset.setValue(i, getValue(i));
136 return subset;
137 }
138
139
140 /** Get an estimate for the number of wavelet scales in this signal.
141 * @return the estimated number of wavelet scales contained in this signal, results are garanteed to be greater than zero
142 * @see Scale
143 */
144 public int estimateNumberOfScales() {
145 int maximumScale = 1 + (int) (log( 3.0d * ((double) size()) / 4.0d) / log(2.0d));
146 if (maximumScale >= 1) return maximumScale; else return 1;
147 }
148
149 //-----------------factory methods--------------------------------------------------
150
151 /**
152 * Get a dicrete approximation of the Dirac delta function. There is an issue
153 * with signal having a size which is even : The middle is not defined !
154 * The signal has the value of one at the first position and zero everywhere else and
155 * periodic boundary conditions.
156 * @return a dirac function signal
157 */
158 public static SimpleSignal getDirac(int size) {
159 SimpleSignal dirac = new SimpleSignal(size);
160 dirac.setValue(0, 1f);
161 dirac.setBoundaryCondition(BoundaryCondition.Periodic);
162 return dirac;
163 }
164
165 /**
166 * Get a unit amplitude chirp signal with the given start and end frequencies.
167 * @param size the size of the signal
168 * @param startFrequency the starting frequency (in units of sample size)
169 * @param endFrequency the ending frequency (in units of sample size)
170 * @return the chirp
171 */
172 public static SimpleSignal getChirp(int size, float startFrequency, float endFrequency) {
173 SimpleSignal dirac = new SimpleSignal(size);
174 dirac.setValue(0, 1f);
175 return dirac;
176 }
177
178 /**
179 * Get a unit variance gaussian noise signal.
180 * @param size the size of the signal
181 * @return the gaussian noise signal
182 */
183 public static SimpleSignal getGaussianNoise(int size) {
184 SimpleSignal noise = new SimpleSignal(size);
185 for (int i = 0; i < size; i++) noise.setValue(i, getGaussianDeviate());
186 return noise;
187 }
188
189 static int iset = 0;
190 static float gset;
191
192 /**
193 * Get a normally distributed gaussian deviate with zero mean and unit variance.
194 * (see function gasdev(long *idnum) in Press (1992) Numerical Recipes in C, 2nd ed. page 289)
195 * @return a number roughly between -1 and +1
196 */
197 public static float getGaussianDeviate() {
198 if (iset == 0) {
199 float v1, v2, rsq;
200 do { //TODO: use better random number generator like ran1(idnum) from Num.Rec.C
201 v1 = 2f * (float) random() - 1f; // Using java.lang.Math random number generator
202 v2 = 2f * (float) random() - 1f;
203 rsq = v1*v1 + v2*v2;
204 } while (rsq >= 1f || rsq == 0f);
205 float fac = (float) sqrt(-2d * log(rsq)/rsq);
206 gset = v1 * fac;
207 iset = 1;
208 return v2 * fac;
209 } else {
210 iset = 0;
211 return gset;
212 }
213 }
214
215 /**
216 * Get the B3-spline scaling funtion of the a-trous (with Mirror boundary)
217 * @return a scaling function
218 */
219 public static SimpleSignal getScalingFunction(int size, float scale) {
220 SimpleSignal scalingFunction = new SimpleSignal(size);
221 for (int i = 0; i < size; i++) {
222 float x = scale * i;
223 scalingFunction.setValue(i, ( aCube(x - 2f)
224 - 4f * aCube(x - 1f)
225 + 6f * aCube(x)
226 - 4f * aCube(x + 1f)
227 + aCube(x + 2f))/12f);
228 }
229
230 return scalingFunction;
231 }
232
233 static float aCube(float x) {
234 float absX = abs(x);
235 return absX * absX * absX;
236 }
237
238 /**
239 * Get the B3-spline wavelet funtion of the a-trous (with Mirror boundary)
240 * @return a wavelet function
241 */
242 public static SimpleSignal getWavelet(int size, float scale) {
243 return getScalingFunction(size, 2f * scale).multiply(2f).subtract(getScalingFunction(size, scale));
244 }
245
246 //-----------------------simple math---------------------------------------------------
247
248 /**
249 * Add the given signal to this signals and return result in this instance.
250 * Precondition: signal must be non-null and equal in size to this signal
251 * @param signal the signal to be added
252 */
253 public SimpleSignal add(SimpleSignal signal) {
254 add(this, signal, this);
255 return this;
256 }
257
258 /**
259 * Subtract the given signal from this signal and return result in this instance.
260 * Precondition: signal must be non-null and equal in size to this signal
261 * @param signal the signal to be subtracted
262 */
263 public SimpleSignal subtract(SimpleSignal signal) {
264 subtract(this, signal, this);
265 return this;
266 }
267
268 /**
269 * Multiply by the given signal and return result in this instance.
270 * Precondition: signal must be non-null and equal in size to this signal
271 * @param signal the signal to be multiplied
272 */
273 public SimpleSignal multiply(SimpleSignal signal) {
274 multiply(this, signal, this);
275 return this;
276 }
277
278 /**
279 * Multiply by the given multipler and return result in this instance.
280 * Precondition: signal must be non-null and equal in size to this signal
281 * @param multiplier the multiplier
282 */
283 public SimpleSignal multiply(float multiplier) {
284 multiply(this, multiplier, this);
285 return this;
286 }
287 /**
288 * Square the given signal and return result in this instance.
289 * Precondition: signal must be non-null and equal in size to this signal
290 */
291 public SimpleSignal square() {
292 square(this, this);
293 return this;
294 }
295
296 /**
297 * Return in 'c' the addition of 'a' and 'b'.
298 * @param signalA input
299 * @param signalB input
300 * @param signalC output equals signalA + signalB
301 */
302 public static void add(SimpleSignal signalA, SimpleSignal signalB, SimpleSignal signalC) {
303 for (int i = 0; i < signalA.size(); i++) signalC.setValue(i, signalA.getValue(i) + signalB.getValue(i));
304 }
305
306 /**
307 * Return in 'c' the subtraction of 'a' minus 'b'.
308 * @param signalA input
309 * @param signalB input
310 * @param signalC output equals signalA - signalB
311 */
312 public static void subtract(SimpleSignal signalA, SimpleSignal signalB, SimpleSignal signalC) {
313 for (int i = 0; i < signalA.size(); i++) signalC.setValue(i, signalA.getValue(i) - signalB.getValue(i));
314 }
315
316 /**
317 * Return in 'c' the multiplication of 'a' and 'b'.
318 * @param signalA input
319 * @param signalB input
320 * @param signalC output equals signalA * signalB
321 */
322 public static void multiply(SimpleSignal signalA, SimpleSignal signalB, SimpleSignal signalC) {
323 for (int i = 0; i < signalA.size(); i++) signalC.setValue(i, signalA.getValue(i) * signalB.getValue(i));
324 }
325
326 /**
327 * Return in 'B' the multiplication of 'a' and multiplier.
328 * @param signalA input
329 * @param multiplier input
330 * @param signalB output equals signalA * multiplier
331 */
332 public static void multiply(SimpleSignal signalA, float multiplier, SimpleSignal signalB) {
333 for (int i = 0; i < signalA.size(); i++) signalB.setValue(i, signalA.getValue(i) * multiplier);
334 }
335
336 /**
337 * Return in 'b' the square of 'a' and 'b'.
338 * @param signalA input
339 * @param signalB output equals signalA*signalA
340 */
341 public static void square(SimpleSignal signalA, SimpleSignal signalB) {
342 for (int i = 0; i < signalA.size(); i++) signalB.setValue(i, signalA.getValue(i) * signalA.getValue(i));
343 }
344
345 }