@seismo/core
Version:
This is the package for the core library of Seismo, a JavaScript library for seismic data processing and visualization. It provides utilities for handling seismic data, including FDSN web services, waveform processing, and event handling. The library is d
492 lines (491 loc) • 21 kB
JavaScript
;
/*
* Philip Crotwell
* University of South Carolina, 2019
* https://www.seis.sc.edu
*/
// converted from Steim2.java in seedCodec
// https://github.com/crotwell/seedcodec/
// constants for compression types
Object.defineProperty(exports, "__esModule", { value: true });
exports.EncodedDataSegment = exports.UnsupportedCompressionType = exports.CodecException = exports.DWWSSN = exports.SRO = exports.CDSN = exports.STEIM2 = exports.STEIM1 = exports.DOUBLE = exports.FLOAT = exports.INTEGER = exports.INT24 = exports.SHORT = exports.ASCII = void 0;
exports.isFloatCompression = isFloatCompression;
exports.decompress = decompress;
exports.decodeSteim1 = decodeSteim1;
exports.decodeSteim2 = decodeSteim2;
/** ascii */
exports.ASCII = 0;
/** 16 bit integer, or java short */
exports.SHORT = 1;
/** 24 bit integer */
exports.INT24 = 2;
/** 32 bit integer, or java int */
exports.INTEGER = 3;
/** ieee float */
exports.FLOAT = 4;
/** ieee double*/
exports.DOUBLE = 5;
/** Steim1 compression */
exports.STEIM1 = 10;
/** Steim2 compression */
exports.STEIM2 = 11;
/** CDSN 16 bit gain ranged */
exports.CDSN = 16;
/** (A)SRO */
exports.SRO = 30;
/** DWWSSN 16 bit */
exports.DWWSSN = 32;
class CodecException extends Error {
constructor(message) {
super(message);
this.message = message;
this.name = "CodecException";
}
}
exports.CodecException = CodecException;
class UnsupportedCompressionType extends Error {
constructor(message) {
super(message);
this.message = message;
this.name = "UnsupportedCompressionType";
}
}
exports.UnsupportedCompressionType = UnsupportedCompressionType;
function isFloatCompression(compressionType) {
if (compressionType === exports.FLOAT || compressionType === exports.DOUBLE) {
return true;
}
return false;
}
/**
* A holder for compressed data independent of the file format.
*/
class EncodedDataSegment {
compressionType;
dataView;
numSamples;
littleEndian;
constructor(compressionType, dataView, numSamples, littleEndian) {
this.compressionType = compressionType;
this.dataView = dataView;
this.numSamples = numSamples;
this.littleEndian = littleEndian;
}
isFloatCompression() {
return isFloatCompression(this.compressionType);
}
decode() {
return decompress(this.compressionType, this.dataView, this.numSamples, this.littleEndian);
}
}
exports.EncodedDataSegment = EncodedDataSegment;
/**
* Decompress the samples from the provided DataView and
* return an array of the decompressed values.
* Only 16 bit short, 32 bit int, 32 bit float and 64 bit double
* along with Steim1 and Steim2 are supported.
*
* @param compressionType compression format as defined in SEED blockette 1000
* @param dataView input DataView to be decoded
* @param numSamples the number of samples that can be decoded from array
* <b>b</b>
* @param littleEndian if true, dataView is little-endian (intel byte order) <b>b</b>.
* @returns array of length <b>numSamples</b>.
* @throws CodecException fail to decompress.
* @throws UnsupportedCompressionType unsupported compression type
*/
function decompress(compressionType, dataView, numSamples, littleEndian) {
// in case of record with no data points, ex detection blockette, which often have compression type
// set to 0, which messes up the decompresser even though it doesn't matter since there is no data.
if (numSamples === 0) {
return new Int32Array(0);
}
let out;
let offset = 0;
let i;
switch (compressionType) {
case exports.SHORT:
case exports.DWWSSN:
// 16 bit values
if (dataView.byteLength < 2 * numSamples) {
throw new CodecException("Not enough bytes for " +
numSamples +
" 16 bit data points, only " +
dataView.byteLength +
" bytes.");
}
out = new Int32Array(numSamples);
for (i = 0; i < numSamples; i++) {
out[i] = dataView.getInt16(offset, littleEndian);
offset += 2;
}
break;
case exports.INTEGER:
// 32 bit integers
if (dataView.byteLength < 4 * numSamples) {
throw new CodecException("Not enough bytes for " +
numSamples +
" 32 bit data points, only " +
dataView.byteLength +
" bytes.");
}
out = new Int32Array(numSamples);
for (i = 0; i < numSamples; i++) {
out[i] = dataView.getInt32(offset, littleEndian);
offset += 4;
}
break;
case exports.FLOAT:
// 32 bit floats
if (dataView.byteLength < 4 * numSamples) {
throw new CodecException("Not enough bytes for " +
numSamples +
" 32 bit data points, only " +
dataView.byteLength +
" bytes.");
}
out = new Float32Array(numSamples);
for (i = 0; i < numSamples; i++) {
out[i] = dataView.getFloat32(offset, littleEndian);
offset += 4;
}
break;
case exports.DOUBLE:
// 64 bit doubles
if (dataView.byteLength < 8 * numSamples) {
throw new CodecException("Not enough bytes for " +
numSamples +
" 64 bit data points, only " +
dataView.byteLength +
" bytes.");
}
out = new Float64Array(numSamples);
for (i = 0; i < numSamples; i++) {
out[i] = dataView.getFloat64(offset, littleEndian);
offset += 8;
}
break;
case exports.STEIM1:
// steim 1
out = decodeSteim1(dataView, numSamples, littleEndian, 0);
break;
case exports.STEIM2:
// steim 2
out = decodeSteim2(dataView, numSamples, littleEndian, 0);
break;
default:
// unknown format????
throw new UnsupportedCompressionType("Type " + compressionType + " is not supported at this time.");
}
// end of switch ()
return out;
}
/**
* 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 <b>bias</b>, otherwise leave
* the value as 0.
*
* @param dataView input DataView to be decoded
* @param numSamples the number of samples that can be decoded from array
* <b>b</b>
* @param littleEndian if true, dataView is little-endian (intel byte order) <b>b</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.
* @returns int array of length <b>numSamples</b>.
* @throws CodecException - encoded data length is not multiple of 64
* bytes.
*/
function decodeSteim1(dataView, numSamples, littleEndian, bias) {
// Decode Steim1 compression format from the provided byte array, which contains numSamples number
// of samples. swapBytes is set to true if the value words are to be byte swapped. bias represents
// a previous value which acts as a starting constant for continuing differences integration. At the
// very start, bias is set to 0.
if (dataView.byteLength % 64 !== 0) {
throw new CodecException("encoded data length is not multiple of 64 bytes (" +
dataView.byteLength +
")");
}
const buf = new ArrayBuffer(4 * numSamples);
const samples = new Int32Array(buf);
let tempSamples;
const numFrames = dataView.byteLength / 64;
let current = 0;
let start = 0;
let firstData = 0;
let lastValue = 0;
let i, j;
for (i = 0; i < numFrames; i++) {
tempSamples = extractSteim1Samples(dataView, i * 64, littleEndian); // 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 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 (j = firstData; j < tempSamples.length && current < numSamples; j++) {
samples[current] = lastValue + tempSamples[j]; // X(n) = X(n-1) + d(n)
lastValue = samples[current];
current++;
}
}
// end for each frame...
if (current !== numSamples) {
throw new CodecException("Number of samples decompressed doesn't match number in header: " +
current +
" !== " +
numSamples);
}
// ignore last sample check???
//if (end !== samples[numSamples-1]) {
// throw new SteimException("Last sample decompressed doesn't match value x(n) value in Steim1 record: "+samples[numSamples-1]+" !== "+end);
//}
return samples;
}
/**
* 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 dataView byte array of compressed data differences
* @param offset index to begin reading compressed bytes for decoding
* @param littleEndian reverse the endian-ness of the compressed bytes being read
* @returns integer array of difference (and constant) values
*/
function extractSteim1Samples(dataView, offset, littleEndian) {
/* get nibbles */
const nibbles = dataView.getInt32(offset, littleEndian);
let currNibble = 0;
const temp = []; // 4 samples * 16 longwords, can't be more than 64
let currNum = 0;
let i, n;
for (i = 0; i < 16; i++) {
// i is the word number of the frame starting at 0
//currNibble = (nibbles >>> (30 - i*2 ) ) & 0x03; // count from top to bottom each nibble in W(0)
currNibble = (nibbles >> (30 - i * 2)) & 0x03; // count from top to bottom each nibble in W(0)
//System.err.print("c(" + i + ")" + currNibble + ","); // DEBUG
// Rule appears to be:
// only check for byte-swap on actual value-atoms, so a 32-bit word in of itself
// is not swapped, but two 16-bit short *values* are or a single
// 32-bit int *value* is, if the flag is set to TRUE. 8-bit values
// are naturally not swapped.
// It would seem that the W(0) word is swap-checked, though, which is confusing...
// maybe it has to do with the reference to high-order bits for c(0)
switch (currNibble) {
case 0:
//System.out.println("0 means header info");
// only include header info if offset is 0
if (offset === 0) {
temp[currNum++] = dataView.getInt32(offset + i * 4, littleEndian);
}
break;
case 1:
//System.out.println("1 means 4 one byte differences");
for (n = 0; n < 4; n++) {
temp[currNum] = dataView.getInt8(offset + i * 4 + n);
currNum++;
}
break;
case 2:
//System.out.println("2 means 2 two byte differences");
for (n = 0; n < 4; n += 2) {
temp[currNum] = dataView.getInt16(offset + i * 4 + n, littleEndian);
currNum++;
}
break;
case 3:
//System.out.println("3 means 1 four byte difference");
temp[currNum++] = dataView.getInt32(offset + i * 4, littleEndian);
break;
default:
throw new CodecException("unreachable case: " + currNibble);
//System.out.println("default");
}
}
return temp;
}
/**
* 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 <b>bias</b>, otherwise leave
* the value as 0.
*
* @param dataView input byte array to be decoded
* @param numSamples the number of samples that can be decoded from array
* @param swapBytes if true, swap reverse the endian-ness of the elements of
* dataview
* @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.
* @returns int array of length <b>numSamples</b>.
* @throws SteimException - encoded data length is not multiple of 64
* bytes.
*/
function decodeSteim2(dataView, numSamples, swapBytes, bias) {
if (dataView.byteLength % 64 !== 0) {
throw new CodecException("encoded data length is not multiple of 64 bytes (" +
dataView.byteLength +
")");
}
const buf = new ArrayBuffer(4 * numSamples);
const samples = new Int32Array(buf);
let tempSamples;
const numFrames = dataView.byteLength / 64;
let current = 0;
let start = 0;
let firstData = 0;
let lastValue = 0;
//System.err.println("DEBUG: number of samples: " + numSamples + ", number of frames: " + numFrames + ", byte array size: " + b.length);
for (let i = 0; i < numFrames; i++) {
tempSamples = extractSteim2Samples(dataView, 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 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)
}
//System.err.print("DEBUG: ");
for (let j = firstData; j < tempSamples.length && current < numSamples; j++) {
samples[current] = lastValue + tempSamples[j]; // X(n) = X(n-1) + d(n)
lastValue = samples[current];
current++;
} //System.err.println("DEBUG: end of frame " + i);
}
// end for each frame...
if (current !== numSamples) {
throw new CodecException("Number of samples decompressed doesn't match number in header: " +
current +
" !== " +
numSamples);
}
// ignore last sample check???
//if (end !== samples[numSamples-1]) {
// throw new SteimException("Last sample decompressed doesn't match value x(n) value in Steim2 record: "+samples[numSamples-1]+" !== "+end);
//}
return samples;
}
/**
* 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 dataView 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
* @returns integer array of difference (and constant) values
*/
function extractSteim2Samples(dataView, offset, swapBytes) {
/* get nibbles */
const nibbles = dataView.getUint32(offset, swapBytes);
let currNibble = 0;
let dnib = 0;
const temp = new Int32Array(106); //max 106 = 7 samples * 15 long words + 1 nibble int
let tempInt;
let currNum = 0;
let diffCount = 0; // number of differences
let bitSize = 0; // bit size
let headerSize = 0; // number of header/unused bits at top
for (let i = 0; i < 16; i++) {
currNibble = (nibbles >> (30 - i * 2)) & 0x03;
switch (currNibble) {
case 0:
// "0 means header info"
// only include header info if offset is 0
if (offset === 0) {
temp[currNum++] = dataView.getInt32(offset + i * 4, swapBytes);
}
break;
case 1:
// "1 means 4 one byte differences " +currNum+" "+dataView.getInt8(offset+(i*4))+" "+dataView.getInt8(offset+(i*4)+1)+" "+dataView.getInt8(offset+(i*4)+2)+" "+dataView.getInt8(offset+(i*4)+3)
temp[currNum++] = dataView.getInt8(offset + i * 4);
temp[currNum++] = dataView.getInt8(offset + i * 4 + 1);
temp[currNum++] = dataView.getInt8(offset + i * 4 + 2);
temp[currNum++] = dataView.getInt8(offset + i * 4 + 3);
break;
case 2:
tempInt = dataView.getUint32(offset + i * 4, swapBytes);
dnib = (tempInt >> 30) & 0x03;
switch (dnib) {
case 1:
// "2,1 means 1 thirty bit difference"
temp[currNum++] = (tempInt << 2) >> 2;
break;
case 2:
// "2,2 means 2 fifteen bit differences"
temp[currNum++] = (tempInt << 2) >> 17; // d0
temp[currNum++] = (tempInt << 17) >> 17; // d1
break;
case 3:
// "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:
throw new CodecException(`Unknown case currNibble=${currNibble} dnib=${dnib} for chunk ${i} offset ${offset}, nibbles: ${nibbles}`);
}
break;
case 3:
tempInt = dataView.getUint32(offset + i * 4, 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.
diffCount = 0; // number of differences
bitSize = 0; // bit size
headerSize = 0; // number of header/unused bits at top
switch (dnib) {
case 0:
//System.out.println("3,0 means 5 six bit differences");
headerSize = 2;
diffCount = 5;
bitSize = 6;
break;
case 1:
//System.out.println("3,1 means 6 five bit differences");
headerSize = 2;
diffCount = 6;
bitSize = 5;
break;
case 2:
//System.out.println("3,2 means 7 four bit differences, with 2 unused bits");
headerSize = 4;
diffCount = 7;
bitSize = 4;
break;
default:
throw new CodecException(`Unknown case currNibble=${currNibble} dnib=${dnib} for chunk ${i} offset ${offset}, nibbles: ${nibbles}`);
}
if (diffCount > 0) {
for (let d = 0; d < diffCount; d++) {
// for-loop formulation
temp[currNum++] =
(tempInt << (headerSize + d * bitSize)) >>
((diffCount - 1) * bitSize + headerSize);
}
}
break;
default:
throw new CodecException(`Unknown case currNibble=${currNibble}`);
}
}
return temp.slice(0, currNum);
}