All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.iris.dmc.seedcodec.Steim2 Maven / Gradle / Ivy

The newest version!
package edu.iris.dmc.seedcodec;

//import edu.iris.Fissures.seed.util.*;

/**
 * Class for decoding or encoding Steim2-compressed data blocks to or from an
 * array of integer values.
 * 

* Steim compression scheme Copyrighted by Dr. Joseph Steim. *

*

*
Reference material found in:
*
Appendix B of SEED Reference Manual, 2nd Ed., pp. 119-125 Federation * of Digital Seismic Networks, et al. February, 1993
*
Coding concepts gleaned from code written by:
*
Guy Stewart, IRIS, 1991
*
Tom McSweeney, IRIS, 2000
*
Doug Neuhauser, UC Berkeley, 2010
*
Kevin Frechette, ISTI, 2010
*
* * @author Philip Crotwell (U South Carolina) * @author Robert Casey (IRIS DMC) * @author Doug Neuhauser (UC Berkeley) * @author Kevin Frechette (ISTI) * @version 9/13/2010 */ public class Steim2 { public static java.io.PrintStream debug = null; /** * Decode the indicated number of samples from the provided byte array and * return an integer array of the decompressed values. Being differencing * compression, there may be an offset carried over from a previous data * record. This offset value can be placed in bias, otherwise leave * the value as 0. * * @param b * input byte array to be decoded * @param numSamples * the number of samples that can be decoded from array b * @param swapBytes * if true, swap reverse the endian-ness of the elements of byte * array b. * @param bias * the first difference value will be computed from this value. * If set to 0, the method will attempt to use the X(0) constant * instead. * @return int array of length numSamples. * @throws SteimException * - encoded data length is not multiple of 64 bytes. */ public static int[] decode(byte[] b, int numSamples, boolean swapBytes, int bias) throws SteimException { if (b.length % 64 != 0) { throw new SteimException("encoded data length is not multiple of 64 bytes (" + b.length + ")"); } int[] samples = new int[numSamples]; int[] tempSamples; int numFrames = b.length / 64; int current = 0; int start = 0, end = 0; int firstData = 0; int lastValue = 0; if (debug != null) { debug.println("number of samples: " + numSamples + ", number of frames: " + numFrames + ", byte array size: " + b.length); } for (int i = 0; i < numFrames; i++) { if (debug != null) { debug.println("start of frame " + i); } tempSamples = extractSamples(b, i * 64, swapBytes); // returns only // differences // except for // frame 0 firstData = 0; // d(0) is byte 0 by default if (i == 0) { // special case for first frame lastValue = bias; // assign our X(-1) // x0 and xn are in 1 and 2 spots start = tempSamples[1]; // X(0) is byte 1 for frame 0 end = tempSamples[2]; // X(n) is byte 2 for frame 0 firstData = 3; // d(0) is byte 3 for frame 0 if (debug != null) { debug.println("frame " + i + ", bias = " + bias + ", x(0) = " + start + ", x(n) = " + end); } // if bias was zero, then we want the first sample to be X(0) // constant if (bias == 0) lastValue = start - tempSamples[3]; // X(-1) = X(0) - d(0) } for (int j = firstData; j < tempSamples.length && current < numSamples; j++) { samples[current] = lastValue + tempSamples[j]; // X(n) = X(n-1) // + d(n) lastValue = samples[current]; if (debug != null) { debug.print("d(" + (j - firstData) + ")" + tempSamples[j] + ", x(" + current + ")" + samples[current] + ";"); } current++; } if (debug != null) { debug.println(": end of frame " + i); } } // end for each frame... if (current != numSamples) { throw new SteimException( "Number of samples decompressed doesn't match number in header: " + current + " != " + numSamples); } return samples; } /** * Abbreviated, zero-bias version of decode(). * * @param b * @param numSamples * @param swapBytes * @return * @throws SteimException */ public static int[] decode(byte[] b, int numSamples, boolean swapBytes) throws SteimException { // zero-bias version of decode return decode(b, numSamples, swapBytes, 0); } /** * Abbreviated zero-bias version of encode(). * * @param samples * @param frames * @return * @throws SteimException */ public static SteimFrameBlock encode(int[] samples, int frames) throws SteimException { return encode(samples, frames, 0); // zero-bias version of encode } /** * Encode the array of integer values into a Steim 2 * compressed byte frame * block. This algorithm will not create a byte block any greater * than 63 * 64-byte frames. frames represents the number of frames to be * written. This number should be determined from the desired logical record * length minus the data offset from the record header (modulo 64) If * samples is exhausted before all frames are filled, the remaining * frames will be nulls. bias is a value carried over from a previous * data record, representing X(-1)...set to 0 otherwise * * @param samples * the data points represented as signed integers * @param frames * the number of Steim frames to use in the encoding * @param bias * offset for use as a constant for the first difference, * otherwise set to 0 * @return SteimFrameBlock containing encoded byte array * @throws SteimException * samples array is zero size * @throws SteimException * number of frames is not a positive value * @throws SteimException * cannot encode more than 63 frames */ public static SteimFrameBlock encode(int[] samples, int frames, int bias) throws SteimException { return encode(samples, frames, bias, samples.length); } /** * Encode the array of integer values into a Steim 2 * compressed byte frame * block. This algorithm will not create a byte block any greater * than 63 * 64-byte frames. frames represents the number of frames to be * written. This number should be determined from the desired logical record * length minus the data offset from the record header (modulo 64) If * samples is exhausted before all frames are filled, the remaining * frames will be nulls. bias is a value carried over from a previous * data record, representing X(-1)...set to 0 otherwise * * @param samples * the data points represented as signed integers * @param frames * the number of Steim frames to use in the encoding * @param bias * offset for use as a constant for the first difference, * otherwise set to 0 * @param samplesLength * the samples length * @return SteimFrameBlock containing encoded byte array * @throws SteimException * samples array is zero size * @throws SteimException * number of frames is not a positive value * @throws SteimException * cannot encode more than 63 frames */ public static SteimFrameBlock encode(int[] samples, int frames, int bias, int samplesLength) throws SteimException { if (samplesLength == 0) { throw new SteimException("samples array is zero size"); } if (frames <= 0) { throw new SteimException("number of frames is not a positive value"); } if (frames > 63) { throw new SteimException("cannot encode more than 63 frames, you asked for " + frames); } // all encoding will be contained within a frame block // Steim encoding 2 SteimFrameBlock frameBlock = new SteimFrameBlock(frames, 2); // // pass through the list of samples, and pass encoded words // to frame block // end loop if we run out of samples or the frame block // fills up // ............................................................. // first initialize the first frame with integration constant X(0) // and reverse integration constant X(N) // ...reverse integration constant may need to be changed if // the frameBlock fills up. frameBlock.addEncodedWord(samples[0], 0, 0); // X(0) -- first sample // value frameBlock.addEncodedWord(samples[samplesLength - 1], 0, 0); // X(N) -- // last // sample // value // // now begin looping over differences int sampleIndex = 0; // where we are in the sample array int[] diff = new int[7]; // store differences here int[] minbits = new int[7]; // store min # bits required to represent // diff here int points_remaining; // the number of points remaining while (sampleIndex < samplesLength) { // look at the next (up to seven) differences // and assess the number that can be put into // the upcoming word points_remaining = 0; for (int i = 0; i < 7; i++) { if (sampleIndex + i < samplesLength) { // as long as there are still samples // get next difference X[i] - X[i-1] if (sampleIndex + i == 0) { // special case for d(0) = x(0) - x(-1). diff[0] = samples[0] - bias; } else { diff[i] = samples[sampleIndex + i] - samples[sampleIndex + i - 1]; } // and increment the counter minbits[i] = minBitsNeeded(diff[i]); points_remaining++; } else { break; // no more samples, leave for loop } } // end for (0..7) // Determine the packing required for the next compressed word in // the SteimFrame. final int nbits = bitsForPack(minbits, points_remaining); // generate the encoded word and the nibble value final int ndiff; // the number of differences final int bitmask; final int submask; final int nibble; switch (nbits) { case 4: ndiff = 7; bitmask = 0x0000000f; submask = 0x02; nibble = 3; break; case 5: ndiff = 6; bitmask = 0x0000001f; submask = 0x01; nibble = 3; break; case 6: ndiff = 5; bitmask = 0x0000003f; submask = 0x00; nibble = 3; break; case 8: ndiff = 4; bitmask = 0x000000ff; submask = 0; nibble = 1; break; case 10: ndiff = 3; bitmask = 0x000003ff; submask = 0x03; nibble = 2; break; case 15: ndiff = 2; bitmask = 0x00007fff; submask = 0x02; nibble = 2; break; case 30: ndiff = 1; bitmask = 0x3fffffff; submask = 0x01; nibble = 2; break; default: throw new SteimException("Unable to encode " + nbits + " bit difference in Steim2 format"); } final int word = steimPackWord(diff, nbits, ndiff, bitmask, submask); // add the encoded word to the frame block if (frameBlock.addEncodedWord(word, ndiff, nibble)) { // frame block is full (but the value did get added) // so modify reverse integration constant to be the very last // value added // and break out of loop (read no more samples) frameBlock.setXsubN(samples[sampleIndex + ndiff - 1]); // X(N) break; } // increment the sampleIndex by the number of differences sampleIndex += ndiff; } // end while next sample return frameBlock; } private static int minBitsNeeded(int diff) { int minbits = 0; if (diff >= -8 && diff < 8) minbits = 4; else if (diff >= -16 && diff < 16) minbits = 5; else if (diff >= -32 && diff < 32) minbits = 6; else if (diff >= -128 && diff < 128) minbits = 8; else if (diff >= -512 && diff < 512) minbits = 10; else if (diff >= -16384 && diff < 16384) minbits = 15; else if (diff >= -536870912 && diff < 536870912) minbits = 30; else minbits = 32; return minbits; } private static int bitsForPack(int[] minbits, int points_remaining) { if (points_remaining >= 7 && (minbits[0] <= 4) && (minbits[1] <= 4) && (minbits[2] <= 4) && (minbits[3] <= 4) && (minbits[4] <= 4) && (minbits[5] <= 4) && (minbits[6] <= 4)) return 4; if (points_remaining >= 6 && (minbits[0] <= 5) && (minbits[1] <= 5) && (minbits[2] <= 5) && (minbits[3] <= 5) && (minbits[4] <= 5) && (minbits[5] <= 5)) return 5; if (points_remaining >= 5 && (minbits[0] <= 6) && (minbits[1] <= 6) && (minbits[2] <= 6) && (minbits[3] <= 6) && (minbits[4] <= 6)) return 6; if (points_remaining >= 4 && (minbits[0] <= 8) && (minbits[1] <= 8) && (minbits[2] <= 8) && (minbits[3] <= 8)) return 8; if (points_remaining >= 3 && (minbits[0] <= 10) && (minbits[1] <= 10) && (minbits[2] <= 10)) return 10; if (points_remaining >= 2 && (minbits[0] <= 15) && (minbits[1] <= 15)) return 15; if (points_remaining >= 1 && (minbits[0] <= 30)) return 30; return 32; } /** * Pack Steim2 compressed word with optional submask. * * @param diff * the differences * @param nbits * the number of bits * @param ndiff * the number of differences * @param bitmask * the bit mask * @param submask * the sub mask or 0 if none */ private static int steimPackWord(int[] diff, int nbits, int ndiff, int bitmask, int submask) { int val = 0; int i = 0; for (i = 0; i < ndiff; i++) { val = (val << nbits) | (diff[i] & bitmask); } if (submask != 0) { val |= (submask << 30); } return val; } /** * Extracts differences from the next 64 byte frame of the given compressed * byte array (starting at offset) and returns those differences in an int * array. An offset of 0 means that we are at the first frame, so include * the header bytes in the returned int array...else, do not include the * header bytes in the returned array. * * @param bytes * byte array of compressed data differences * @param offset * index to begin reading compressed bytes for decoding * @param swapBytes * reverse the endian-ness of the compressed bytes being read * @return integer array of difference (and constant) values */ protected static int[] extractSamples(byte[] bytes, int offset, boolean swapBytes) { /* get nibbles */ int nibbles = Utility.bytesToInt(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], swapBytes); if (debug != null) { debug.printf("extractSamples: %d bytes, offset is %d, byte swap is %b, nibbles is %08X\n", bytes.length, offset, swapBytes, nibbles); } int currNibble = 0; int dnib = 0; int[] temp = new int[106]; // 7 samples * 15 long words + 1 nibble int int tempInt; int startNum; int currNum = 0; for (int i = 0; i < 16; i++) { currNibble = (nibbles >> (30 - i * 2)) & 0x03; startNum = currNum; switch (currNibble) { case 0: // only include header info if offset is 0 if (offset == 0) { if (debug != null) { debug.print("0 means header info"); } temp[currNum++] = Utility.bytesToInt(bytes[offset + (i * 4)], bytes[offset + (i * 4) + 1], bytes[offset + (i * 4) + 2], bytes[offset + (i * 4) + 3], swapBytes); } break; case 1: if (debug != null) { debug.print("1 means 4 one byte differences"); } temp[currNum++] = Utility.bytesToInt(bytes[offset + (i * 4)]); temp[currNum++] = Utility.bytesToInt(bytes[offset + (i * 4) + 1]); temp[currNum++] = Utility.bytesToInt(bytes[offset + (i * 4) + 2]); temp[currNum++] = Utility.bytesToInt(bytes[offset + (i * 4) + 3]); break; case 2: tempInt = Utility.bytesToInt(bytes[offset + (i * 4)], bytes[offset + (i * 4) + 1], bytes[offset + (i * 4) + 2], bytes[offset + (i * 4) + 3], swapBytes); dnib = (tempInt >> 30) & 0x03; switch (dnib) { case 1: if (debug != null) { debug.print("2,1 means 1 thirty bit difference"); } temp[currNum++] = (tempInt << 2) >> 2; break; case 2: if (debug != null) { debug.print("2,2 means 2 fifteen bit differences"); } temp[currNum++] = (tempInt << 2) >> 17; // d0 temp[currNum++] = (tempInt << 17) >> 17; // d1 break; case 3: if (debug != null) { debug.print("2,3 means 3 ten bit differences"); } temp[currNum++] = (tempInt << 2) >> 22; // d0 temp[currNum++] = (tempInt << 12) >> 22; // d1 temp[currNum++] = (tempInt << 22) >> 22; // d2 break; default: if (debug != null) { debug.print("2," + dnib + " default"); } } break; case 3: tempInt = Utility.bytesToInt(bytes[offset + (i * 4)], bytes[offset + (i * 4) + 1], bytes[offset + (i * 4) + 2], bytes[offset + (i * 4) + 3], swapBytes); dnib = (tempInt >> 30) & 0x03; // for case 3, we are going to use a for-loop formulation that // accomplishes the same thing as case 2, just less verbose. int diffCount = 0; // number of differences int bitSize = 0; // bit size int headerSize = 0; // number of header/unused bits at top switch (dnib) { case 0: if (debug != null) { debug.print("3,0 means 5 six bit differences"); } headerSize = 2; diffCount = 5; bitSize = 6; break; case 1: if (debug != null) { debug.print("3,1 means 6 five bit differences"); } headerSize = 2; diffCount = 6; bitSize = 5; break; case 2: if (debug != null) { debug.print("3,2 means 7 four bit differences, with 2 unused bits"); } headerSize = 4; diffCount = 7; bitSize = 4; break; default: if (debug != null) { debug.print("3," + dnib + " default"); } } if (diffCount > 0) { for (int d = 0; d < diffCount; d++) { // for-loop // formulation temp[currNum++] = (tempInt << (headerSize + (d * bitSize))) >> (((diffCount - 1) * bitSize) + headerSize); } } break; } if (debug != null && startNum != currNum) { debug.print("{"); for (int tempIndex = startNum; tempIndex < currNum; tempIndex++) { if (tempIndex > startNum) { debug.print(","); } debug.print(temp[tempIndex]); } debug.println("}"); } } int[] out = new int[currNum]; System.arraycopy(temp, 0, out, 0, currNum); return out; } /** * Static method for testing the decode() method. * * @param args * not used * @throws SteimException * from called method(s) */ public static void main(String[] args) throws SteimException { byte[] b = new byte[64]; int[] temp; for (int i = 0; i < 64; i++) { b[i] = 0x00; } b[0] = 0x01; b[1] = (byte) 0xb0; System.out.println(b[1]); b[2] = (byte) 0xff; b[3] = (byte) 0xff; b[4] = 0; b[5] = 0; b[6] = 0; b[7] = 0; b[8] = 0; b[9] = 0; b[10] = 0; b[11] = 0; b[12] = 1; b[13] = 2; b[14] = 3; b[15] = 0; b[16] = 1; b[17] = 1; b[18] = 0; b[19] = 0; b[20] = 0; b[21] = 1; b[22] = 0; b[23] = 0; temp = Steim2.decode(b, 17, false); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy