View Javadoc

1   package edu.iris.dmc.seedcodec;
2   
3   //import edu.iris.Fissures.seed.util.*;
4   
5   /***
6    * Class for decoding or encoding Steim1-compressed data blocks
7    * to or from an array of integer values.
8    * <p>
9    * Steim compression scheme Copyrighted by Dr. Joseph Steim.<p>
10   * <dl>
11   * <dt>Reference material found in:</dt>
12   * <dd>
13   * Appendix B of SEED Reference Manual, 2nd Ed., pp. 119-125
14   * <i>Federation of Digital Seismic Networks, et al.</i>
15   * February, 1993
16   * </dd>
17   * <dt>Coding concepts gleaned from code written by:</dt>
18   * <dd>Guy Stewart, IRIS, 1991</dd>
19   * <dd>Tom McSweeney, IRIS, 2000</dd>
20   * </dl>
21   *
22   * @author Philip Crotwell (U South Carolina)
23   * @author Robert Casey (IRIS DMC)
24   * @version 10/22/2002
25   */
26  
27  
28  public class Steim1 {
29  
30  	/***
31  	 *  Decode the indicated number of samples from the provided byte array and
32  	 *  return an integer array of the decompressed values.  Being differencing
33  	 *  compression, there may be an offset carried over from a previous data
34  	 *  record.  This offset value can be placed in <b>bias</b>, otherwise leave
35  	 *  the value as 0.
36  	 *  @param b input byte array to be decoded
37  	 *  @param numSamples the number of samples that can be decoded from array
38  	 *  <b>b</b>
39  	 *  @param swapBytes if true, swap reverse the endian-ness of the elements of
40  	 *  byte array <b>b</b>.
41  	 *  @param bias the first difference value will be computed from this value.
42  	 *  If set to 0, the method will attempt to use the X(0) constant instead.
43  	 *  @return int array of length <b>numSamples</b>.
44  	 *  @throws SteimException - encoded data length is not multiple of 64
45  	 *  bytes.
46  	 */
47  	public static int[] decode(byte[] b, int numSamples, boolean swapBytes, int bias) throws SteimException {
48  		// Decode Steim1 compression format from the provided byte array, which contains numSamples number
49  		// of samples.  swapBytes is set to true if the value words are to be byte swapped.  bias represents
50  		// a previous value which acts as a starting constant for continuing differences integration.  At the
51  		// very start, bias is set to 0.
52  		if (b.length % 64 != 0) {
53  			throw new SteimException("encoded data length is not multiple of 64 bytes (" + b.length + ")"); 
54  		}
55  		int[] samples = new int[numSamples];
56  		int[] tempSamples;
57  		int numFrames = b.length / 64;
58  		int current = 0;
59  		int start=0, end=0;
60  		int firstData=0;
61  		int lastValue = 0;
62  
63  		//System.err.println("DEBUG: number of samples: " + numSamples + ", number of frames: " + numFrames + ", byte array size: " + b.length);
64  		for (int i=0; i< numFrames; i++ ) {
65  			//System.err.println("DEBUG: start of frame " + i);
66  			tempSamples = extractSamples(b, i*64, swapBytes);   // returns only differences except for frame 0
67  			firstData = 0; // d(0) is byte 0 by default
68  			if (i==0) {   // special case for first frame
69  				lastValue = bias; // assign our X(-1)
70  				// x0 and xn are in 1 and 2 spots
71  				start = tempSamples[1];  // X(0) is byte 1 for frame 0
72  				end = tempSamples[2];    // X(n) is byte 2 for frame 0
73  				firstData = 3; // d(0) is byte 3 for frame 0
74  				//System.err.println("DEBUG: frame " + i + ", bias = " + bias + ", x(0) = " + start + ", x(n) = " + end);
75  				// if bias was zero, then we want the first sample to be X(0) constant
76  				if (bias == 0) lastValue = start - tempSamples[3];  // X(-1) = X(0) - d(0)
77  			}
78  			//System.err.print("DEBUG: ");
79  			for (int j = firstData; j < tempSamples.length && current < numSamples; j++) {
80  				samples[current] = lastValue + tempSamples[j];  // X(n) = X(n-1) + d(n)
81  				lastValue = samples[current];
82  				//System.err.print("d(" + (j-firstData) + ")" + tempSamples[j] + ", x(" + current + ")" + samples[current] + ";"); // DEBUG
83  				current++;
84  			}
85  			//System.err.println("DEBUG: end of frame " + i);
86  		}  // end for each frame...
87          if (current != numSamples) {
88              throw new SteimException("Number of samples decompressed doesn't match number in header: "+current+" != "+numSamples);
89          }
90  		return samples;
91  	}
92  
93  	/***
94  	 * Abbreviated, zero-bias version of decode().
95  	 *
96  	 * @see edu.iris.Fissures.codec.Steim1#decode(byte[],int,boolean,int)
97  	 */
98  	public static int[] decode(byte[] b, int numSamples, boolean swapBytes) throws SteimException {
99  		// zero-bias version of decode
100 		return decode(b,numSamples,swapBytes,0);
101 	}
102 
103 
104 	/***
105 	* Encode the array of integer values into a Steim 1 * compressed byte frame block.
106 	* This algorithm will not create a byte block any greater * than 63 64-byte frames.
107 	* <b>frames</b> represents the number of frames to be written.
108 	* This number should be determined from the desired logical record length
109 	* <i>minus</i> the data offset from the record header (modulo 64)
110 	* If <b>samples</b> is exhausted before all frames are filled, the remaining frames
111 	* will be nulls.
112 	* <b>bias</b> is a value carried over from a previous data record, representing
113 	* X(-1)...set to 0 otherwise
114 	* @param samples the data points represented as signed integers
115 	* @param frames the number of Steim frames to use in the encoding
116 	* @param bias offset for use as a constant for the first difference, otherwise
117 	* set to 0
118 	* @return SteimFrameBlock containing encoded byte array
119 	* @throws SteimException samples array is zero size
120 	* @throws SteimException number of frames is not a positive value
121 	* @throws SteimException cannot encode more than 63 frames
122 	*/
123 	public static SteimFrameBlock encode(int[] samples, int frames, int bias) throws SteimException {
124 		if (samples.length == 0) {
125 			throw new SteimException("samples array is zero size");
126 		}
127 		if (frames <= 0) {
128 			throw new SteimException("number of frames is not a positive value");
129 		}
130 		if (frames > 63) {
131 			throw new SteimException("cannot encode more than 63 frames, you asked for " + frames);
132 		}
133 		// all encoding will be contained within a frame block
134 		// Steim encoding 1
135 		SteimFrameBlock frameBlock = new SteimFrameBlock(frames,1);
136 		//
137 		// pass through the list of samples, and pass encoded words
138 		// to frame block
139 		// end loop if we run out of samples or the frame block
140 		// fills up
141 		// .............................................................
142 		// first initialize the first frame with integration constant X(0)
143 		// and reverse integration constant X(N)
144 		// ...reverse integration constant may need to be changed if 
145 		// the frameBlock fills up.
146 		frameBlock.addEncodedWord(samples[0],0,0);                // X(0) -- first sample value
147 		frameBlock.addEncodedWord(samples[samples.length-1],0,0); // X(N) -- last sample value
148 		//
149 		// now begin looping over differences
150 		int sampleIndex = 0;  // where we are in the sample array
151 		int[] diff = new int[4]; // store differences here
152 		int diffCount = 0;  // how many sample diffs we put into current word
153 		int maxSize = 0;    // the maximum diff value size encountered
154 		int curSize = 0;    // size of diff value currently looked at
155 		while(sampleIndex < samples.length) {
156 			// look at the next (up to four) differences
157 			// and assess the number that can be put into
158 			// the upcoming word
159 			diffCount = 0;
160 			maxSize = 0;
161 			for (int i=0; i<4; i++) {
162 				if (sampleIndex+i < samples.length) {
163 					// as long as there are still samples
164 					// get next difference  X[i] - X[i-1]
165 					if (sampleIndex+i == 0) {
166 						// special case for d(0) = x(0) - x(-1).
167 						diff[0] = samples[0] - bias;
168 					} else {
169 						diff[i] = samples[sampleIndex+i] - samples[sampleIndex+i-1];
170 					}
171 					// and increment the counter
172 					diffCount++;
173 				} else break;  // no more samples, leave for loop
174 				// curSize indicates how many bytes the number would fill
175 				if (diff[i] <= 127 && diff[i] >= -128) curSize = 1;
176 				else if (diff[i] <= 32767 && diff[i] >= -32768) curSize = 2;
177 				else curSize = 4;
178 				// get the maximum size
179 				if (curSize > maxSize) maxSize = curSize;
180 				// now we multiply the maximum size encountered so far
181 				// by the number of differences examined so far
182 				// if the number is less than 4, we move on to the next diff
183 				// if the number is equal to 4, then we stop with the 
184 				// current count
185 				// if the number is greater than 4, then back off one count
186 				// and if the count is 3 (cannot end with a 3 byte count), 
187 				// then back off one count again
188 				// (the whole idea is we are looking for the proper fit)
189 				if (maxSize * diffCount == 4) break;
190 				else if (maxSize * diffCount > 4) {
191 					diffCount--;
192 					if (diffCount == 3) diffCount--;
193 					break;
194 				}
195 			} // end for (0..3)
196 
197 			// generate the encoded word and the nibble value
198 			int nibble = 0;
199 			int word = 0;
200 			if (diffCount == 1) {
201 				word = diff[0];
202 				nibble = 3;  // size 4 = 11
203 			} else if (diffCount == 2) {
204 				word = (diff[0] & 0xffff) << 16;  // clip to 16 bits, then shift
205 				word |= (diff[1] & 0xffff);
206 				nibble = 2;  // size 2 = 10
207 			} else {  // diffCount == 4
208 				word = (diff[0] & 0xff) << 24;    // clip to 8 bits, then shift
209 				word |= (diff[1] & 0xff) << 16;
210 				word |= (diff[2] & 0xff) << 8;
211 				word |= (diff[3] & 0xff);
212 				nibble = 1;  // size 1 = 01
213 			}
214 
215 			// add the encoded word to the frame block
216 			if (frameBlock.addEncodedWord(word,diffCount,nibble)) {
217 				// frame block is full (but the value did get added) 
218 				// so modify reverse integration constant to be the very last value added
219 				// and break out of loop (read no more samples)
220 				frameBlock.setXsubN(samples[sampleIndex+diffCount-1]); // X(N)
221 				break;
222 			}
223 
224 			// increment the sampleIndex by the diffCount
225 			sampleIndex += diffCount;
226 		} // end while next sample
227 
228 		return frameBlock;
229 	}
230 
231 	/***
232 	 * Abbreviated zero-bias version of encode().
233 	 * @see edu.iris.Fissures.codec.Steim1#encode(int[],int,int)
234 	 */
235 	public static SteimFrameBlock encode(int[] samples, int frames) throws SteimException {
236 		return encode(samples,frames,0);   // zero-bias version of encode
237 	}
238 
239 
240 	/***
241 	 * Extracts differences from the next 64 byte frame of the given compressed
242 	 * byte array (starting at offset) and returns those differences in an int
243 	 * array.
244 	 * An offset of 0 means that we are at the first frame, so include the header
245 	 * bytes in the returned int array...else, do not include the header bytes
246 	 * in the returned array.
247 	 * @param bytes byte array of compressed data differences
248 	 * @param offset index to begin reading compressed bytes for decoding
249 	 * @param swapBytes reverse the endian-ness of the compressed bytes being read
250 	 * @return integer array of difference (and constant) values
251 	 */
252 	protected static int[] extractSamples(byte[] bytes,
253 			int offset, 
254 			boolean swapBytes) {
255 		/* get nibbles */
256 		int nibbles = Utility.bytesToInt(bytes[offset], 
257 				bytes[offset+1], 
258 				bytes[offset+2], 
259 				bytes[offset+3], 
260 				swapBytes);
261 		int currNibble = 0;
262 		int[] temp = new int[64];  // 4 samples * 16 longwords, can't be more
263 		int currNum = 0;
264 		//System.err.print ("DEBUG: ");
265 		for (int i=0; i<16; i++) {   // i is the word number of the frame starting at 0
266 			//currNibble = (nibbles >>> (30 - i*2 ) ) & 0x03; // count from top to bottom each nibble in W(0)
267 			currNibble = (nibbles >> (30 - i*2) ) & 0x03; // count from top to bottom each nibble in W(0)
268 			//System.err.print("c(" + i + ")" + currNibble + ",");  // DEBUG
269 			// Rule appears to be:
270 			// only check for byte-swap on actual value-atoms, so a 32-bit word in of itself
271 			// is not swapped, but two 16-bit short *values* are or a single
272 			// 32-bit int *value* is, if the flag is set to TRUE.  8-bit values
273 			// are naturally not swapped.
274 			// It would seem that the W(0) word is swap-checked, though, which is confusing...
275 			// maybe it has to do with the reference to high-order bits for c(0)
276 			switch (currNibble) {
277 				case 0:
278 					//System.out.println("0 means header info");
279 					// only include header info if offset is 0
280 					if (offset == 0) {
281 						temp[currNum++] = Utility.bytesToInt(bytes[offset+(i*4)], 
282 								bytes[offset+(i*4)+1],
283 								bytes[offset+(i*4)+2],
284 								bytes[offset+(i*4)+3],
285 								swapBytes);
286 					}
287 					break;
288 				case 1:
289 					//System.out.println("1 means 4 one byte differences");
290 					for (int n=0; n<4; n++) {
291 						temp[currNum] = Utility.bytesToInt(bytes[offset+(i*4)+n]);
292 						currNum++;
293 					}
294 					break;
295 				case 2:
296 					//System.out.println("2 means 2 two byte differences");
297 					for (int n=0; n<4; n+=2) {
298 						temp[currNum] = Utility.bytesToInt(bytes[offset+(i*4)+n], 
299 								bytes[offset+(i*4)+n+1],
300 								swapBytes);
301 						currNum++;
302 					}
303 					break;
304 				case 3:
305 					//System.out.println("3 means 1 four byte difference");
306 					temp[currNum++] = Utility.bytesToInt(bytes[offset+(i*4)], 
307 							bytes[offset+(i*4)+1],
308 							bytes[offset+(i*4)+2], 
309 							bytes[offset+(i*4)+3],
310 							swapBytes);
311 					break;
312 				default:
313 					//System.out.println("default");
314 			}
315 		}
316 		//System.err.println(".");   // DEBUG
317 		int[] out = new int[currNum];
318 		System.arraycopy(temp, 0, out, 0, currNum);  // trim array to number of values
319 		return out;
320 	}
321 
322 	/***
323 	 * Static method for testing the decode() method.
324 	 * @param args not used
325 	 * @throws SteimException from called method(s)
326 	*/
327 	public static void main(String[] args) throws SteimException {
328 
329 		byte[] b = new byte[64];
330 		int[] temp;
331 
332 		for (int i=0; i< 64 ; i++) {
333 			b[i] = 0x00;
334 		}
335 		b[0] = 0x01;
336 		b[1] = (byte)0xb0;
337 		System.out.println(b[1]);
338 		b[2] = (byte)0xff;
339 		b[3] = (byte)0xff;
340 
341 		b[4] = 0;
342 		b[5] = 0;
343 		b[6] = 0;
344 		b[7] = 0;
345 
346 		b[8] = 0;
347 		b[9] = 0;
348 		b[10] = 0;
349 		b[11] = 0;
350 
351 		b[12] = 1;
352 		b[13] = 2;
353 		b[14] = 3;
354 		b[15] = 0;
355 
356 		b[16] = 1;
357 		b[17] = 1;
358 		b[18] = 0;
359 		b[19] = 0;
360 
361 		b[20] = 0;
362 		b[21] = 1;
363 		b[22] = 0;
364 		b[23] = 0;
365 		temp = Steim1.decode(b, 17, false);
366 	}
367 }