VLab Issue #4865 - MISC/ASCT latest Data string appears incorrectly

This commit fixes #4865; refs #4864

Change-Id: I024a86831425f7eae776a00b9d6cf07993e74932

Former-commit-id: 9559830d4cbcf9e77cb269403c4ac6cca8a88796
This commit is contained in:
Stephen Gilbert 2014-11-21 08:28:21 -05:00
parent b2067ead8a
commit b53bb2c40e
7 changed files with 558 additions and 502 deletions

View file

@ -0,0 +1,220 @@
/**
*
*/
package gov.noaa.nws.ncep.common.dataplugin.ncscat;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* NcscatMode - Enum class to centralize and encapsulate all the things that
* vary among different satellite and data feed types.
*
* //TODO: Consider moving this information entirely to the bundle and/or
* preferences (.xml/.prm) files, so the Java code can be completely agnostic
* about satellite data types; would allow extended 'configurability', at the
* expense of slightly longer bundle/preference files...
*
* This code has been developed by the SIB for use in the AWIPS2 system.
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 02 Jun 2010 235B B. Hebbard Initial creation.
* 03 Feb 2011 235E B. Hebbard Add support for ambiguity variants.
* 16 Aug 2012 B. Hebbard Add OSCAT / OSCAT_HI
* 11 Apr 2014 1128 B. Hebbard Add longitudeCoding field; change wind sense from boolean to enum.
* 21 Oct 2014 R4865 B. Hebbard (TTR 984) Add file-header length and reportType fields to enable refactor to make decoding more robust (and thereby prevent date and other corruption)
*
* </pre>
*
* @author bhebbard
* @version 1.0
*/
public enum NcscatMode {
// The only ordering constraint here is that if A.pointsPerRow divides
// B.pointsPerRow and A and B have the same byteOrder, then A should come
// before B. (Currently, no such cases exist.) This is so that we can check
// an unknown data file for 'consistency' with these types, in order, and
// choose the first one that matches. (In the hypothetical case above, A and
// B could both pass consistency checking, but we'd want to choose A.)
// @formatter:off ppr hdr
QUIKSCAT ( 76, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "quikscat" ),
QUIKSCAT_HI ( 152, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "quikscat-hi"),
ASCAT ( 42, 0, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "ascat"),
ASCAT_HI ( 82, 0, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "ascat-hi"),
EXASCT ( 42, 0, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN, "Exasct"),
EXASCT_HI ( 82, 0, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN, "Exasct-hi"),
OSCAT ( 36, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "oscat"),
OSCAT_HI ( 76, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN, "oscat-hi"),
WSCAT ( 79, 56, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.SIGNED, ByteOrder.LITTLE_ENDIAN, "wscat"),
UNKNOWN ( 76, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "unknown");
// @formatter:on
private int pointsPerRow; // number of Wind Vector Cell in each scan row
// across satellite track
private WindDirectionSense windDirectionSense; // is the numeric wind
// direction the "from"
// (METEOROLOGICAL)
// direction, or the "to"
// (OCEANOGRAPHIC) direction?
private LongitudeCoding longitudeCoding; // is the two-byte value a SIGNED
// (-18000 -- +18000) or UNSIGNED
// (0 -- 36000) representation of
// the (scaled by 100) longitude
// east of Greenwich?
private ByteOrder byteOrder; // endianess of data in the byte stream
private String reportType; // string to use in reportType field of DB
private int valuesPerPoint = 9; // number of (short int) data values per
// point
private int bytesPerValue = 2; // all are short int
private int fileHeaderLength; // length in bytes of per-file header field,
// if any, in input file, before repeating
// per-row data begins; of interest to decoder
private int rowHeaderLength = 8; // length in bytes of per-row header field
// (2 bytes each for day, hour, min, sec)
private int bytesPerRow; // length in bytes of each row, comprising row
// header and all data values for all points in
// that row
public int getBytesPerRow() {
return bytesPerRow;
}
// Constructor
NcscatMode(int pointsPerRow, int fileHeaderLength,
WindDirectionSense windDirectionSense,
LongitudeCoding longitudeCoding, ByteOrder byteOrder,
String reportType) {
this.pointsPerRow = pointsPerRow;
this.fileHeaderLength = fileHeaderLength;
this.windDirectionSense = windDirectionSense;
this.longitudeCoding = longitudeCoding;
this.byteOrder = byteOrder;
this.reportType = reportType;
bytesPerRow = rowHeaderLength + pointsPerRow * valuesPerPoint
* bytesPerValue;
}
public String getReportType() {
return reportType;
}
public int getPointsPerRow() {
return pointsPerRow;
}
public int getFileHeaderLength() {
return fileHeaderLength;
}
public WindDirectionSense getWindDirectionSense() {
return windDirectionSense;
}
public LongitudeCoding getLongitudeCoding() {
return longitudeCoding;
}
public ByteOrder getByteOrder() {
return byteOrder;
}
public enum WindDirectionSense { // numeric direction value gives...
METEOROLOGICAL, // degrees FROM which wind is blowing
OCEANOGRAPHIC // degrees TO which wind is blowing
}
public enum LongitudeCoding { // 2-byte wvc_lon (Wind Vector Cell -
// longitude) field is (x0.01 deg)...
SIGNED, // SIGNED short (-18000..+18000)
UNSIGNED // UNSIGNED short (0..36000 east of Greenwich)
}
public static NcscatMode stringToMode(String name) {
// Given a string, return the corresponding enum
NcscatMode returnValue = null;
name = name.toUpperCase();
name = name.replaceAll("-", "_");
// TODO: Remove ambiguity number??
try {
returnValue = valueOf(name);
} catch (IllegalArgumentException e) {
// TODO: Signal unrecognized Ncscat mode string
returnValue = UNKNOWN;
}
return returnValue;
}
public boolean consistentWith(ByteBuffer byteBuffer) {
// Given a ByteBuffer containing ingested binary data of unknown
// satellite type (mode), determine whether data are consistent
// with "this" mode. We do this by seeing if date/hour fields
// repeat where they would be expected to appear, and contain
// reasonable values for those fields.
// TODO: Consider moving this to decoder...? (Kind of prefer
// encapsulating it here, but also like enums to be rather
// minimalistic...? -bh)
byteBuffer.order(byteOrder);
int base = fileHeaderLength;
for (int cycle = 0; cycle < 2; cycle++) {
// DAY (of year) candidate fields of consecutive rows out of
// range...
short thisCandidateDayOfYear = byteBuffer.getShort(base);
short nextCandidateDayOfYear = byteBuffer.getShort(base
+ bytesPerRow);
if (thisCandidateDayOfYear < 1 || thisCandidateDayOfYear > 366
|| nextCandidateDayOfYear < 1
|| nextCandidateDayOfYear > 366)
// ...means (right away) data can't be this type
return false;
// ...but if they don't match...
if (thisCandidateDayOfYear != nextCandidateDayOfYear) {
// ...can't rule it out (since first two rows could straddle a
// day boundary)...but can skip checking hour and go to check
// next pair of rows...
break;
}
// HOUR candidate fields (of consecutive rows) in range...?
short thisCandidateHour = byteBuffer.getShort(base + 2);
short nextCandidateHour = byteBuffer.getShort(base + 2
+ bytesPerRow);
if (thisCandidateHour < 0 || thisCandidateHour > 23
|| nextCandidateHour < 0 || nextCandidateHour > 23)
return false;
// ...and if they do match (as well as day field above), then we
// conclude consistency
if (byteBuffer.getShort(base) == byteBuffer.getShort(base
+ bytesPerRow)) {
return true;
}
// ...but if they don't, again, first two rows could straddle an
// hour boundary, so go to next pair of rows (cycle)...
base += bytesPerRow;
}
// We've made it through 2 consecutive cycles, and neither (1st/2nd
// nor 2nd/3rd) day+hour match, so conclude (assuming real consecutive
// rows are not separated by an hour or more) data not consistent with
// this type
return false;
}
}

View file

@ -2,7 +2,8 @@
*
* Ncscat Decoder
*
* This java class decodes Ascat quikscat (NESDIS File) raw data.
* This Java class decodes ocean winds scatterometer/radiometer (NESDIS File) raw data.
*
* HISTORY
*
* Date Author Description
@ -10,6 +11,7 @@
* 11/2009 Uma Josyula Initial creation
* 01/2011 B. Hebbard Handle ambiguity variants
* 07/2012 B. Hebbard Handle OSCAT / OSCAT_HI
* 10/2014 B. Hebbard Get reportType from NcscatMode, instead of if-then-else'ing over hardcode integer lengths
*
* This code has been developed by the SIB for use in the AWIPS2 system.
*/
@ -67,7 +69,7 @@ public class NcscatDecoder extends AbstractDecoder {
NcscatProcessing sProcess = new NcscatProcessing();
sProcess.setInputFileName(plugin);
messageData = sProcess.separate(data);
recLength = NcscatProcessing.getRecordLength();
recLength = sProcess.getRecordLength();
if (messageData != null) {
record = new NcscatRecord();
@ -76,38 +78,7 @@ public class NcscatDecoder extends AbstractDecoder {
record.setEndTime(sProcess.getEndTime());
record.setRecordLength(recLength);
record.setDataTime(new DataTime(sProcess.getStartTime()));
if (record.getRecordLength() == 688) {
if (plugin.equalsIgnoreCase("oscat")) {
record.setReportType("oscat-hi");
} else {
record.setReportType("quikscat");
}
} else if (record.getRecordLength() == 1372) {
record.setReportType("quikscat-hi");
} else if (record.getRecordLength() == 382) {
if (plugin.equalsIgnoreCase("ascatx")) {
record.setReportType("Exasct");
} else {
record.setReportType("ascat");
}
} else if (record.getRecordLength() == 742) {
if (plugin.equalsIgnoreCase("ascatx")) {
record.setReportType("Exasct-hi");
} else {
record.setReportType("ascat-hi");
}
} else if (record.getRecordLength() == 328) {
record.setReportType("oscat");
} else if (record.getRecordLength() == 715) {
record.setReportType("wscat");
}
record.setReportType(sProcess.getNcscatMode().getReportType());
// If this is an (numbered) 'ambiguity' variant, add suffix to
// reportType
@ -178,20 +149,16 @@ public class NcscatDecoder extends AbstractDecoder {
}
// TODO: Review this temporary fix by BH to see if a broader
// refactor might be better.
// Problem: Without the added 'else' below, non-local variable
// "plugin" will
// retain its previous value if the pattern matcher fails above;
// this will happen
// if the file suffix/extension (after the ".") is shorter than 5
// characters.
// Scenario: A file with suffix ".ascatx" is ingested, then later a
// ".qsct" file.
// "plugin" will retain its previous value if the pattern matcher
// fails above; this will happen if the file suffix/extension (after
// the ".") is shorter than 5 characters. Scenario: A file with
// suffix ".ascatx" is ingested, then later a ".qsct" file.
// Variable "plugin" will be set to "ascatx" by the first, but will
// retain this
// same value for the second; logic in NcscatProcessing.doSeparate()
// will as a
// result interpret the big-endian ".qsct" data as little endian
// format.
// retain this same value for the second; logic in
// NcscatProcessing.doSeparate() will as a result interpret the
// big-endian ".qsct" data as little endian format.
// Alternate (better): Could make "plugin" local to this method
else {
plugin = "";
@ -200,20 +167,14 @@ public class NcscatDecoder extends AbstractDecoder {
// NcscatProcessing
// Handle ambiguity variants: In addition to files containing the
// primary
// (greatest likelihood) wind vector solutions, for some data types
// we also
// receive (typically 2 or 4) 'ambiguity' variants for the same
// point locations,
// but with (lesser likelihood) solutions. Since these cannot be
// distinguished
// from the primary files based on data format/content alone, we
// look for a
// (prearranged) telltale pattern somewhere in the file name. If
// found, we
// remember the indicated ambiguity number, for later tagging of the
// database
// record.
// primary (greatest likelihood) wind vector solutions, for some
// data types we also receive (typically 2 or 4) 'ambiguity'
// variants for the same point locations, but with (lesser
// likelihood) solutions. Since these cannot be distinguished from
// the primary files based on data format/content alone, we
// look for a (prearranged) telltale pattern somewhere in the file
// name. If found, we remember the indicated ambiguity number, for
// later tagging of the database record.
pattern = Pattern.compile("ambig([1-9])");
matcher = pattern.matcher(fileName);
if (matcher.find()) {

View file

@ -7,14 +7,18 @@
* <pre>
* Uma Josyula 11/2009 Creation
* B. Hebbard 07/2012 Handle OSCAT / OSCAT_HI
* B. Hebbard R4865/TTR984 10/2014 Tighten code that infers satellite type
* (from date/hour field recurrence) and
* date handling to prevent garbage from being
* interpreted as dates decades in the future.
* </pre>
*
* This code has been developed by the SIB for use in the AWIPS system.
*/
package gov.noaa.nws.ncep.edex.plugin.ncscat.util;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatPoint;
import java.nio.ByteBuffer;
@ -24,369 +28,357 @@ import java.util.Calendar;
import java.util.List;
import java.util.NoSuchElementException;
import javax.xml.bind.JAXBException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Logger;
import com.raytheon.uf.common.serialization.SerializationUtil;
public class NcscatProcessing {
private final Logger log = Logger.getLogger(getClass().getName());
private final Log theLogger = LogFactory.getLog(getClass());
private final Logger log = Logger.getLogger(getClass().getName());
private final Log theLogger = LogFactory.getLog(getClass());
private List<NcscatPoint> item;
List<Byte> newMessage=null;
NcscatPoint sPointObj ;
private Calendar endTime ;
private static int scatNumber ;
private Calendar startTime;
private static int recordLength;
private String inputFileName;
private List<NcscatPoint> item;
List<Byte> newMessage = null;
public NcscatProcessing() {
}
NcscatPoint sPointObj;
public byte[] separate(byte[] data) {
doSeparate(data);
try {
if(newMessage ==null){
return (byte[])null;
}
else {
byte[] tempBytes = listByteTobyteArray(newMessage);
return tempBytes;
}
private Calendar endTime;
} catch (NoSuchElementException e) {
return (byte[])null;
}
private int scatNumber;
}
private Calendar startTime;
private int recordLength;
/**
* @param message
* separate bulletins
*/
private void doSeparate(byte[] message) {
int ji=0;
int n = 0,doubleNcscat =0,scatXLen =0;
ByteBuffer byteBuffer = null;
byteBuffer = ByteBuffer.allocate(message.length);
byteBuffer.put(message,0,message.length);
int bitNum =0;
int tempLength=message.length;
int day,hour,min,sec;
private String inputFileName;
startTime = Calendar.getInstance();
endTime = Calendar.getInstance();
private NcscatMode ncscatMode;
// Attempt to discriminate type of data by looking for 'period' of repeating date+hour fields.
// TODO !! Guard against false negative which could occur if first 2 times straddle hour boundary,
// !! possibly by checking for match EITHER 1st-&-2nd OR 2nd-&-3rd
if ((byteBuffer.getShort(0) == byteBuffer.getShort(1484)) && (byteBuffer.getShort(2) == byteBuffer.getShort(1486))) {
// ASCAT_HI or EXASCT_HI (...which are big and little endian, respectively)
n=1476;
scatNumber=82;
recordLength=742;
}
else if((byteBuffer.getShort(0)== byteBuffer.getShort(764)) &&(byteBuffer.getShort(2)== byteBuffer.getShort(766))) {
// ASCAT or EXASCT (...which are big and little endian, respectively)
n=756;
scatNumber=42;
recordLength=382;
}
else if ((byteBuffer.getShort(0)== byteBuffer.getShort(1376)) &&(byteBuffer.getShort(2)== byteBuffer.getShort(1378))) {
// QUIKSCAT -OR- OSCAT_HI (...which are big and little endian, respectively)
n=1368;
scatNumber = 76;
recordLength =688;
}
else if ((byteBuffer.getShort(0) == byteBuffer.getShort(656)) && (byteBuffer.getShort(2) == byteBuffer.getShort(658))) {
// OSCAT
n=648;
scatNumber = 36;
recordLength = 328;
}
else if ((byteBuffer.getShort(0) == byteBuffer.getShort(2744)) && (byteBuffer.getShort(2) == byteBuffer.getShort(2746))) {
// QUIKSCAT_HI
n=2736;
recordLength = 1372;
scatNumber = 152;
}
else if((byteBuffer.getShort(56)== byteBuffer.getShort(1486)) &&(byteBuffer.getShort(58)== byteBuffer.getShort(1488))){
// WSAT
n=1422;
scatNumber=79;
recordLength=715;
//TODO: Review this temporary fix by BH to see if a broader refactor might be better.
// For WindSat -- which has a 56-byte header once per file, remove it from the
// message array here, so we don't have to mess with it separately later.
tempLength=message.length - 56;
byte[] xmessage = new byte[tempLength];
for (int i = 0; i<tempLength; i++) {
xmessage[i] = message[i+56];
public NcscatProcessing() {
}
public byte[] separate(byte[] data) {
doSeparate(data);
try {
if (newMessage == null) {
return (byte[]) null;
} else {
byte[] tempBytes = listByteTobyteArray(newMessage);
return tempBytes;
}
message = xmessage;
byteBuffer = null;
byteBuffer = ByteBuffer.allocate(tempLength);
byteBuffer.put(message,0,tempLength);
/* Original code as follows...
byteBuffer = null;
tempLength=message.length -56;
byteBuffer = ByteBuffer.allocate(tempLength);
byteBuffer.put(message,56,tempLength);
*/
//END of temporary fix by BH - Part 1 (compare Part 4)
}
//TODO: Review this temporary fix by BH to see if a broader refactor might be better.
// Endianess correction must be applied to the 4-short date/time field on each
// line. Here we use set the ByteOrder of the byteBuffer, so that the 4
// getShort(...) calls below will do the byte-flipping for us. We also
// remember byteOrder for a bit later... (Part 3)
//
ByteOrder byteOrder;
if ( (n == 1422) || // WSAT
((n == 1368) && "oscat".equalsIgnoreCase(getInputFileName())) || // OSCAT_HI
"ascatx".equalsIgnoreCase(getInputFileName()) ) { // EXASCT or EXASCT_HI
byteOrder = ByteOrder.LITTLE_ENDIAN;
}
else {
byteOrder = ByteOrder.BIG_ENDIAN;
}
byteBuffer.order(byteOrder);
//END of temporary fix by BH - Part 2 (new code added only)
try {
if(message!=null){
int dNcscatMult =0;
doubleNcscat = scatNumber*2;
scatXLen =doubleNcscat*9+8;
while (ji < tempLength) {
day = byteBuffer.getShort(ji) ;
hour= byteBuffer.getShort(ji+2);
min = byteBuffer.getShort(ji+4);
sec = byteBuffer.getShort(ji+6);
if (day<0) break;
if(ji<8){
//stTime = endTime;
startTime.set(Calendar.DAY_OF_YEAR, day);
startTime.set(Calendar.HOUR_OF_DAY, hour);
startTime.set(Calendar.MINUTE, min);
startTime.set(Calendar.SECOND, sec);
}
//else{
endTime.set(Calendar.DAY_OF_YEAR, day);
endTime.set(Calendar.HOUR_OF_DAY, hour);
endTime.set(Calendar.MINUTE, min);
endTime.set(Calendar.SECOND, sec);
//}
ji=ji+scatXLen;
}//for while
newMessage= new ArrayList<Byte>(tempLength);
while(bitNum<tempLength){
int consBitNum = bitNum;
while (bitNum<consBitNum+8)
{
//TODO: Review this temporary fix by BH to see if a broader refactor might be better.
// Here we again apply endianess correction to the 4-short date/time field on each
// line. Above (via the ByteBuffer) was just for determining startTime and endTime
// of the entire data set. Here we do it again as the 'message' array is moved to
// newMessage (ArrayList) for later return and eventual writing to the HDF5 data
// for each scan row. Note that the byte-swapping was already done below for the
// data fields in each row following the leading date/time (in the process of
// regrouping the data so data for each point is together); here we do it for the
// date as well (where no other regrouping is required).
//
int offsetInDate = bitNum - consBitNum; // 0 thru 7
if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
// need to flip the 2 bytes of each short; map 0 1 2 3 4 5 6 7 to 1 0 3 2 5 4 7 6, respectively
offsetInDate = offsetInDate/2*2+(1-offsetInDate%2);
}
newMessage.add((Byte)message[consBitNum+offsetInDate]);
/* Original code as follows...
newMessage.add((Byte)message[bitNum]);
*/
//END of temporary fix by BH - Part 3
bitNum++;
}//date field 8 bytes
consBitNum = bitNum;
while((scatXLen*dNcscatMult+7)<=bitNum && bitNum<(scatXLen*(dNcscatMult+1))){
int calc=0;
for (int qNoIndex =0; qNoIndex<doubleNcscat;qNoIndex++){
for(int rou=0;rou<=8;rou++){
if(bitNum<tempLength){
calc=consBitNum+doubleNcscat*rou+qNoIndex;
//TODO: Review this temporary fix by BH to see if a broader refactor might be better.
// Problem with the following is it applies the 56-byte bias (for WindSat) to the
// address to copy from, but for the date transfer code to work above, bitNum
// must already have had this bias applied (fix above moved it), and so consBitNum
// -- the base address for this row of data, after the date field -- must also
// have already reflected this correction. (Note that the 56-byte header is once
// per file, rather than once per row.)
/* Original code [removed] as follows...
if(recordLength==715){
calc=calc+56;
}
*/
//END of temporary fix by BH - Part 4 (compare Part 1)
if(byteOrder == ByteOrder.LITTLE_ENDIAN) {// swap the bytes
newMessage.add(message[calc+1]);
bitNum++;
newMessage.add(message[calc]);//since 2 bytes form a short
bitNum++;
}
else{
newMessage.add(message[calc]);
bitNum++;
newMessage.add(message[calc+1]);//since 2 bytes form a short
bitNum++;
}
}//end of if
}//end of for
qNoIndex++;
}
dNcscatMult++;
}
}
}//if message is not null
}// end of try block
catch (Exception e) {
e.printStackTrace();
if (log.isInfoEnabled()) {
log.info("No valid records found!");
}
theLogger.warn("No valid records found!");
}
return;
}
} catch (NoSuchElementException e) {
return (byte[]) null;
}
}
/**
* processHDF5Data -- SHOULD NOT BE USED IN PRESENT FORM
* Please see NcscatResource.processHDF5Data(...)
* @param message
* separate bulletins
*/
private void doSeparate(byte[] message) {
ByteBuffer byteBuffer = ByteBuffer.allocate(message.length);
byteBuffer.put(message, 0, message.length);
int tempLength = message.length;
startTime = Calendar.getInstance();
startTime.setLenient(false); // guard against wild values
startTime.set(Calendar.MILLISECOND, 0);
endTime = Calendar.getInstance();
endTime.setLenient(false); // guard against wild values
endTime.set(Calendar.MILLISECOND, 0);
// Determine type of data (that is, which satellite and resolution)
// by examining it for unique consistency with known characteristics
NcscatMode matchedMode = NcscatMode.UNKNOWN;
for (NcscatMode mode : NcscatMode.values()) {
if (mode.consistentWith(byteBuffer)) {
matchedMode = mode;
break;
}
}
if (matchedMode == NcscatMode.UNKNOWN) {
// TODO: Log error/warning -- and quit?
}
ncscatMode = matchedMode;
// Set for historical compatibility...
scatNumber = ncscatMode.getPointsPerRow();
// assert ncscatMode.getBytesPerRow() % 2 == 0;
recordLength = ncscatMode.getBytesPerRow() / 2;
if (ncscatMode.getFileHeaderLength() > 0) {
// If there is a file header (separate from per-row headers), remove
// it from the message array here, so we don't have to mess with it
// separately later.
tempLength = message.length - ncscatMode.getFileHeaderLength();
byte[] xmessage = new byte[tempLength];
for (int i = 0; i < tempLength; i++) {
xmessage[i] = message[i + ncscatMode.getFileHeaderLength()];
}
message = xmessage;
byteBuffer.clear();
byteBuffer = null;
byteBuffer = ByteBuffer.allocate(tempLength);
byteBuffer.put(message, 0, tempLength);
}
// Endianess correction must be applied to the 4-short date/time field
// on each line. Here we set the ByteOrder of the byteBuffer, so
// that the 4 getShort(...) calls below will do the byte-flipping for
// us.
//
ByteOrder byteOrder = ncscatMode.getByteOrder();
byteBuffer.order(byteOrder);
try {
if (message != null) {
int ji = 0;
int dNcscatMult = 0;
int doubleNcscat = scatNumber * 2;
int scatXLen = doubleNcscat * 9 + 8;
int byteNum = 0;
// Make a pass through all rows in file, just to determine
// earliest and latest times
int day, hour, min, sec;
while (ji < tempLength) {
day = byteBuffer.getShort(ji);
hour = byteBuffer.getShort(ji + 2);
min = byteBuffer.getShort(ji + 4);
sec = byteBuffer.getShort(ji + 6);
if (day < 1 || day > 366 || hour < 0 || hour > 23
|| min < 0 || min > 59 || sec < 0 || sec > 60) {
// TODO log error?
break;// TODO continue?
}
if (ji < 8) { // first row
startTime.set(Calendar.DAY_OF_YEAR, day);
startTime.set(Calendar.HOUR_OF_DAY, hour);
startTime.set(Calendar.MINUTE, min);
startTime.set(Calendar.SECOND, sec);
}
// Don't know ahead of time which row will be last
// so set each time; last one will remain endTime
endTime.set(Calendar.DAY_OF_YEAR, day);
endTime.set(Calendar.HOUR_OF_DAY, hour);
endTime.set(Calendar.MINUTE, min);
endTime.set(Calendar.SECOND, sec);
ji = ji + scatXLen;
}// for while
// Time bounds scan done; now go back through the row data and
// rearrange as needed.
newMessage = new ArrayList<Byte>(tempLength);
while (byteNum < tempLength) {
int consByteNum = byteNum;
while (byteNum < consByteNum + 8) {
// Here we again apply endianess correction to the
// 4-short date/time field on each row. Above (via the
// ByteBuffer) was just for determining startTime and
// endTime of the entire data set. Here we do it again
// as the 'message' array is moved to newMessage
// (ArrayList) for later return and eventual writing to
// the HDF5 data for each scan row. Note that the
// byte-swapping is done below for the data fields in
// each row following the leading date/time (in the
// process of regrouping the data so data for each point
// is together); here we do it for the date as well
// (where no other regrouping is required).
//
int offsetInDate = byteNum - consByteNum; // 0 thru 7
if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
// need to flip the 2 bytes of each short; map 0 1 2
// 3 4 5 6 7 to 1 0 3 2 5 4 7 6, respectively
offsetInDate = offsetInDate / 2 * 2
+ (1 - offsetInDate % 2);
}
newMessage.add((Byte) message[consByteNum
+ offsetInDate]);
byteNum++;
}// date field 8 bytes
// Done with row header (date/time); now start on the data
// for points in that row. (consByteNum is index of first
// byte of such data .)
consByteNum = byteNum;
// In the incoming data for a row, all values for each
// single parameter are grouped together (for all points in
// the row). The following code "shuffles" things so that
// all values for a single point are grouped together
// (that is, all parameters for a given point). In the
// process, proper endianess order is set for the two bytes
// making up each data value.
while ((scatXLen * dNcscatMult + 7) <= byteNum
&& byteNum < (scatXLen * (dNcscatMult + 1))) {
int calc = 0;
for (int qNoIndex = 0; qNoIndex < doubleNcscat; qNoIndex++) {
for (int rou = 0; rou <= 8; rou++) {
if (byteNum < tempLength) {
calc = consByteNum + doubleNcscat * rou
+ qNoIndex;
if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
// swap the two bytes making up the
// short
newMessage.add(message[calc + 1]);
byteNum++;
newMessage.add(message[calc]);
byteNum++;
} else { // ByteOrder.BIG_ENDIAN
// preserve order of the two bytes
// making up the short
newMessage.add(message[calc]);
byteNum++;
newMessage.add(message[calc + 1]);
byteNum++;
}
} // end of if
} // end of for rou
qNoIndex++;
} // end of for qNoIndex
dNcscatMult++;
} // end of while
} // while (each row)
}// if message is not null
}// end of try block
catch (Exception e) {
e.printStackTrace();
if (log.isInfoEnabled()) {
log.info("No valid records found!");
}
theLogger.warn("No valid records found!");
}
return;
}
public NcscatMode getNcscatMode() {
return ncscatMode;
}
/**
* processHDF5Data -- SHOULD NOT BE USED IN PRESENT FORM Please see
* NcscatResource.processHDF5Data(...)
*
* @return List<NcscatPoint>
*
* @deprecated Please see NcscatResource.processHDF5Data(...)
*/
@Deprecated
public List<NcscatPoint> processHDF5Data(byte[] hdf5Msg) {
int ji = 0, bitNum = 0;
int day, hour, min, sec;
item = new ArrayList<NcscatPoint>();
// TODO - Caution! Separate startTime Calendar object needs to be
// allocated for each point row, since will be shared by all points
// in that row. See below.
startTime = Calendar.getInstance();
ByteBuffer byteBuffer = null;
byteBuffer = ByteBuffer.allocate(hdf5Msg.length);
byteBuffer.put(hdf5Msg, 0, hdf5Msg.length);
while (ji < hdf5Msg.length) {
public List<NcscatPoint> processHDF5Data(byte[] hdf5Msg){
int ji=0, bitNum=0;
int day,hour,min,sec;
item= new ArrayList<NcscatPoint>();
// TODO - Caution! Separate startTime Calendar object needs to be allocated
// for each point row, since will be shared by all points
// in that row. See below.
startTime = Calendar.getInstance();
ByteBuffer byteBuffer = null;
byteBuffer = ByteBuffer.allocate(hdf5Msg.length);
byteBuffer.put(hdf5Msg,0,hdf5Msg.length);
day = byteBuffer.getShort(bitNum);
hour = byteBuffer.getShort(bitNum + 2);
min = byteBuffer.getShort(bitNum + 4);
sec = byteBuffer.getShort(bitNum + 6);
ji = ji + 8;
bitNum = bitNum + 8;
// TODO - Caution! Need to allocate new startTime here...
startTime.set(Calendar.DAY_OF_YEAR, day);
startTime.set(Calendar.HOUR_OF_DAY, hour);
startTime.set(Calendar.MINUTE, min);
startTime.set(Calendar.SECOND, sec);
for (int j = ji; j < ji + scatNumber * 18
&& bitNum < hdf5Msg.length; j = j + 18) {
sPointObj = new NcscatPoint();
// TODO - continued - otherwise all points in all rows get same
// time here
sPointObj.setStTime(startTime);
sPointObj.setLat(byteBuffer.getShort(j));
sPointObj.setLon(byteBuffer.getShort(j + 2));
sPointObj.setIql(byteBuffer.getShort(j + 4));
sPointObj.setIsp(byteBuffer.getShort(j + 6));
sPointObj.setIdr(byteBuffer.getShort(j + 8));
sPointObj.setIrn(byteBuffer.getShort(j + 10));
sPointObj.setIb1(byteBuffer.getShort(j + 12));
sPointObj.setIb2(byteBuffer.getShort(j + 14));
sPointObj.setIb3(byteBuffer.getShort(j + 16));
bitNum = bitNum + 18;
item.add(sPointObj);
while ( ji < hdf5Msg.length) {
}// for
day = byteBuffer.getShort(bitNum) ;
hour= byteBuffer.getShort(bitNum+2);
min = byteBuffer.getShort(bitNum+4);
sec = byteBuffer.getShort(bitNum+6);
ji=ji+8;
bitNum=bitNum+8;
// TODO - Caution! Need to allocate new startTime here...
startTime.set(Calendar.DAY_OF_YEAR, day);
startTime.set(Calendar.HOUR_OF_DAY, hour);
startTime.set(Calendar.MINUTE, min);
startTime.set(Calendar.SECOND, sec);
for(int j=ji;j<ji+scatNumber*18 && bitNum <hdf5Msg.length;j=j+18){
sPointObj = new NcscatPoint();
// TODO - continued - otherwise all points in all rows get same time here
sPointObj.setStTime(startTime);
sPointObj.setLat(byteBuffer.getShort(j));
sPointObj.setLon(byteBuffer.getShort(j+2));
sPointObj.setIql(byteBuffer.getShort(j+4));
sPointObj.setIsp(byteBuffer.getShort(j+6));
sPointObj.setIdr(byteBuffer.getShort(j+8));
sPointObj.setIrn(byteBuffer.getShort(j+10));
sPointObj.setIb1(byteBuffer.getShort(j+12));
sPointObj.setIb2(byteBuffer.getShort(j+14));
sPointObj.setIb3(byteBuffer.getShort(j+16));
bitNum=bitNum+18;
item.add(sPointObj);
ji = bitNum;
}//for
}// for while
ji=bitNum;
return item;
}//for while
}
return item;
public Calendar getStartTime() {
return startTime;
}
}
public void setStartTime(Calendar startTime) {
this.startTime = startTime;
}
public Calendar getStartTime() {
return startTime;
}
public Calendar getEndTime() {
return endTime;
}
public void setEndTime(Calendar endTime) {
this.endTime = endTime;
}
public void setStartTime(Calendar startTime) {
this.startTime = startTime;
}
private static byte[] listByteTobyteArray(List<Byte> temp) {
byte[] byteArray = new byte[temp.size()];
int jkl = 0;
for (Byte current : temp) {
byteArray[jkl] = current;
jkl++;
}
public Calendar getEndTime() {
return endTime;
}
public void setEndTime(Calendar endTime) {
this.endTime = endTime;
}
return byteArray;
}
private static byte[] listByteTobyteArray(List<Byte> temp) {
byte[] byteArray = new byte[temp.size()];
int jkl=0;
for(Byte current:temp){
byteArray[jkl] =current;
jkl++;
}
public int getRecordLength() {
return recordLength;
}
return byteArray;
}
public void setRecordLength(int recordLength) {
this.recordLength = recordLength;
}
public static int getRecordLength() {
return recordLength;
}
public String getInputFileName() {
return inputFileName;
}
public static void setRecordLength(int recordLength) {
NcscatProcessing.recordLength = recordLength;
}
public String getInputFileName() {
return inputFileName;
}
public void setInputFileName(String inputFileName) {
this.inputFileName = inputFileName;
}
public void setInputFileName(String inputFileName) {
this.inputFileName = inputFileName;
}
}

View file

@ -1,5 +1,6 @@
package gov.noaa.nws.ncep.viz.rsc.ncscat.rsc;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode;
import gov.noaa.nws.ncep.viz.common.ui.color.ColorButtonSelector;
import gov.noaa.nws.ncep.viz.resources.INatlCntrsResourceData;
import gov.noaa.nws.ncep.viz.resources.attributes.AbstractEditResourceAttrsDialog;

View file

@ -1,120 +0,0 @@
/**
*
*/
package gov.noaa.nws.ncep.viz.rsc.ncscat.rsc;
import java.nio.ByteOrder;
/**
* NcscatMode - Enum class to centralize and encapsulate all the things that
* vary among different satellite and data feed types.
*
* //TODO: Consider moving this information entirely to the bundle and/or
* preferences (.xml/.prm) files, so the Java code can be completely agnostic
* about satellite data types; would allow extended 'configurability', at the
* expense of slightly longer bundle/preference files...
*
* This code has been developed by the SIB for use in the AWIPS2 system.
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 02 Jun 2010 235B B. Hebbard Initial creation.
* 03 Feb 2011 235E B. Hebbard Add support for ambiguity variants.
* 16 Aug 2012 B. Hebbard Add OSCAT / OSCAT_HI
* 11 Apr 2014 1128 B. Hebbard Add longitudeCoding field; change wind sense from boolean to enum.
*
* </pre>
*
* @author bhebbard
* @version 1.0
*/
public enum NcscatMode {
// @formatter:off
QUIKSCAT ( 76, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ),
QUIKSCAT_HI ( 152, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ),
ASCAT ( 42, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ),
ASCAT_HI ( 82, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ),
EXASCT ( 42, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN ),
EXASCT_HI ( 82, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN ),
OSCAT ( 36, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ),
OSCAT_HI ( 76, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN ),
WSCAT ( 79, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.SIGNED, ByteOrder.LITTLE_ENDIAN ),
UNKNOWN ( 76, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN );
// @formatter:on
private int pointsPerRow; // number of Wind Vector Cell in each scan row
// across satellite track
private WindDirectionSense windDirectionSense; // is the numeric wind
// direction the "from"
// (METEOROLOGICAL)
// direction, or the "to"
// (OCEANOGRAPHIC) direction?
private LongitudeCoding longitudeCoding; // is the two-byte value a SIGNED
// (-18000 -- +18000) or UNSIGNED
// (0 -- 36000) representation of
// the (scaled by 100) longitude
// east of Greenwich?
private ByteOrder byteOrder; // endianess of data in the byte stream
// TODO: could add more here, to simplify (switch) code with more table
// driven logic. But see above note about .xml/.prm...
// Constructor
NcscatMode(int pointsPerRow, WindDirectionSense windDirectionSense,
LongitudeCoding longitudeCoding, ByteOrder byteOrder) {
this.pointsPerRow = pointsPerRow;
this.windDirectionSense = windDirectionSense;
this.longitudeCoding = longitudeCoding;
this.byteOrder = byteOrder;
}
public int getPointsPerRow() {
return pointsPerRow;
}
public WindDirectionSense getWindDirectionSense() {
return windDirectionSense;
}
public LongitudeCoding getLongitudeCoding() {
return longitudeCoding;
}
public ByteOrder getByteOrder() {
return byteOrder;
}
public static NcscatMode stringToMode(String name) {
// Given a string, return the corresponding enum
NcscatMode returnValue = null;
name = name.toUpperCase();
name = name.replaceAll("-", "_");
// TODO: Remove ambiguity number??
try {
returnValue = valueOf(name);
} catch (IllegalArgumentException e) {
// TODO: Signal unrecognized Ncscat mode string
returnValue = UNKNOWN;
}
return returnValue;
}
public enum WindDirectionSense { // numeric direction value gives...
METEOROLOGICAL, // degrees FROM which wind is blowing
OCEANOGRAPHIC // degrees TO which wind is blowing
}
public enum LongitudeCoding { // 2-byte wvc_lon (Wind Vector Cell -
// longitude) field is (x0.01 deg)...
SIGNED, // SIGNED short (-18000..+18000)
UNSIGNED // UNSIGNED short (0..36000 east of Greenwich)
}
}

View file

@ -1,7 +1,10 @@
package gov.noaa.nws.ncep.viz.rsc.ncscat.rsc;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatPoint;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatRecord;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode.LongitudeCoding;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode.WindDirectionSense;
import gov.noaa.nws.ncep.gempak.parameters.colorbar.ColorBarOrientation;
import gov.noaa.nws.ncep.ui.pgen.display.DisplayElementFactory;
import gov.noaa.nws.ncep.ui.pgen.display.IDisplayable;
@ -13,8 +16,6 @@ import gov.noaa.nws.ncep.viz.resources.AbstractNatlCntrsResource;
import gov.noaa.nws.ncep.viz.resources.INatlCntrsResource;
import gov.noaa.nws.ncep.viz.resources.colorBar.ColorBarResource;
import gov.noaa.nws.ncep.viz.resources.colorBar.ColorBarResourceData;
import gov.noaa.nws.ncep.viz.rsc.ncscat.rsc.NcscatMode.LongitudeCoding;
import gov.noaa.nws.ncep.viz.rsc.ncscat.rsc.NcscatMode.WindDirectionSense;
import gov.noaa.nws.ncep.viz.ui.display.ColorBar;
import gov.noaa.nws.ncep.viz.ui.display.NCMapDescriptor;

View file

@ -4,6 +4,7 @@ import gov.noaa.nws.ncep.viz.resources.AbstractNatlCntrsRequestableResourceData;
import gov.noaa.nws.ncep.viz.resources.INatlCntrsResourceData;
import gov.noaa.nws.ncep.viz.resources.attributes.RGBColorAdapter;
import gov.noaa.nws.ncep.viz.ui.display.ColorBar;
import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode;
import gov.noaa.nws.ncep.ui.pgen.display.IVector.VectorType;
import java.util.regex.Matcher;