/*
* Copyright 2010, United States Geological Survey or
* third-party contributors as indicated by the @author tags.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package gov.usgs.anss.cd11;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;
import org.apache.log4j.Logger;
/**
* This class represents a CD1.1 channel subframe and has methods for reading
* that information from a positioned byte buffer. The subframe is detailed in
* the manual as table 2.10. This class was implemented based on the Version 0.3
* version of the manual dated 18 Dec 2002
*
* @author davidketchum
*/
public class ChannelSubframe {
private static final Logger logger = Logger.getLogger(ChannelSubframe.class);
private int len;
@SuppressWarnings("unused") //Placeholder for data read from stream
private int authOffset;
private byte auth;
private byte transform;
private byte sensorType;
@SuppressWarnings("unused") //Placeholder for data read from stream
private byte optionFlag;
private String station; // a SSSSSCCCLL name!
private byte[] statbuf = new byte[10];
private String uncompressedFormat; // two characters\
@SuppressWarnings("unused") //Placeholder for data read from stream
private float calibFactor;
@SuppressWarnings("unused") //Placeholder for data read from stream
private float calibPeriod;
private String timeStamp;
private byte[] timebuf = new byte[20]; // scratch space to get time
private GregorianCalendar time = new GregorianCalendar();
private int msLength;
private int nsamp;
private int statusSize;
private byte[] status;
private int dataSize;
private byte[] data;
private ByteBuffer bdata;
private int subframeCount;
@SuppressWarnings("unused") //Placeholder for data read from stream
private int authKeyID;
private int authSize;
private byte[] authBytes;
public int getFrameLength() {
return len;
}
public int getTransform() {
return transform;
}
public byte getSensorType() {
return sensorType;
}
/**
* get the station
*
* @return a SSSSSCCCLL station name
*/
public String getStation() {
return station;
}
public String getUncompressedFormat() {
return uncompressedFormat;
}
public String getCDTimeString() {
return timeStamp;
}
public GregorianCalendar getGregorianTime() {
return time;
}
public int getMSLength() {
return msLength;
}
public int getNsamp() {
return nsamp;
}
public int getStatusSize() {
return statusSize;
}
public byte[] getStatusBytes() {
return status;
}
public int getDataSize() {
return dataSize;
}
public byte[] getDataBytes() {
return data;
}
public int getSubframeCount() {
return subframeCount;
}
public double getRate() {
return nsamp / (msLength / 1000.);
}
@Override
public String toString() {
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
return station + " " + timeStamp + " " + timeFormat.format(time) + " #samp=" + nsamp + " msLen=" + msLength + " tfrm="
+ transform + " ucfrm=" + uncompressedFormat + " sens=" + sensorType + " auth=" + auth + " #sta="
+ statusSize + " #data=" + dataSize;
}
public ChannelSubframe(ByteBuffer b) {
load(b);
}
/**
* Load this ChannelSubframe with data from byte buffer b starting at the
* current position of b
*
* @param b
* A ByteBuffer position to the start of a ChannelSubframe
*/
private void load(ByteBuffer b) {
// save position of beginning - These fields are in table 10 pg 23 of
// manual
int pos = b.position();
len = b.getInt();
authOffset = b.getInt(); //
auth = b.get();
transform = b.get();
sensorType = b.get();
optionFlag = b.get();
b.get(statbuf); // get 10 bytes of station name
for (int i = 0; i < 10; i++)
if (statbuf[i] == 0)
statbuf[i] = 32;
station = new String(statbuf);
if (station.substring(5, 7).equals("sz"))
station = station.substring(0, 5) + "SHZ"
+ station.substring(8, 10);
if (station.substring(5, 7).equals("sn"))
station = station.substring(0, 5) + "SHN"
+ station.substring(8, 10);
if (station.substring(5, 7).equals("se"))
station = station.substring(0, 5) + "SHE"
+ station.substring(8, 10);
if (station.substring(5, 7).equals("bz"))
station = station.substring(0, 5) + "BHZ"
+ station.substring(8, 10);
if (station.substring(5, 7).equals("bn"))
station = station.substring(0, 5) + "BHN"
+ station.substring(8, 10);
if (station.substring(5, 7).equals("be"))
station = station.substring(0, 5) + "BHE"
+ station.substring(8, 10);
/*
* if( !(station.substring(5,8).equals("BHZ") ||
* station.substring(5,8).equals("BHN")
* ||station.substring(5,8).equals("BHE") ||
* station.substring(5,8).equals("BH1") ||
* station.substring(5,8).equals("BH2") ||
* station.substring(5,8).equals("SHZ") ||
* station.substring(5,8).equals("SHN")
* ||station.substring(5,8).equals("SHE") ||
* station.substring(5,8).equals("MHZ") ||
* station.substring(5,8).equals("MHN")
* ||station.substring(5,8).equals("MHE") ||
* station.substring(5,8).equals("HHZ") ||
* station.substring(5,8).equals("HHN")
* ||station.substring(5,8).equals("HHE") ||
* station.substring(5,8).equals("EHZ") ||
* station.substring(5,8).equals("EHN")
* ||station.substring(5,8).equals("EHE") ||
* station.substring(5,8).equals("HNZ") ||
* station.substring(5,8).equals("HNN")
* ||station.substring(5,8).equals("HNE") ||
* station.substring(5,8).equals("BNZ") ||
* station.substring(5,8).equals("BNN")
* ||station.substring(5,8).equals("BNE") ||
* station.substring(5,8).equals("EHZ") ||
* station.substring(5,8).equals("EDH")) ) {
*
* lg.error(" ****** CD1.1 channel subframe : Got bad component name="+
* station+"|"+station.substring(5,8)+"|"); }
*/
b.get(timebuf, 0, 2);
for (int i = 0; i < 2; i++)
if (timebuf[i] == 0)
timebuf[i] = 32;
uncompressedFormat = new String(timebuf, 0, 2);
calibFactor = b.getFloat();
calibPeriod = b.getFloat();
b.get(timebuf);
timeStamp = new String(timebuf);
CD11Frame.fromCDTimeString(timeStamp, time);
msLength = b.getInt();
nsamp = b.getInt();
// Only the type 1 packet from table 4.22 makes any sense here
statusSize = b.getInt();
if (statusSize > 0) {
try {
if (status == null)
status = new byte[statusSize];
else if (status.length < statusSize)
status = new byte[statusSize];
b.get(status, 0, statusSize);
if (statusSize % 4 != 0)
b.position(b.position() + 4 - (statusSize % 4));
} catch (RuntimeException e) {
logger.error("Runtime getting status sssize=" + statusSize
+ " pos=" + b.position() + " e:", e);
}
}
// The datasize has to be at least 8 bytes bigger than the actual data.
// The uncompressor often gets a long
// when it does not need all of it and you get buffer underflow if there
// are not enough bytes in the backing buffer
dataSize = b.getInt();
if (data == null) {
data = new byte[dataSize + 8];
bdata = ByteBuffer.wrap(data);
} else if (data.length < dataSize + 8) {
data = new byte[dataSize + 8];
bdata = ByteBuffer.wrap(data);
}
if (dataSize <= 0)
logger.error("**** ChannelSubFrame: got a load datasize <= 0! datsize="
+ dataSize + " pos=" + b.position() + " " + toString());
else {
try {
b.get(data, 0, dataSize);
if (dataSize % 4 != 0)
b.position(b.position() + 4 - (dataSize % 4)); // i*4 align
} catch (RuntimeException e) {
logger.error("*** ChannelSubFrame: got buffer runtime datsize="
+ dataSize + " pos=" + b.position() + " e:", e);
}
}
subframeCount = b.getInt();
authKeyID = b.getInt();
authSize = b.getInt();
if (authSize > 0) {
try {
authBytes = new byte[authSize];
b.get(authBytes);
if (authSize % 4 != 0)
b.position(b.position() + (4 - (authSize % 4))); // i*4
// align
} catch (RuntimeException e) {
logger.error("*** ChannelSubFrame: AUTH authsize=" + authSize
+ " pos=" + b.position() + " e:", e);
}
}
if (b.position() - pos != len + 4) // test that we are positions where
// the length says
logger.error("Seem to have the wrong subframe length!");
}
/**
* get the data samples from this subframe, this routine does all of the
* decoding of various allowed formats for the data.
*
* @param samples
* A user buffer to conain the samples. It must be big enough!
* @return The number of samples decoded
* @throws CanadaException
* If detected during decompression of Canadian Compressed frame
*/
public int getSamples(int[] samples) throws CanadaException {
bdata.position(0);
switch (transform) {
case 0: // no transform, type is done by uncompressed format
if (uncompressedFormat.equals("s4")) {
for (int i = 0; i < nsamp; i++) {
samples[i] = bdata.getInt();
}
return nsamp;
} else if (uncompressedFormat.equals("s3")) {
for (int i = 0; i < nsamp; i++) {
samples[i] = ((((int) bdata.get()) & 0xff) << 16)
| ((((int) bdata.get()) & 0xff) << 8)
| (((int) bdata.get()) & 0xff);
}
return nsamp;
} else if (uncompressedFormat.equals("s2")) {
for (int i = 0; i < nsamp; i++) {
samples[i] = bdata.getShort();
}
return nsamp;
} else if (uncompressedFormat.equals("i4")) {
logger.error("Cannot do format " + uncompressedFormat);
break;
} else if (uncompressedFormat.equals("i2")) {
logger.error("Cannot do format " + uncompressedFormat);
break;
} else if (uncompressedFormat.equals("CD")) {
logger.error("Cannot do format " + uncompressedFormat);
break;
} else {
logger.error("Cannot do format " + uncompressedFormat);
break;
}
case 1: // Canadian compression applied before signature
if (uncompressedFormat.equals("CD")) { // This is the CD1.0
// encapsulated data
ByteBuffer bb = ByteBuffer.wrap(data);
bb.position(0);
int len2 = bb.getInt();
if (auth != 0) {
bb.position(bb.position() + 40); // Skip the auth bytes
}
double time2 = bb.getDouble();
GregorianCalendar g2 = new GregorianCalendar();
g2.setTimeInMillis((long) (time2 * 1000.));
//int ns = bb.getInt();
//int stat2 = bb.getInt();
// par.prt("CD : "+station+" len="+len2+" datasize="+dataSize+" time="+time2+" as g ="+Util.ascdate(g2)+" "+Util.asctime2(g2)+" status="+Util.toHex(stat2)+" ns="+ns);
byte[] cddata = new byte[len2 - bb.position() + 4 + 8];
bb.get(cddata, 0, len2 - bb.position() + 4);
Canada.canada_uncompress(cddata, samples, cddata.length - 8,
nsamp, 0);
} else
Canada.canada_uncompress(data, samples, dataSize, nsamp, 0);
return nsamp;
case 2: // Canadian compression applied after signature
Canada.canada_uncompress(data, samples, dataSize, nsamp, 0);
break;
case 3: // Steim compression applied before signature
break;
case 4: // Steim compression applied after signature
break;
default:
logger.error("transformation type " + transform
+ " is not implemented!");
}
return 0; // if we got here, the decoding failed or is not implemented.
}
}