All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lrgs.common.DcpMsg Maven / Gradle / Ivy

Go to download

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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy