package edu.colostate.vchill.chill;
import edu.colostate.vchill.ChillDefines;
import edu.colostate.vchill.ChillDefines.ColorType;
import edu.colostate.vchill.socket.SocketUtil;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* Field description / scaling information header
*
* @author Jochen Deyke
* @version 2007-10-29
*/
public class ChillMomentFieldScale extends ChillHeader implements Comparable<ChillMomentFieldScale> {
/**
* Maximum length (in bytes) of UTF-8 coded string
*/
public static final int fieldNameLength = 32;
public static final int fieldDescriptionLength = 128;
public static final int unitsLength = 32;
/**
* size (in bytes) of this header (including ChillHeaderHeader, but not including extraData)
*/
public static final int BYTE_SIZE = ChillHeaderHeader.BYTE_SIZE +
fieldNameLength + fieldDescriptionLength + ChillDefines.INT_BYTE_SIZE + unitsLength +
6 * ChillDefines.INT_BYTE_SIZE + 2 * ChillDefines.SHORT_BYTE_SIZE;
/**
* field name in UTF-8 coded string
*/
public String fieldName;
public String fieldDescription;
public int keyboardAccelerator;
public String units;
/**
* identifies this field's bit number in the requested_data word.
* 0 = bit 0 (lsb) = value of 1 in long long requested_data word
*/
public int fieldNumber;
/**
* factor, scale and bias provide scaling info for this field.
* actual field value = ((value in data array)*scale + bias)/factor
*/
public int factor;
public int scale;
public int bias;
/**
* maximum field value (scaled by factor)
*/
public int maxValue;
/**
* minimum field value (scaled by factor)
*/
public int minValue;
/**
* bit 0 -> if 1 -> 8 bit signed if 0 -> 8 bit unsigned
* bits 1-3 reserved
* bit 4 -> if set indicates field may be unfolded
* (currently CHILL uses only unsigned 8 bit data. This is offset
* binary rather than 2's complement. The 'bias' word is used to
* locate the 0 for fields with positive and negative values)
*/
public short dataWordCoding;
/**
* This is used in selecting from available color tables, and as an indicator for special processing.
* e.g. vel fields can be unfolded by the display, and NCP fields may be used in thresholding
*/
public ColorType colorMapType;
/**
* current values used for display
*/
private volatile double currentMax;
private volatile double currentMin;
public ChillMomentFieldScale(final ChillFieldInfo info, final int keyboardAccelerator, final String units, final int factor, final int scale, final int bias) {
super(new ChillHeaderHeader(ChillDefines.FIELD_SCALE_DATA, BYTE_SIZE));
extraData = new byte[0];
this.fieldName = info.fieldName;
this.fieldDescription = info.longFieldName;
this.keyboardAccelerator = keyboardAccelerator;
this.units = units;
this.fieldNumber = info.fieldNumber;
this.factor = factor;
this.scale = scale;
this.bias = bias;
this.maxValue = info.maxValue;
this.minValue = info.minValue;
this.currentMax = this.maxValue / (double) this.factor;
this.currentMin = this.minValue / (double) this.factor;
this.dataWordCoding = info.dataWordCoding;
this.colorMapType = ColorType.values()[info.colorMapType];
}
/**
* Constructs a header by reading initial values from a DataInput.
*
* @param in the DataInput to read initialization values from
*/
public ChillMomentFieldScale(final DataInput in, final ChillHeaderHeader header) throws IOException {
super(header);
assert header.recordType == ChillDefines.FIELD_SCALE_DATA;
this.fieldName = SocketUtil.readString(in, fieldNameLength);
this.fieldDescription = SocketUtil.readString(in, fieldDescriptionLength);
this.keyboardAccelerator = in.readInt();
this.units = SocketUtil.readString(in, unitsLength);
this.fieldNumber = in.readInt();
assert this.fieldNumber < ChillDefines.MAX_NUM_TYPES;
this.factor = in.readInt();
this.scale = in.readInt();
this.bias = in.readInt();
this.maxValue = in.readInt();
this.minValue = in.readInt();
this.dataWordCoding = in.readShort();
short color = in.readShort();
if (color >= ColorType.values().length) color = 0; //default to reflectivity on unknown color type
this.colorMapType = ColorType.values()[color];
this.currentMax = this.maxValue / (double) this.factor;
this.currentMin = this.minValue / (double) this.factor;
assert header.headerLength - ChillMomentFieldScale.BYTE_SIZE >= 0;
in.readFully(this.extraData = new byte[header.headerLength - ChillMomentFieldScale.BYTE_SIZE]);
}
/**
* Writes this header to a DataOut
*
* @param out the DataOutput to write values to
*/
public void write(final DataOutput out) throws IOException {
assert header.headerLength == ChillMomentFieldScale.BYTE_SIZE + extraData.length;
super.header.write(out);
SocketUtil.writeString(this.fieldName, out, fieldNameLength);
SocketUtil.writeString(this.fieldDescription, out, fieldDescriptionLength);
out.writeInt(this.keyboardAccelerator);
SocketUtil.writeString(this.units, out, unitsLength);
assert this.fieldNumber < ChillDefines.MAX_NUM_TYPES;
out.writeInt(this.fieldNumber);
out.writeInt(this.factor);
out.writeInt(this.scale);
out.writeInt(this.bias);
out.writeInt(this.maxValue);
out.writeInt(this.minValue);
out.writeShort(this.dataWordCoding);
out.writeShort((short) (this.colorMapType.ordinal()));
out.write(this.extraData);
}
public double getValue(final int byteValue) {
if (byteValue == 0) return Double.NaN;
return (byteValue * (double) this.scale + this.bias) / this.factor;
}
public int getHash(final double doubleValue) {
if (Double.isNaN(doubleValue)) return 0;
return (int) ((doubleValue * this.factor - this.bias) / this.scale);
}
public double getMax() {
return this.currentMax;
}
public void setMax(final double newMax) {
this.currentMax = newMax; //Math.min(newMax, this.maxValue / (double)this.factor);
}
public double getMin() {
return this.currentMin;
}
public void setMin(final double newMin) {
this.currentMin = newMin; //Math.max(newMin, this.minValue / (double)this.factor);
}
public boolean isUnfoldable() {
return ((1 << 4) & this.dataWordCoding) > 0;
}
public boolean isGradientable() {
return this.colorMapType == ColorType.Z;
}
public boolean shouldLowerBoundBeClipped() {
return this.colorMapType == ColorType.Z;
}
public int compareTo(final ChillMomentFieldScale other) {
return this.fieldNumber - other.fieldNumber;
}
public int hashCode() {
return this.fieldNumber;
}
public boolean equals(Object other) {
if (!(other instanceof ChillMomentFieldScale)) return false;
return this.fieldNumber == ((ChillMomentFieldScale) other).fieldNumber;
}
public String toString() {
return this.fieldName;
}
}