lrgs.common.DcpMsg Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opendcs Show documentation
Show all versions of opendcs Show documentation
A collection of software for aggregatting and processing environmental data such as from NOAA GOES satellites.
The newest version!
/*
* $Id$
*/
package lrgs.common;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
import java.util.TimeZone;
import lrgs.archive.XmitWindow;
import decodes.consumer.HtmlFormatter;
import decodes.db.Constants;
import decodes.sql.DbKey;
import ilex.util.ArrayUtil;
import ilex.util.ByteUtil;
import ilex.util.IDateFormat;
import ilex.util.TwoDigitYear;
import ilex.util.Logger;
/**
Data Structure that holds a single DCP Message
*/
public class DcpMsg
{
/**
* Primary database key for this record. This will be undefinedId for messages
* not stored in a database.
*/
private DbKey recordId = Constants.undefinedId;
/**
The DCP message data, including header, is stored as an
array of bytes.
*/
private byte[] data = null;
/** The flag bits */
public int flagbits = 0;
/** The DROT (LRGS) time stamp */
private Date localRecvTime = DcpMsgIndex.zeroDate;
/** (legacy) file name constructed from DCP address & sequence num */
private String seqFileName = null;
/** DOMSAT (or other) sequence number for this message (-1 if unknown) */
private int sequenceNum = -1;
public byte mergeFilterCode = 0;
/** Baud rate if known, 0 = unknown */
private int baud = 0;
/** Time stamp (msec) of carrier start (0 = unknown) */
private Date carrierStart;
/** Time stamp (msec) of carrier stop (0 = unknown) */
private Date carrierStop;
/** Time stamp (msec) of DOMSAT Receipt time (0 = unknown) */
private Date domsatTime;
/** The database ID of the data source from whence this message came */
private int dataSourceId;
/** GOES time stamp from header, or SBD session time. */
private Date xmitTime;
/** Iridium SBD Mobile Terminated Msg Sequence Number */
private int mtmsm;
/** Iridium SBD CDR Reference Number */
private long cdrReference;
/** For GOES: DCP Address, For Iridium SBD, International Mobile Equip ID */
private DcpAddress dcpAddress = null;
/** For Iridium SBD, store the session status. */
private int sessionStatus = 0;
/** reserved for future use. */
public byte reserved[];
/**
* Max length that can be stored. The upper limit is based on:
* - 5 digit length field in DDS imposes limit to 99800
*/
public static final int MAX_DATA_LENGTH = 99800;
/** transient storage for DRGS interface. Original address is NOT saved. */
private DcpAddress origAddress = null;
/** transient storage for battery voltage, used by DCP Monitor. */
private double battVolt = 0.0;
/** Failure code for non-GOES messages */
private char failureCode = (char)0;
/** Set after header is parsed */
private int headerLength = 0;
/** Max number of failure codes any messge can have */
public static final int MAX_FAILURE_CODES = 8;
/** Additional failure/status codes (used by DCP Monitor) */
private char xmitFailureCodes[] = new char[MAX_FAILURE_CODES];
private XmitWindow xmitWindow = null;
private int msgLength = 0;
/** When read from DCP Mon database, this indicates the table it was read from */
private int dayNumber = 0;
// New fields added for OpenDCS 6.6 LRGS HRIT and DAMS-NT
private double goesSignalStrength = 0.;
private double goesFreqOffset = 0.;
private double goesGoodPhasePct = 0.;
private double goesPhaseNoise = 0.;
private static SimpleDateFormat timeSdfSec = new SimpleDateFormat("HH:mm:ss");
private static SimpleDateFormat timeSdfMS = new SimpleDateFormat("HH:mm:ss.SSS");
static NumberFormat bvFormat = NumberFormat.getNumberInstance();
static
{
timeSdfSec.setTimeZone(TimeZone.getTimeZone("UTC"));
timeSdfMS.setTimeZone(TimeZone.getTimeZone("UTC"));
bvFormat.setGroupingUsed(false);
bvFormat.setMaximumFractionDigits(1);
}
// Constructors ===============================================
/** Allocate an empty DCP message */
public DcpMsg()
{
setCarrierStart(null);
setCarrierStop(null);
setDomsatTime(null);
setDataSourceId(-1);
reserved = new byte[32];
setXmitTime(null);
setMtmsm(0);
setCdrReference(0L);
for(int i=0; i MAX_DATA_LENGTH || size < 0))
// {
// Logger.instance().warning("Cannot set DcpMsg data, invalid size="
// + size + ", attempting to parse length from header.");
//
// byte ml[] = ArrayUtil.getField(data, offset + 32, 5);
// if (ml == null)
// {
// Logger.instance().warning("Parse failed setting empty msg.");
// size = 0;
// }
// else
// {
// try
// {
// size = 37 + Integer.parseInt(new String(ml));
// Logger.instance().warning("Parsed msg length = " + size);
// }
// catch(NumberFormatException ex)
// {
// Logger.instance().warning("Parse failed setting empty msg.");
// size = 0;
// }
// }
// }
//MJM 20160831 replaced with the following, simply truncate to max data len if too big.
//Do this regardless of type.
if (size > MAX_DATA_LENGTH)
{
Logger.instance().warning("DcpMsg too big (" + size + "). Truncated to max len="
+ MAX_DATA_LENGTH);
ArrayUtil.getField(data, 0, size = MAX_DATA_LENGTH);
}
byte[] buf = new byte[size];
for(int i = 0; i= 0)
return true;
return false;
}
public boolean isGoesRandom()
{
return (getFlagbits() & DcpMsgFlag.MSG_TYPE_MASK) == DcpMsgFlag.MSG_TYPE_GOES_RD;
}
public boolean hasCarrierTimes()
{
int f = getFlagbits();
return (f & DcpMsgFlag.HAS_CARRIER_TIMES) != 0
&& (f & DcpMsgFlag.CARRIER_TIME_EST) == 0;
}
public XmitWindow getXmitTimeWindow()
{
return xmitWindow;
}
public void setXmitWindow(XmitWindow xmitWindow)
{
this.xmitWindow = xmitWindow;
}
/**
* For DCP mon, a very long message may be only partially read. Return true
* if the entire message is already present in this object. Return false if
* extended message blocks are required.
* @return true if entire message is already present here.
*/
public boolean isReadComplete()
{
return msgLength <= data.length;
}
public void setMsgLength(int msgLength)
{
this.msgLength = msgLength;
}
public int getDayNumber()
{
return dayNumber;
}
public void setDayNumber(int dayNumber)
{
this.dayNumber = dayNumber;
}
public String getStartTimeStr()
{
if (carrierStart != null)
{
String ret = "";
synchronized(timeSdfMS) { ret = timeSdfMS.format(carrierStart); }
// Limit to tenths of seconds.
if (ret.length() > 10)
ret = ret.substring(0,10);
return ret;
}
else
{
synchronized(timeSdfSec) { return timeSdfSec.format(getXmitTime()); }
}
}
public String getStopTimeStr()
{
String ret = "";
if (carrierStop != null)
{
synchronized(timeSdfMS) { ret = timeSdfMS.format(carrierStop); }
// Limit to tenths of seconds.
if (ret.length() > 10)
ret = ret.substring(0,10);
}
else
{
double dursec = getMessageLength() * 8.0 / (double)baud;
if (baud == 300)
dursec += .693;
else // 1200
dursec += .298;
Date stop = new Date(getXmitTime().getTime() + (long)(dursec*1000));
synchronized(timeSdfSec) { ret = timeSdfSec.format(stop); }
System.out.println("getStopTimeStr() computed="+stop+", fmt='" + timeSdfSec.format(stop) + ", ret='" + ret + "'");
}
return ret;
}
public String getWindowStartStr()
{
if (xmitWindow == null)
return "";
return IDateFormat.printSecondOfDay(xmitWindow.thisWindowStart, true);
}
public String getWindowStopStr()
{
if (xmitWindow == null)
return "";
return IDateFormat.printSecondOfDay(
xmitWindow.thisWindowStart + xmitWindow.windowLengthSec, true);
}
public String getBattVoltStr()
{
if (battVolt < .01)
return "N/A";
synchronized(bvFormat) { return bvFormat.format(battVolt); }
}
/**
* This method is used by the Html formatter and the DCP Monitor JSF code to
* print a block of data for display on an HTML page.
* @return
*/
public String getDataStr()
{
// MJM 20170407 improvements to rendering raw message added.
String msgStr = new String(getData());
int newlines=0, longestSpan=0, span=0;
for(int idx = 0; idx < msgStr.length(); idx++)
{
char c = msgStr.charAt(idx);
if (Character.isWhitespace(c))
{
if (span > longestSpan)
longestSpan = span;
span = 0;
if (c == '\n' || c == '\r')
newlines++;
}
else
span++;
}
if (span > longestSpan)
longestSpan = span;
// Long messages with no whitespace, e.g. pseudobinary. Add space separators.
if (longestSpan > 80)
msgStr = HtmlFormatter.wrapString(msgStr);
else if (newlines > 2)
// Preserve line breaks in formatted ascii messages like RAWS data.
msgStr = "" + msgStr + "
";
Logger.instance().info("writeRaw: msglen=" + msgStr.length() + ", newlines=" + newlines + ", longestSpan=" + longestSpan);
return msgStr;
}
public String getSource()
{
if (DcpMsgFlag.isGOES(flagbits))
return "GOES";
else if (isIridium())
return "Iridium";
LineNumberReader lnr = new LineNumberReader(new InputStreamReader(
new ByteArrayInputStream(data)));
String line;
try
{
while((line = lnr.readLine()) != null && line.startsWith("//"))
{
line = line.substring(2).trim();
if (line.startsWith("SOURCE"))
return line.substring(6).trim();
}
}
catch (IOException e) { /* Won't happen */ }
finally { try { lnr.close(); } catch(Exception ex) {} }
return "";
}
public boolean isIridium()
{
return DcpMsgFlag.isIridium(flagbits)
|| (data[0] == (byte)'I' && data[1] == (byte)'D' && data[2] == (byte)'=');
}
public double getGoesSignalStrength()
{
return goesSignalStrength;
}
public void setGoesSignalStrength(double goesSignalStrength)
{
this.goesSignalStrength = goesSignalStrength;
}
public double getGoesFreqOffset()
{
return goesFreqOffset;
}
public void setGoesFreqOffset(double goesFreqOffset)
{
this.goesFreqOffset = goesFreqOffset;
}
public double getGoesGoodPhasePct()
{
return goesGoodPhasePct;
}
public void setGoesGoodPhasePct(double goesGoodPhasePct)
{
this.goesGoodPhasePct = goesGoodPhasePct;
}
public double getGoesPhaseNoise()
{
return goesPhaseNoise;
}
public void setGoesPhaseNoise(double goesPhaseNoise)
{
this.goesPhaseNoise = goesPhaseNoise;
}
}