package org.yamcs.xtceproc;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.parameter.ParameterValue;
import org.yamcs.xtce.BinaryDataEncoding;
import org.yamcs.xtce.BooleanDataEncoding;
import org.yamcs.xtce.DataEncoding;
import org.yamcs.xtce.FloatDataEncoding;
import org.yamcs.xtce.IntegerDataEncoding;
import org.yamcs.xtce.IntegerDataEncoding.Encoding;
import org.yamcs.xtce.StringDataEncoding;
import org.yamcs.xtce.StringDataEncoding.SizeType;
import org.yamcs.xtceproc.ContainerProcessingContext.ContainerProcessingPosition;
/**
* Decodes TM data according to the specification of the DataEncoding
* @author nm
*
*/
public class DataEncodingDecoder {
ContainerProcessingContext pcontext;
Logger log=LoggerFactory.getLogger(this.getClass().getName());
public DataEncodingDecoder(ContainerProcessingContext pcontext) {
this.pcontext=pcontext;
}
/**
* Extracts the raw uncalibrated parameter value from the packet.
*/
void extractRaw(DataEncoding de, ParameterValue pv){
if(de instanceof IntegerDataEncoding) {
extractRawInteger((IntegerDataEncoding) de, pv);
} else if(de instanceof FloatDataEncoding) {
extractRawFloat((FloatDataEncoding) de, pv);
} else if(de instanceof StringDataEncoding) {
extractRawString((StringDataEncoding) de, pv);
} else if(de instanceof BooleanDataEncoding) {
extractRawBoolean((BooleanDataEncoding) de, pv);
} else if(de instanceof BinaryDataEncoding) {
extractRawBinary((BinaryDataEncoding) de, pv);
} else {
log.error("DataEncoding {} not implemented", de);
throw new IllegalArgumentException("DataEncoding "+de+" not implemented");
}
}
private void extractRawInteger(IntegerDataEncoding ide, ParameterValue pv) {
ContainerProcessingPosition position = pcontext.position;
ByteBuffer bb = position.bb;
// Integer encoded as string, don't even try reading it as int
if(ide.getEncoding() == Encoding.string) {
extractRaw(ide.getStringEncoding(), pv);
return;
}
int byteOffset = (position.bitPosition)/8;
int byteSize = (position.bitPosition + ide.getSizeInBits()-1)/8-byteOffset+1;
int bitOffsetInsideMask = position.bitPosition - 8*byteOffset;
int bitsToShift = 8*byteSize-bitOffsetInsideMask-ide.getSizeInBits();
long mask = (-1L<<(64-ide.getSizeInBits()))>>>(64-ide.getSizeInBits()-bitsToShift);
bb.order(ide.getByteOrder());
long rv=0;
switch(byteSize) {
case 1:
rv = bb.get(byteOffset);
break;
case 2:
rv = bb.getShort(byteOffset);
break;
case 3:
if(ide.getByteOrder()==ByteOrder.BIG_ENDIAN) {
rv = (bb.getShort(byteOffset) & 0xFFFF) << 8;
rv += bb.get(byteOffset + 2) & 0xFF;
} else {
rv = bb.getShort(byteOffset) & 0xFFFF;
rv += (bb.get(byteOffset + 2) & 0xFF) << 16;
}
break;
case 4:
rv = bb.getInt(byteOffset);
break;
case 5:
if(ide.getByteOrder()==ByteOrder.BIG_ENDIAN) {
rv = (bb.getInt(byteOffset) & 0xFFFFFFFFL) << 8;
rv += bb.get(byteOffset + 4) & 0xFF;
} else {
rv = bb.getInt(byteOffset) & 0xFFFFFFFFL;
rv += (bb.get(byteOffset + 4) & 0xFFL) << 32;
}
break;
case 6:
if (ide.getByteOrder()==ByteOrder.BIG_ENDIAN) {
rv = (bb.getInt(byteOffset) & 0xFFFFFFFFL) << 16;
rv += bb.getShort(byteOffset + 4) & 0xFFFF;
} else {
rv = bb.getInt(byteOffset) & 0xFFFFFFFFL;
rv += bb.getShort(byteOffset + 4) & 0xFFFFL << 32;
}
break;
case 7:
if (ide.getByteOrder() == ByteOrder.BIG_ENDIAN) {
rv = (bb.getInt(byteOffset) & 0xFFFFFFFFL) << 24;
rv += (bb.getShort(byteOffset + 4) & 0xFFFFL) << 8;
rv += bb.get(byteOffset + 6) & 0xFFL;
} else {
rv = bb.getInt(byteOffset) & 0xFFFFFFFFL;
rv += (bb.getShort(byteOffset + 4) & 0xFFFFL) << 32;
rv += (bb.get(byteOffset + 6) & 0xFFL) << 48;
}
break;
case 8:
rv = bb.getLong(byteOffset);
break;
default:
log.warn(String.format("parameter extraction for %d bytes not supported, used for %s", byteSize, pv.getParameter()));
}
position.bitPosition+=ide.getSizeInBits();
switch(ide.getEncoding()) {
case twosComplement:
//we shift it to the left first such that the sign bit arrives on the first position
rv= (rv&mask)<<(64-ide.getSizeInBits()-bitsToShift);
rv=rv>>(64-ide.getSizeInBits());
if (ide.getSizeInBits() <= 32)
pv.setRawSignedInteger((int)rv);
else
pv.setRawSignedLong(rv);
break;
case unsigned:
//we use the ">>>" such that the sign bit is not carried
rv=(rv&mask)>>>bitsToShift;
break;
case signMagnitude:
boolean negative = ((rv>>>(ide.getSizeInBits()-1) & 1L) == 1L);
mask >>>= 1; // Don't include sign in mask
rv=(rv&(mask))>>>bitsToShift;
if (negative) {
rv = -rv;
}
break;
default:
throw new UnsupportedOperationException("encoding "+ide.getEncoding()+" not implemented");
}
setRawValue(ide, pv, rv);
}
private static void setRawValue(IntegerDataEncoding ide, ParameterValue pv, long longValue) {
if(ide.getSizeInBits() <= 32) {
if(ide.getEncoding() == Encoding.unsigned) {
pv.setRawUnsignedInteger((int)longValue);
} else {
pv.setRawSignedInteger((int)longValue);
}
} else {
if(ide.getEncoding() == Encoding.unsigned) {
pv.setRawUnsignedLong(longValue);
} else {
pv.setRawSignedLong(longValue);
}
}
}
private void extractRawString(StringDataEncoding sde, ParameterValue pv) {
ContainerProcessingPosition position = pcontext.position;
ByteBuffer bb = position.bb;
if(position.bitPosition%8!=0) {
log.warn("String Parameter that does not start at byte boundary not supported. bitPosition: {}", pcontext.position);
}
int sizeInBytes=0;
switch(sde.getSizeType()) {
case Fixed:
sizeInBytes=sde.getSizeInBits()/8;
break;
case LeadingSize:
for(int i=0;i<sde.getSizeInBitsOfSizeTag();i+=8) {
sizeInBytes=(sizeInBytes<<8) + bb.get(position.bitPosition/8);
position.bitPosition+=8;
}
break;
case TerminationChar:
int byteOffset = position.bitPosition/8;
while(bb.get(byteOffset)!=sde.getTerminationChar()){
sizeInBytes++;
byteOffset++;
}
break;
}
byte[] b=new byte[sizeInBytes];
bb.position(position.bitPosition/8);
bb.get(b);
pv.setRawValue(new String(b));
pv.setBitSize(8*sizeInBytes);
position.bitPosition += 8*sizeInBytes;
if(sde.getSizeType()==SizeType.TerminationChar) {
position.bitPosition+=8;//the termination char
}
}
private void extractRawFloat(FloatDataEncoding de, ParameterValue pv) {
ContainerProcessingPosition position = pcontext.position;
ByteBuffer bb = position.bb;
if(position.bitPosition%8!=0) {
log.warn("Float Parameter that does not start at byte boundary not supported. bitPosition: {}", position.bitPosition);
}
bb.order(de.getByteOrder());
switch(de.getEncoding()) {
case IEEE754_1985:
extractRawIEEE754_1985(de, pv);
break;
case STRING:
extractRaw(de.getStringDataEncoding(), pv);
break;
default:
throw new IllegalArgumentException("Float Encoding "+de.getEncoding()+" not implemented");
}
}
private void extractRawIEEE754_1985(FloatDataEncoding de, ParameterValue pv) {
ContainerProcessingPosition position = pcontext.position;
ByteBuffer bb = position.bb;
if(position.bitPosition%8!=0) {
log.warn("Float Parameter that does not start at byte boundary not supported. bitPosition: {}", pcontext.position);
}
bb.order(de.getByteOrder());
int byteOffset=position.bitPosition/8;
position.bitPosition+=de.getSizeInBits();
if(de.getSizeInBits()==32) {
pv.setRawValue(bb.getFloat(byteOffset));
} else {
pv.setRawValue(bb.getDouble(byteOffset));
}
}
private void extractRawBoolean(BooleanDataEncoding bde, ParameterValue pv) {
ContainerProcessingPosition position = pcontext.position;
ByteBuffer bb = position.bb;
int byteOffset = (position.bitPosition)/8;
int bitOffsetInsideMask = position.bitPosition-8*byteOffset;
int bitsToShift = 8-bitOffsetInsideMask-1;
int mask = (-1<<(32-1))>>>(32-1-bitsToShift);
int rv = bb.get(byteOffset)&0xFF;
rv=(rv&mask)>>>bitsToShift;
position.bitPosition+=1;
pv.setRawValue(rv!=0);
}
private void extractRawBinary(BinaryDataEncoding bde, ParameterValue pv) {
ContainerProcessingPosition position = pcontext.position;
ByteBuffer bb = position.bb;
if(position.bitPosition%8!=0) {
log.warn("Binary Parameter that does not start at byte boundary not supported. bitPosition: {}", pcontext.position);
}
byte[] b = new byte[bde.getSizeInBits()/8];
bb.position(position.bitPosition/8);
bb.get(b);
pv.setRawValue(b);
position.bitPosition += bde.getSizeInBits();
}
}