1 package edu.iris.dmc.seedcodec;
2
3
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
49
50
51
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
64 for (int i=0; i< numFrames; i++ ) {
65
66 tempSamples = extractSamples(b, i*64, swapBytes);
67 firstData = 0;
68 if (i==0) {
69 lastValue = bias;
70
71 start = tempSamples[1];
72 end = tempSamples[2];
73 firstData = 3;
74
75
76 if (bias == 0) lastValue = start - tempSamples[3];
77 }
78
79 for (int j = firstData; j < tempSamples.length && current < numSamples; j++) {
80 samples[current] = lastValue + tempSamples[j];
81 lastValue = samples[current];
82
83 current++;
84 }
85
86 }
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
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
134
135 SteimFrameBlock frameBlock = new SteimFrameBlock(frames,1);
136
137
138
139
140
141
142
143
144
145
146 frameBlock.addEncodedWord(samples[0],0,0);
147 frameBlock.addEncodedWord(samples[samples.length-1],0,0);
148
149
150 int sampleIndex = 0;
151 int[] diff = new int[4];
152 int diffCount = 0;
153 int maxSize = 0;
154 int curSize = 0;
155 while(sampleIndex < samples.length) {
156
157
158
159 diffCount = 0;
160 maxSize = 0;
161 for (int i=0; i<4; i++) {
162 if (sampleIndex+i < samples.length) {
163
164
165 if (sampleIndex+i == 0) {
166
167 diff[0] = samples[0] - bias;
168 } else {
169 diff[i] = samples[sampleIndex+i] - samples[sampleIndex+i-1];
170 }
171
172 diffCount++;
173 } else break;
174
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
179 if (curSize > maxSize) maxSize = curSize;
180
181
182
183
184
185
186
187
188
189 if (maxSize * diffCount == 4) break;
190 else if (maxSize * diffCount > 4) {
191 diffCount--;
192 if (diffCount == 3) diffCount--;
193 break;
194 }
195 }
196
197
198 int nibble = 0;
199 int word = 0;
200 if (diffCount == 1) {
201 word = diff[0];
202 nibble = 3;
203 } else if (diffCount == 2) {
204 word = (diff[0] & 0xffff) << 16;
205 word |= (diff[1] & 0xffff);
206 nibble = 2;
207 } else {
208 word = (diff[0] & 0xff) << 24;
209 word |= (diff[1] & 0xff) << 16;
210 word |= (diff[2] & 0xff) << 8;
211 word |= (diff[3] & 0xff);
212 nibble = 1;
213 }
214
215
216 if (frameBlock.addEncodedWord(word,diffCount,nibble)) {
217
218
219
220 frameBlock.setXsubN(samples[sampleIndex+diffCount-1]);
221 break;
222 }
223
224
225 sampleIndex += diffCount;
226 }
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);
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
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];
263 int currNum = 0;
264
265 for (int i=0; i<16; i++) {
266
267 currNibble = (nibbles >> (30 - i*2) ) & 0x03;
268
269
270
271
272
273
274
275
276 switch (currNibble) {
277 case 0:
278
279
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
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
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
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
314 }
315 }
316
317 int[] out = new int[currNum];
318 System.arraycopy(temp, 0, out, 0, currNum);
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 }