readd binlightning plugins

This commit is contained in:
mjames-upc 2018-07-16 11:57:40 -06:00
parent bd3cb4e29e
commit 154776639c
34 changed files with 3641 additions and 0 deletions

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.raytheon.edex.plugin.binlightning.legacy</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,8 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: LegacyLightningDecoder
Bundle-SymbolicName: com.raytheon.edex.plugin.binlightning.legacy
Bundle-Version: 1.16.0
Bundle-Vendor: RAYTHEON
Fragment-Host: com.raytheon.edex.plugin.binlightning;bundle-version="1.14.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.7

View file

@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.

View file

@ -0,0 +1,155 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.impl;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
import com.raytheon.uf.common.time.util.TimeUtil;
/**
* Provide the base class for the binary lightning decoders. This class
* abstracts data and methods common to the current lightning decoder types.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 20070810 379 jkorman Initial Coding from prototype.
* 20070912 379 jkorman Code review cleanup.
* May 14, 2014 2536 bclement removed TimeTools
* Jun 05, 2014 3226 bclement parseDate() now returns calendar
* </pre>
*
* @author jkorman
* @version 1.0
*/
abstract class BaseLightningDecoder implements IBinLightningDecoder
{
private final Calendar BASE_TIME = TimeUtil.newGmtCalendar(1980, 2, 29);
private static final int DAYS_MASK = 0xFFFE;
private static final int DAYS_SHFT = 1;
private static final int HOURS_HI_BIT_MASK = 0x0001;
private static final int HOURS_HI_BIT_SHFT = 0x0004;
private static final int HOURS_LO_NYB_MASK = 0x00F0;
private static final int HOURS_LO_NYB_SHFT = 0x0004;
private static final int MIN_P1_MASK = 0x000F;
private static final int MIN_P1_SHFT = 2;
private static final int MIN_P2_MASK = 0x00C0;
private static final int MIN_P2_SHFT = 6;
private static final int SECONDS_MASK = 0x003F;
// package private visibility - only sub-classes need to see these.
static final int FLASH_MSG_SIZE = 6;
static final int RT_MSG_SIZE = 8;
static final int TIME_SIZE = 4;
private int lastError = NO_ERROR;
private List<LightningStrikePoint> strikes = new ArrayList<LightningStrikePoint>();
/**
* Parse the date field from a given data source. It is assumed that the
* data source is pointing to the current date/time data.
*
* @return A Calendar object with the time fields set to the observation
* time.
*/
protected Calendar parseDate(IBinDataSource msgData)
{
//********* Don't reorder these reads!!!
int b1 = msgData.getU8();
int b2 = msgData.getU8();
int word1 = msgData.getU16();
//********* Don't reorder these reads!!!
Calendar obsTime = (Calendar) BASE_TIME.clone();
// number of days since BASE_TIME
int days = ((word1 & DAYS_MASK) >> DAYS_SHFT);
obsTime.add(Calendar.DAY_OF_MONTH, days);
int hours = (word1 & HOURS_HI_BIT_MASK) << HOURS_HI_BIT_SHFT;
hours += (b2 & HOURS_LO_NYB_MASK) >>> HOURS_LO_NYB_SHFT;
obsTime.set(Calendar.HOUR, hours);
int minutes = (b2 & MIN_P1_MASK) << MIN_P1_SHFT;
minutes += (b1 & MIN_P2_MASK) >>> MIN_P2_SHFT;
obsTime.set(Calendar.MINUTE, minutes);
obsTime.set(Calendar.SECOND, (b1 & SECONDS_MASK));
obsTime.set(Calendar.MILLISECOND, 0);
return obsTime;
}
/**
* Add a strike report the strikes collection.
* @param strike A strike report.
*/
void addStrike(LightningStrikePoint strike)
{
strikes.add(strike);
}
/**
* Set the current error code for this decoder.
* @param errorCode The error code.
*/
void setError(int errorCode)
{
lastError = errorCode;
}
/**
* Get the last error code set for this decoder.
* @return The last error code.
*/
public int getError()
{
return lastError;
}
/**
* Get an iterator to the decoded lightning strikes.
* @return The lightning strike iterator.
*/
@Override
public Iterator<LightningStrikePoint> iterator()
{
return strikes.iterator();
}
}

View file

@ -0,0 +1,132 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.impl;
import java.util.Calendar;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgMsgType;
import com.raytheon.uf.common.time.util.TimeUtil;
/**
* Decode one or more Flash lightning observations. Decode algorithm is taken
* from the NWS D2D binary lightning decoder.
*
* <pre>
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 20070810 379 jkorman Initial Coding from prototype.
* Jun 05, 2014 3226 bclement LightningStikePoint refactor
*
* </pre>
*
* @author jkorman
* @version 1.0
*/
public class FlashLightningDecoder extends BaseLightningDecoder
{
/**
* Construct and decode a portion of a lightning observation.
* @param msgData Message data.
* @param count Number of flash reports contained in the message part.
*/
public FlashLightningDecoder(IBinDataSource msgData, int count)
{
super();
doDecode(msgData,count);
}
/**
* Perform the message decode.
* @param msgData Message data.
* @param count Number of flash reports contained in the message part.
*/
private void doDecode(IBinDataSource msgData, int count)
{
if(msgData.available(TIME_SIZE))
{
Calendar baseTime = parseDate(msgData);
if(msgData.available(FLASH_MSG_SIZE * count))
{
for(int i = 0;i < count;i++)
{
double lon = getFlashLon(msgData);
double lat = getFlashLat(msgData);
double strikeStrength = msgData.getS8() * 2.0;
// strike count and 1/10s seconds
int u8 = msgData.getU8();
int flashCount = u8 & 0x0F;
Calendar obsTime = TimeUtil.newCalendar(baseTime);
obsTime.set(Calendar.MILLISECOND, ((u8 & 0xF0) >> 4) * 100);
// Create the strike record from the report info and base
// time information.
LightningStrikePoint strikeData = new LightningStrikePoint(
lat, lon, baseTime, LtgMsgType.STRIKE_MSG_FL);
strikeData.setType(DEFAULT_FLASH_TYPE);
strikeData.setStrikeStrength(strikeStrength);
strikeData.setPulseCount(flashCount);
addStrike(strikeData);
}
}
else
{
setError(IBinLightningDecoder.NOT_ENOUGH_DATA);
}
}
else
{
setError(IBinLightningDecoder.NO_TIME_INFO);
}
}
/**
* Calculate the lightning strike longitude. From D2D lightning decoder.
* @param msgData Message data source.
* @return The lightning longitude.
*/
private double getFlashLon(IBinDataSource msgData)
{
int value = msgData.getU16();
double lon = (value * 0.001068115234) - 130.0;
return lon;
}
/**
* Calculate the lightning strike latitude. From D2D lightning decoder.
* @param msgData Message data source.
* @return The lightning latitude.
*/
private double getFlashLat(IBinDataSource msgData)
{
int value = msgData.getU16() & 0x7FFF;
double lat = (value * 0.001281738) + 18.0;
return lat;
}
}

View file

@ -0,0 +1,174 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.impl;
import java.util.Calendar;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgMsgType;
/**
* Decode one or more Real Time Flash lightning observations. Decode algorithm
* is taken from the NWS D2D binary lightning decoder.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 20070810 379 jkorman Initial Coding from prototype.
* 20070821 379 jkorman Added default strike type.
* 20080823 379 jkorman getRTLat was using 24 bits instead of 23.
* Jun 05, 2014 3226 bclement LightningStikePoint refactor
* </pre>
*
* @author jkorman
* @version 1.0
*/
public class RTLightningDecoder extends BaseLightningDecoder {
/**
* Construct an instance of this decoder, and decode the message data.
*
* @param msgData
* Message data.
* @param count
* Number of flash reports contained in the message part.
*/
public RTLightningDecoder(IBinDataSource msgData, int count) {
super();
doDecode(msgData, count);
}
/**
* Perform the message decode for real time strike message.
*
* @param msgData
* Message data.
* @param count
* Number of flash reports contained in the message part.
*/
private void doDecode(IBinDataSource msgData, int count) {
if (msgData.available(TIME_SIZE + (RT_MSG_SIZE * count))) {
Calendar baseTime = parseDate(msgData);
// for now just consume some data
for (int i = 0; i < count; i++) {
long part = msgData.getU32();
double lon = getRTLon(part);
double strength = getSignalStrength(part);
part = msgData.getU32();
double lat = getRTLat(part);
int strikeCount = getMult(part);
LightningStrikePoint strikeData = new LightningStrikePoint(lat,
lon, baseTime, LtgMsgType.STRIKE_MSG_RT);
strikeData.setStrikeStrength(strength);
strikeData.setPulseCount(strikeCount);
// *****
// NCDC documents indicate that RT data can report both CC/CG
// but haven't seen any data nor is it in the D2D decoders. Set
// to default for now.
// *****
strikeData.setType(DEFAULT_FLASH_TYPE);
strikeData.setMillis(0);
addStrike(strikeData);
}
} else {
setError(IBinLightningDecoder.NO_TIME_INFO);
}
}
/**
* Decode the Real Time lightning longitude. Data is in the lower 24 bits.
*
* @param msgPart
* Unsigned 32 bit value holding the coded longitude.
* @return The decoded longitude.
*/
private double getRTLon(long msgPart) {
int value = (int) (msgPart & 0xFFFFFFL);
double lon = (value / 16777216.0 * 360.0) - 180.0;
return lon;
}
/**
* Decode the Real Time lightning latitude. Data is in the lower 24 bits.
*
* @param msgPart
* Unsigned 32 bit value holding the coded latitude.
* @return The decoded latitude.
*/
private double getRTLat(long msgPart) {
int value = (int) (msgPart & 0x7FFFFFL);
double lat = (value / 16777216.0 * 360.0) - 90.0;
return lat;
}
/**
* Decode the Real Time lightning signal strength. Data is in the upper 8
* bits.
*
* @param msgPart
* Unsigned 32 bit value holding the coded strength.
* @return The decoded signal strength.
*/
private double getSignalStrength(long msgPart) {
final int SIGNMAG = 128;
double sigStrength = 0;
int temp = (int) ((msgPart >> 24) & 0xFF);
// if the temp strength is greater than 127, the result should be
// negative.
if (temp >= SIGNMAG) {
temp = SIGNMAG - temp;
}
// Make signal strength in the range -254..254
sigStrength = temp * 2;
return sigStrength;
}
/**
* Decode the number of strikes within the strike record. Data is encoded in
* bits 24..27.
*
* @param msgPart
* Unsigned 32 bit value holding the count.
* @return The number of strikes within the strike record.
*/
private int getMult(long msgPart) {
// TODO : Need to check this! There is data in bit 28-31 but none in
// 24..27 in current data.
// NCDC document 9603 indicates that RT_FLASH doesn't report mult so
// always zero would be correct
int temp = (int) ((msgPart >> 24) & 0x0F);
return temp;
}
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.raytheon.edex.plugin.binlightning</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,32 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Binlightning Plug-in
Bundle-SymbolicName: com.raytheon.edex.plugin.binlightning
Bundle-Version: 1.14.0.qualifier
Bundle-Vendor: RAYTHEON
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Export-Package: com.raytheon.edex.plugin.binlightning.dao
Import-Package: com.raytheon.edex.esb,
com.raytheon.edex.exception,
com.raytheon.edex.plugin,
com.raytheon.uf.common.dataquery.requests,
com.raytheon.uf.common.geospatial,
com.raytheon.uf.common.geospatial.adapter,
com.raytheon.uf.common.localization,
com.raytheon.uf.common.localization.exception,
com.raytheon.uf.common.numeric,
com.raytheon.uf.common.serialization,
com.raytheon.uf.common.serialization.adapters,
com.raytheon.uf.common.status,
com.raytheon.uf.common.wmo,
com.vividsolutions.jts,
com.vividsolutions.jts.geom,
com.vividsolutions.jts.geom.prep,
com.vividsolutions.jts.io,
gov.noaa.nws.ost.edex.plugin.binlightning
Require-Bundle: com.raytheon.uf.common.dataplugin.binlightning;bundle-version="1.12.1174",
com.raytheon.uf.common.dataplugin;bundle-version="1.12.1174",
com.raytheon.uf.common.datastorage;bundle-version="1.12.1174",
com.raytheon.uf.edex.core;bundle-version="1.12.1174",
com.raytheon.uf.edex.database;bundle-version="1.0.0",
com.raytheon.uf.edex.decodertools;bundle-version="1.12.1174"

View file

@ -0,0 +1,6 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
utility/,\
.,\
res/

View file

@ -0,0 +1,18 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="binlightningProperties" class="com.raytheon.uf.common.dataplugin.PluginProperties">
<property name="pluginName" value="binlightning" />
<property name="pluginFQN" value="com.raytheon.uf.common.dataplugin.binlightning" />
<property name="dao" value="com.raytheon.edex.plugin.binlightning.dao.BinLightningDao" />
<property name="record" value="com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord" />
</bean>
<bean factory-bean="pluginRegistry" factory-method="register">
<constructor-arg value="binlightning"/>
<constructor-arg ref="binlightningProperties"/>
</bean>
</beans>

View file

@ -0,0 +1,56 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="binlightningDecoder"
class="com.raytheon.edex.plugin.binlightning.BinLightningDecoder" />
<bean id="totalLightningDecoder" class="com.raytheon.edex.plugin.binlightning.total.TotalLightningDecoder" />
<bean id="binlightningDistRegistry" factory-bean="distributionSrv"
factory-method="register">
<constructor-arg value="binlightning" />
<constructor-arg value="jms-durable:queue:Ingest.binlightning" />
</bean>
<bean factory-bean="contextManager" factory-method="registerClusteredContext">
<constructor-arg ref="clusteredBinLightningRoutes" />
</bean>
<camelContext id="clusteredBinLightningRoutes" xmlns="http://camel.apache.org/schema/spring"
errorHandlerRef="errorHandler">
<!-- Begin binlightning routes -->
<route id="binlightningIngestRoute">
<from uri="jms-durable:queue:Ingest.binlightning" />
<setHeader headerName="pluginName">
<constant>binlightning</constant>
</setHeader>
<doTry>
<pipeline>
<bean ref="stringToFile" />
<choice>
<when>
<simple>${in.header.header} regex '^SFPA42 KWBC.*'</simple>
<bean ref="totalLightningDecoder" method="decode" />
</when>
<otherwise>
<bean ref="binlightningDecoder" method="decode" />
</otherwise>
</choice>
<to uri="direct-vm:persistIndexAlert" />
</pipeline>
<doCatch>
<exception>java.lang.Throwable</exception>
<to uri="log:binlightning?level=ERROR" />
</doCatch>
</doTry>
<!-- bean ref="processUtil" method="delete" / -->
</route>
</camelContext>
</beans>

View file

@ -0,0 +1,655 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import com.raytheon.edex.esb.Headers;
import com.raytheon.edex.exception.DecoderException;
import com.raytheon.edex.plugin.binlightning.filter.LightningGeoFilter;
import com.raytheon.edex.plugin.binlightning.impl.BinLightningFactory;
import com.raytheon.edex.plugin.binlightning.impl.IBinDataSource;
import com.raytheon.edex.plugin.binlightning.impl.IBinLightningDecoder;
import com.raytheon.edex.plugin.binlightning.impl.LightningDataSource;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgStrikeType;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.TimeRange;
import com.raytheon.uf.common.time.util.TimeUtil;
import com.raytheon.uf.common.wmo.WMOHeader;
import com.raytheon.uf.common.wmo.WMOTimeParser;
import com.raytheon.uf.edex.decodertools.core.DecoderTools;
import gov.noaa.nws.ost.edex.plugin.binlightning.BinLightningAESKey;
import gov.noaa.nws.ost.edex.plugin.binlightning.BinLightningDataDecryptionException;
import gov.noaa.nws.ost.edex.plugin.binlightning.BinLightningDecoderUtil;
import gov.noaa.nws.ost.edex.plugin.binlightning.DecryptedLightningValidator;
import gov.noaa.nws.ost.edex.plugin.binlightning.EncryptedBinLightningCipher;
/**
* AWIPS decoder adapter strategy for binary lightning data.<br/>
*
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 10, 2007 379 jkorman Initial Coding from prototype.
* Aug 17, 2007 379 jkorman Changed log info to debug in decode().
* Aug 21, 2007 379 jkorman Added SFPA41 lightning data pattern.
* Sep 12, 2007 379 jkorman Code review cleanup.
* Sep 20, 2007 379 jkorman Check for null persistence time.
* Sep 24, 2007 379 jkorman Removed HDFGroup code. Set insert_time
* directly in decode.
* Sep 26, 2007 379 jkorman Updated to set DataTime.
* Mar 18, 2008 1026 jkorman Added debug strike info.
* Apr 08, 2008 1039 jkorman Added traceId for tracing data.
* Nov 11, 2008 1684 chammack Refactored for camel integration
* May 03, 2013 DCS 112 Wufeng Zhou Modified to be able to handle both the
* new encrypted data and legacy bit-shifted
* data
* Aug 30, 2013 2298 rjpeter Make getPluginName abstract
* Jan 24, 2014 DR 16774 Wufeng Zhou Modified for updated Bin-lightning data spec,
* and to used WMO header to distinguish bit-shifted
* GLD360 and NLDN data.
* May 14, 2014 2536 bclement moved WMO Header to common
* Jun 03, 2014 3226 bclement removed unused WMO patterns, switched to UFStatus
* removed TimeTools usage, removed constructDataURI() call
* added decodeBinLightningData() and decodeBitShiftedBinLightningData() from BinLightningDecoderUtil
* Jun 05, 2014 3226 bclement LightningStikePoint refactor, added extractPData()
* Jun 09, 2014 3226 bclement moved data array decrypt prep to EncryptedBinLightingCipher
* Jun 10, 2014 3226 bclement added filter support
* Jun 19, 2014 3226 bclement added validator callback
* Aug 04, 2014 3488 bclement added checkBinRange(), rebin() and finalizeRecords()
* Mar 08, 2016 18336 amoore Keep-alive messages should update the legend.
* Apr 07, 2016 DR18763 mgamazaychikov Switched to using LightningWMOHeader.
* May 02, 2016 18336 amoore BinLightningRecord constructor takes source.
* Jun 10, 2016 DR18939 mgamazaychikov Removed LightningWMOHeader.
* Jul 29, 2016 19202 amoore Decryption properties should be based on WMO ID.
* Sep 02, 2016 19339 amoore On failure of decryption and bit-shifting (returning -1 strikes),
* fail the bin lightning message rather than assume as keep-alive.
*
* </pre>
*
* @author jkorman
*/
public class BinLightningDecoder {
// Allow ingest up to 10 minutes into the future.
private static final long TEN_MINUTES = 10 * 60 * 1000L;
private final SimpleDateFormat SDF;
private static final IUFStatusHandler logger = UFStatus
.getHandler(BinLightningDecoder.class);
private static final boolean REBIN_INVALID_DATA = Boolean
.getBoolean("rebin.invalid.binlightning");
/**
* Cipher properties prefix for bin lightning, to be prepended to WMO IDs.
*/
public static final String BINLIGHTNING_KEYSTORE_PREFIX = "binlightning";
/**
* Default lightning strike type for FLASH messages. RT_FLASH documents
* indicate no default, but D2D code defaults to STRIKE_CG also.
*/
public LtgStrikeType DEFAULT_FLASH_TYPE = LtgStrikeType.CLOUD_TO_GROUND;
private String traceId = null;
/**
* callback for validating decryption results
*/
private static DecryptedLightningValidator validator = new DecryptedLightningValidator() {
@Override
public boolean isValid(byte[] decryptedData) {
return BinLightningDecoderUtil.isKeepAliveRecord(decryptedData)
|| BinLightningDecoderUtil
.isLightningDataRecords(decryptedData);
}
};
/**
* Construct a BinLightning decoder. Calling hasNext() after construction
* will return false, decode() will return a null.
*/
public BinLightningDecoder() {
SDF = new SimpleDateFormat("yyyyMMddHHmmss");
SDF.setTimeZone(TimeZone.getTimeZone("Zulu"));
}
/**
* Get the next decoded data record.
*
* @return One record of decoded data.
* @throws DecoderException
* Thrown if no data is available.
*/
public PluginDataObject[] decode(byte[] data, Headers headers)
throws DecoderException {
// String traceId = null;
PluginDataObject[] rval = new PluginDataObject[0];
if (data != null) {
traceId = (String) headers.get(DecoderTools.INGEST_FILE_NAME);
WMOHeader wmoHdr = new WMOHeader(data);
if (wmoHdr.isValid()) {
String fileName = (String) headers
.get(WMOHeader.INGEST_FILE_NAME);
Calendar baseTime = WMOTimeParser
.findDataTime(wmoHdr.getYYGGgg(), fileName);
/*
* Because binary nature of the encrypted data, the string
* created with its byte[] array may not have the same length of
* the byte[] array length So when DecoderTools.stripWMOHeader()
* assumes byte[] length == String length in its logic, it is
* observed that it may return a shorter byte[] than the real
* data array. (Looks like a bug???)
*/
// byte[] pdata = DecoderTools.stripWMOHeader(data,
// SFUS_PATTERN);
// if (pdata == null) {
// pdata = DecoderTools.stripWMOHeader(data, SFPA_PATTERN);
// }
/*
* instead the following is used to strip WMO header a little
* more safely.
*/
byte[] pdata = extractPData(wmoHdr, data);
if ((pdata == null) || (pdata.length == 0)) {
return new PluginDataObject[0];
}
/*
* Modified by Wufeng Zhou to handle both legacy bit-shifted and
* new encrypted data.
*
* If neither method works, throw an exception.
*
* Preserved the legacy decoding in
* BinLigntningDecoderUtil.decodeBitShiftedBinLightningData(),
* and added logic to process both encrypted data and legacy
* data.
*/
Collection<LightningStrikePoint> strikes = decodeBinLightningData(
data, pdata, traceId, wmoHdr, baseTime.getTime());
/*
* Done MOD by Wufeng Zhou
*/
// post processing data
BinLightningRecord report = null;
if (strikes.size() > 0) {
// not a keep-alive
report = LightningGeoFilter.createFilteredRecord(strikes);
} else {
// keep-alive, get the source from WMO header
String source = getSourceFromHeader(wmoHdr);
synchronized (SDF) {
logger.info(
traceId + ": found keep-alive record of base time ["
+ SDF.format(baseTime.getTime())
+ "] and source [" + source + "]");
}
report = new BinLightningRecord(baseTime, source);
}
Collection<BinLightningRecord> records = checkBinRange(report,
strikes);
rval = finalizeRecords(records, baseTime);
}
} else {
logger.error("No WMOHeader found in data");
}
logger.debug(traceId + ": Returning array of [" + rval.length
+ "] bin lightning records");
return rval;
}
/**
* Perform final actions on each record and populate a PDO array with them.
* Any invalid records will be omitted from the return array.
*
* @param records
* @param baseTime
* @return
* @throws DecoderException
*/
private PluginDataObject[] finalizeRecords(
Collection<BinLightningRecord> records, Calendar baseTime)
throws DecoderException {
Calendar c = TimeUtil.newCalendar(baseTime);
if (c == null) {
throw new DecoderException(traceId + " - Error decoding times");
}
ArrayList<BinLightningRecord> rval = new ArrayList<>(records.size());
for (BinLightningRecord record : records) {
Calendar cStart = record.getStartTime();
if (cStart
.getTimeInMillis() > (c.getTimeInMillis() + TEN_MINUTES)) {
synchronized (SDF) {
logger.info("Discarding future data for " + traceId + " at "
+ SDF.format(cStart.getTime()));
}
} else {
Calendar cStop = record.getStopTime();
TimeRange range = new TimeRange(cStart.getTimeInMillis(),
cStop.getTimeInMillis());
DataTime dataTime = new DataTime(cStart, range);
record.setDataTime(dataTime);
record.setTraceId(traceId);
rval.add(record);
}
}
return rval.toArray(new PluginDataObject[rval.size()]);
}
/**
* Ensure that the record has a valid bin range. If it does, it will be the
* only record in the return value. Otherwise, {@link #REBIN_INVALID_DATA}
* is used to determine if no records should be returned or the strikes
* should be split into valid bin ranges uses {@link #rebin(Collection)}
*
* @param record
* @param strikes
* @return
*/
private Collection<BinLightningRecord> checkBinRange(
BinLightningRecord record,
Collection<LightningStrikePoint> strikes) {
Collection<BinLightningRecord> rval = Collections.emptyList();
Calendar cStart = record.getStartTime();
Calendar cStop = record.getStopTime();
long binRange = cStop.getTimeInMillis() - cStart.getTimeInMillis();
if (binRange > TimeUtil.MILLIS_PER_DAY) {
if (REBIN_INVALID_DATA) {
rval = rebin(strikes);
} else {
String rangeStart;
String rangeEnd;
synchronized (SDF) {
rangeStart = SDF.format(cStart.getTime());
rangeEnd = SDF.format(cStop.getTime());
}
logger.error("Discarding data with invalid bin range of "
+ rangeStart + " to " + rangeEnd);
}
} else {
rval = Arrays.asList(record);
}
return rval;
}
/**
* Split the strikes into 1 day bins and create a new record for each bin
*
* @param strikes
* @return
*/
private Collection<BinLightningRecord> rebin(
Collection<LightningStrikePoint> strikes) {
Map<Long, Collection<LightningStrikePoint>> binMap = new HashMap<>(1);
for (LightningStrikePoint strike : strikes) {
Calendar c = TimeUtil.newCalendar(strike.getTime());
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
long key = c.getTimeInMillis();
Collection<LightningStrikePoint> bin = binMap.get(key);
if (bin == null) {
bin = new ArrayList<>(strikes.size());
binMap.put(key, bin);
}
bin.add(strike);
}
Collection<BinLightningRecord> rval = new ArrayList<>(binMap.size());
for (Entry<Long, Collection<LightningStrikePoint>> e : binMap
.entrySet()) {
Collection<LightningStrikePoint> bin = e.getValue();
BinLightningRecord record = new BinLightningRecord(bin);
rval.add(record);
}
return rval;
}
/**
* Remove WMO header from data and return the remaining pdata
*
* @param wmoHdr
* @param data
* @return null if data is invalid
*/
public static byte[] extractPData(WMOHeader wmoHdr, byte[] data) {
byte[] pdata = null;
if (wmoHdr.isValid() && wmoHdr.getMessageDataStart() > 0) {
pdata = new byte[data.length - wmoHdr.getMessageDataStart()];
System.arraycopy(data, wmoHdr.getMessageDataStart(), pdata, 0,
data.length - wmoHdr.getMessageDataStart());
}
return pdata;
}
/**
* Decode bin lightning data, able to handle both legacy bit-shifted and new
* encryted data
*
* The BinLightningDecoder.decode() method will use this method to decode
* data, which will try to decrypt first, and decode the old fashioned way
* (bit-shifting) when decryption fails
*
* @author Wufeng Zhou
*
* @param data
* - data content from file, including WMO header section
* @param pdata
* - data with WMO header stripped, optional, if null, will strip
* WMO header internally from passed in data parameter
* @param traceId
* - the file name of the data to be deoced
* @param wmoHdr
* - WMOHeader, added 12/24/2013 to help distinguish bit-shifted
* NLDN and GLD360 data (GLD data will have header starts like
* SFPA)
* @param dataDate
* - date of the data, optional, used as a hint to find
* appropriate encryption key faster
* @return null if keep-alive record, otherwise a list (could be empty) of
* LightningStrikePoint
* @throws DecoderException
* if neither decryption nor bit-shifting works.
*/
public static List<LightningStrikePoint> decodeBinLightningData(byte[] data,
byte[] pdata, String traceId, WMOHeader wmoHdr, Date dataDate)
throws DecoderException {
if (pdata == null) { // if data without header not passed, we'll strip
// the WMO header here
WMOHeader header = new WMOHeader(data);
if (header.isValid() && header.getMessageDataStart() > 0) {
pdata = new byte[data.length - header.getMessageDataStart()];
System.arraycopy(data, header.getMessageDataStart(), pdata, 0,
data.length - header.getMessageDataStart());
}
}
List<LightningStrikePoint> strikes = new ArrayList<>();
boolean needDecrypt = true; // set as default unless clear evidence says
// otherwise
boolean decodeDone = false;
EncryptedBinLightningCipher cipher = new EncryptedBinLightningCipher();
/*
* Using different WMO headers to indicate whether the data is encrypted
* or not would be a nice option. However, that idea has been discussed
* but not adopted. If in the future, WMO header can be different for
* legacy and encrypted data, or some other metadata can be used to
* decide whether deceyption is needed, logic can be added here.
*
* Before that happens, we'll use hints and trial & error to decode the
* data Hints: Per lightning data format spec, there are 3 bytes in the
* WMO header starting line that indicates the size of the encrypted
* block or the ASCII sequence # for legacy bit-shifted data However,
* the starting line is optional and AWIPS decode may not see it at all
* because TG will strip that starting line away We'll try to use this
* hint first, if is is not found, then trial and error way to decrypt
* and decode
*
* As of 11/05/2013, There is change in data spec. that the 3-bytes will
* not be encoded as encrypted block size anymore (it will always be
* transmission sequence # if present) So there should have some minor
* changes in the logic below for decoding the data. However, as reading
* into the
* com.raytheon.edex.plugin.binlightning.impl.BinLightningFactory
* .getDecoder () and follow-on code, we see the following data patterns
* for legacy bit-shifted data, which could be used to reduce guess-work
* in data decryption: The bit-shifted data will have multiple groups of
* the following patterns: 1-byte (unsigned byte): for size count 1-byte
* (unsigned byte): for flash type: 0x96 for FLASH_RPT (message size is
* 6 bytes each) 0x97 for RT_FLASH_RPT (message size is 8 bytes each)
* 0xd0 for OTHER_RPT (The D2D decoders declare but do not define this
* message, so unimplemented decoder) 0xd1 for COMM_RPT (The D2D
* decoders declare but do not define this message, so unimplemented
* decoder) 4-bytes: date time multiple of 6 or 8 bytes (as determined
* by 2nd byte flash type) with count indicated in 1st byte
*
* So this is be used to determine whether the data need to be
* decrypted.
*/
/*
* // looks like previous assumption on block size bytes are not valid
* any more. 11/20/2013 if (data != null) { byte[] sizeSeqBytes =
* BinLigntningDecoderUtil.findSizeOrSeqBytesFromWMOHeader(data); if
* (sizeSeqBytes != null) { // if this is in the header (which may not),
* use that as a hint to determine which decoding route to go if
* (BinLigntningDecoderUtil
* .isPossibleWMOHeaderSequenceNumber(sizeSeqBytes) &&
* BinLigntningDecoderUtil
* .getEncryptedBlockSizeFromWMOHeader(sizeSeqBytes) != pdata.length) {
* // looks like a sequence #, and if treat as size, it does not equal
* to the data block size, so most likely legacy data needDecrypt =
* false; } } }
*/
if (needDecrypt) {
try {
byte[] encryptedData = EncryptedBinLightningCipher
.prepDataForDecryption(pdata, traceId);
// use different suffix depending on header
String wmoID = wmoHdr.getTtaaii();
logger.info(
"Decrypting a [" + wmoID + "] bin lightning record");
byte[] decryptedData = cipher.decryptData(encryptedData,
dataDate, BINLIGHTNING_KEYSTORE_PREFIX + wmoID,
validator);
// decrypt ok, then decode, first check if keep-alive record
if (BinLightningDecoderUtil.isKeepAliveRecord(decryptedData)) {
logger.info(traceId
+ " - Keep-alive record detected, no strike decoding.");
decodeDone = true;
return new ArrayList<>(0);
}
/*
* not necessarily keep-alive record, then check data validity
* and decode into an ArrayList<LightningStrikePoint> of strikes
*/
if (BinLightningDecoderUtil
.isLightningDataRecords(decryptedData)) {
strikes = BinLightningDecoderUtil
.decodeDecryptedBinLightningData(decryptedData);
decodeDone = true;
} else {
logger.info(traceId
+ " - Failed data validity check of the decrypted data, will try decode the old-fashioned way.");
decodeDone = false;
}
} catch (IllegalBlockSizeException e) {
logger.info(traceId + " - " + e.getMessage()
+ ": Decryption failed, will try decode the old-fashioned way.");
decodeDone = false;
} catch (BadPaddingException e) {
logger.info(traceId + " - " + e.getMessage()
+ ": Decryption failed, will try decode the old-fashioned way.");
decodeDone = false;
} catch (BinLightningDataDecryptionException e) {
logger.info(traceId + " - " + e.getMessage()
+ ": Decryption failed, will try decode the old-fashioned way.");
decodeDone = false;
}
}
if (decodeDone == false) { // not decoded through decrypt->decode
// process, try the legacy decoder
logger.info(traceId + " - decoding as bit-shifted data");
/*
* bit-shifting data format check call here will get us some more
* information on the data, also can compare the strikes with the
* decoder result
*/
int estimatedStrikes = BinLightningDecoderUtil
.getBitShiftedDataStrikeCount(pdata);
if (estimatedStrikes == -1) {
/**
* BinLightningDecoderUtil#getBitShiftedDataStrikeCount returns
* -1 if the data is not bit-shifted.
*/
throw new DecoderException(traceId + " - "
+ "Cannot get strikes from message using either decryption or bit-shifting. Skipping this message.");
} else {
strikes = decodeBitShiftedBinLightningData(pdata, wmoHdr);
if (estimatedStrikes != strikes.size()) {
logger.warn(
traceId + ": bit-shifted decoder found " + strikes
+ " strikes, which is different from estimate from data pattern examination: "
+ estimatedStrikes);
}
}
}
return strikes;
}
/**
* extracted from the original {@link #decode(byte[], Headers)} method then
* modified by Wufeng Zhou
*
* @param pdata
* @param wmoHdr
* - WMOHeader, added 12/24/2013 to help distinguish bit-shifted
* NLDN and GLD360 data (GLD data will have header starts like
* SFPA)
* @return
*/
public static List<LightningStrikePoint> decodeBitShiftedBinLightningData(
byte[] pdata, WMOHeader wmoHdr) {
List<LightningStrikePoint> strikes = new ArrayList<>();
IBinDataSource msgData = new LightningDataSource(pdata);
boolean continueDecode = true;
while (continueDecode) {
IBinLightningDecoder decoder = BinLightningFactory
.getDecoder(msgData);
switch (decoder.getError()) {
case IBinLightningDecoder.NO_ERROR: {
for (LightningStrikePoint strike : decoder) {
String source = getSourceFromHeader(wmoHdr);
strike.setLightSource(source);
strikes.add(strike);
}
break;
}
default: {
continueDecode = false;
}
}
}
return strikes;
}
/**
* Get the data source from the given WMO header.
*
* @param wmoHdr
* the header.
* @return the data source according to text from the header.
*/
private static String getSourceFromHeader(WMOHeader wmoHdr) {
/*
* use WMO Header to distinguish NLDN or GLD360 data because no
* bit-shifted data spec available for GLD360. 12/24/2013, WZ The WMO
* header start string is defined in BinLightningAESKey.properties file
* (normally, GLD360 data will have WMO header starts with SFPA41, or
* SFPA99 for test data.)
*/
/*
* continually get the properties locally instead of as a constant
* because the properties file could be changed and reloaded by the
* user.
*/
String gld360WMOHeaderString = BinLightningAESKey.getProps()
.getProperty("binlightning.gld360WMOHeaderStartString", "");
String source = null;
if (gld360WMOHeaderString.trim().equals("") == false
&& wmoHdr.getWmoHeader().startsWith(gld360WMOHeaderString)) {
// GLD360 data based on the setup
source = "GLD";
}
/*
* default source is NLDN, which is the only other option at this time
* for this decoder (5/2/2016, DR 18336). For future sources, add here
* and modify BinLightningAESKey.properties.
*/
return BinLightningRecord.validateSource(source);
}
/**
* Set a trace identifier for the source data.
*
* @param traceId
* A unique identifier associated with the input data.
*/
public void setTraceId(String traceId) {
this.traceId = traceId;
}
}

View file

@ -0,0 +1,105 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.dao;
import java.util.Map;
import java.util.Map.Entry;
import com.raytheon.uf.common.dataplugin.PluginException;
import com.raytheon.uf.common.dataplugin.annotations.DataURI;
import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord;
import com.raytheon.uf.common.dataplugin.binlightning.LightningConstants;
import com.raytheon.uf.common.dataplugin.persist.IPersistable;
import com.raytheon.uf.common.datastorage.DataStoreFactory;
import com.raytheon.uf.common.datastorage.IDataStore;
import com.raytheon.uf.common.datastorage.StorageException;
import com.raytheon.uf.common.datastorage.records.IDataRecord;
import com.raytheon.uf.edex.database.plugin.PluginDao;
/**
* Data access object for access binlightning data
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 1/08/09 1674 bphillip Initial creation
* Jun 05, 2014 3226 bclement record now contains maps for data arrays
* </pre>
*
* @author bphillip
* @version 1.0
*/
public class BinLightningDao extends PluginDao {
/**
*
* @param pluginName The name of this plugin.
* @throws PluginException
*/
public BinLightningDao(String pluginName) throws PluginException {
super(pluginName);
}
/**
* Copy data from a Persistable object into a given DataStore container.
* @param dataStore DataStore instance to receive the Persistable data.
* @param obj The Persistable object to be stored.
* @throws Exception Any general exception thrown in this method.
*/
protected IDataStore populateDataStore(IDataStore dataStore,
IPersistable obj) throws Exception {
BinLightningRecord binLightningRec = (BinLightningRecord) obj;
Map<String, Object> strikeDataArrays = binLightningRec
.getStrikeDataArrays();
populateFromMap(dataStore, obj, binLightningRec.getDataURI(),
strikeDataArrays);
Map<String, Object> pulseDataArrays = binLightningRec
.getPulseDataArrays();
String pulseGroup = binLightningRec.getDataURI() + DataURI.SEPARATOR
+ LightningConstants.PULSE_HDF5_GROUP_SUFFIX;
populateFromMap(dataStore, obj, pulseGroup, pulseDataArrays);
return dataStore;
}
/**
* Adds each primitive data array object in map to the datastore using the
* provided group and the key of the map entry as the name
*
* @param dataStore
* @param obj
* @param group
* @param data
* @throws StorageException
*/
private void populateFromMap(IDataStore dataStore, IPersistable obj,
String group, Map<String, Object> data)
throws StorageException {
for (Entry<String, Object> e : data.entrySet()) {
String name = e.getKey();
Object dataArray = e.getValue();
IDataRecord record = DataStoreFactory.createStorageRecord(name,
group, dataArray);
record.setCorrelationObject(obj);
dataStore.addDataRecord(record);
}
}
}

View file

@ -0,0 +1,136 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.filter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
/**
* JAXB POJO for filter bounding box
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 11, 2014 3226 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
@XmlAccessorType(XmlAccessType.NONE)
public class GeoFilterBbox {
@XmlAttribute(required = true)
private double minx;
@XmlAttribute(required = true)
private double maxx;
@XmlAttribute(required = true)
private double miny;
@XmlAttribute(required = true)
private double maxy;
/**
*
*/
public GeoFilterBbox() {
}
/**
* @param minx
* @param maxx
* @param miny
* @param maxy
*/
public GeoFilterBbox(double minx, double maxx, double miny, double maxy) {
this.minx = minx;
this.maxx = maxx;
this.miny = miny;
this.maxy = maxy;
}
/**
* @return the minx
*/
public double getMinx() {
return minx;
}
/**
* @param minx
* the minx to set
*/
public void setMinx(double minx) {
this.minx = minx;
}
/**
* @return the maxx
*/
public double getMaxx() {
return maxx;
}
/**
* @param maxx
* the maxx to set
*/
public void setMaxx(double maxx) {
this.maxx = maxx;
}
/**
* @return the miny
*/
public double getMiny() {
return miny;
}
/**
* @param miny
* the miny to set
*/
public void setMiny(double miny) {
this.miny = miny;
}
/**
* @return the maxy
*/
public double getMaxy() {
return maxy;
}
/**
* @param maxy
* the maxy to set
*/
public void setMaxy(double maxy) {
this.maxy = maxy;
}
}

View file

@ -0,0 +1,70 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.filter;
/**
* Exception thrown during lightning filter config parsing
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 10, 2014 3226 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class GeoFilterException extends Exception {
private static final long serialVersionUID = -7455324971125247083L;
/**
*
*/
public GeoFilterException() {
}
/**
* @param message
*/
public GeoFilterException(String message) {
super(message);
}
/**
* @param cause
*/
public GeoFilterException(Throwable cause) {
super(cause);
}
/**
* @param message
* @param cause
*/
public GeoFilterException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,295 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.filter;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBException;
import javax.xml.stream.XMLStreamException;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.geospatial.SpatialException;
import com.raytheon.uf.common.geospatial.SpatialQueryFactory;
import com.raytheon.uf.common.geospatial.SpatialQueryResult;
import com.raytheon.uf.common.serialization.JAXBManager;
import com.raytheon.uf.common.serialization.SerializationException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
/**
* Parses geographic filter configuration files. <br>
* <br>
* Example file:
*
* <pre>
* {@code
* <!-- example binlightning geospatial filter file
* name of file corresponds to data source (eg NLDN.xml)
* data is persisted if lightning strike point is in any of the filter areas
* if a data source does not have any filters, all data is persisted -->
* <filters>
* <!-- bounding box filter: x is degrees longitude, y is degrees latitude-->
* <bbox minx="-98.23" maxx="-97.24" miny="38.27" maxy="39.39" />
* <!-- CWA filters: correspond to polygons defining the county warning areas -->
* <cwa>OAX</cwa>
* <cwa>DMX</cwa>
* <cwa>FSD</cwa>
* <!-- well known text geometries: x coordinates are longitude, y coordinates are latitude
* first coordinate is always the same as last to close the polygon region -->
* <wkt>POLYGON ((-101.24 41.4, -100.6 41.15, -101.01 40.75, -101.24 41.4))</wkt>
* </filters>
* }
* </pre>
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 10, 2014 3226 bclement Initial creation
* Jul 08, 2016 5744 mapeters Added example file to javadoc
*
* </pre>
*
* @author bclement
*/
public class GeoFilterParser {
private static final double LAT_MIN = -90;
private static final double LAT_MAX = 90;
private static final double LON_MIN = -180;
private static final double LON_MAX = 180;
/* CWA database constants */
private static final String CWA_TABLE = "cwa";
private static final String CWA_COLUMN = "cwa";
/* helper factories */
private static JAXBManager jaxbManager;
private static volatile boolean initialized = false;
private static final GeometryFactory GEOM_FACTORY = new GeometryFactory();
/**
* initialize jaxb manager
*
* @throws JAXBException
*/
private static synchronized void initialize() throws JAXBException {
if (!initialized) {
jaxbManager = new JAXBManager(GeoFilters.class);
initialized = true;
}
}
/**
* Parse filter config file from input stream. Does not close input stream.
* Any non-fatal parsing errors are returned in result.
*
* @param in
* @return
* @throws JAXBException
* @throws SerializationException
*/
public static GeoFilterResult parse(InputStream in) throws JAXBException,
SerializationException {
if (!initialized) {
initialize();
}
Object obj = jaxbManager.unmarshalFromInputStream(in);
if (obj instanceof GeoFilters) {
return parseInternal((GeoFilters) obj);
} else {
throw new SerializationException("Unexpected XML object type: "
+ obj.getClass());
}
}
/**
* @see #parse
* @param filters
* jaxb pojo
* @return
* @throws XMLStreamException
*/
private static GeoFilterResult parseInternal(GeoFilters filters) {
List<Geometry> geoms = new ArrayList<>();
List<GeoFilterException> errors = new ArrayList<>();
List<GeoFilterBbox> bboxes = filters.getBboxes();
if (bboxes != null && !bboxes.isEmpty()) {
for (GeoFilterBbox bbox : bboxes) {
try {
geoms.addAll(convertBbox(bbox));
} catch (GeoFilterException e) {
errors.add(e);
}
}
}
List<String> cwas = filters.getCwas();
if (cwas != null && !cwas.isEmpty()) {
for (String cwa : cwas) {
try {
geoms.addAll(getCWA(cwa));
} catch (GeoFilterException e) {
errors.add(e);
}
}
}
List<Geometry> geometries = filters.getGeometries();
if (geometries != null) {
geoms.addAll(geometries);
}
List<PreparedGeometry> rval = new ArrayList<>(geoms.size());
for (Geometry geom : geoms) {
rval.add(PreparedGeometryFactory.prepare(geom));
}
return new GeoFilterResult(rval, errors);
}
/**
* Get county warning area geometries by name. Performs a database lookup.
*
* @param cwa
* @return
* @throws GeoFilterException
*/
private static Collection<Geometry> getCWA(String cwa)
throws GeoFilterException {
Map<String, RequestConstraint> map = new HashMap<>();
map.put(CWA_COLUMN, new RequestConstraint(cwa));
SpatialQueryResult[] results;
try {
results = SpatialQueryFactory.create().query(CWA_TABLE, null, null,
map, null);
} catch (SpatialException e) {
throw new GeoFilterException("Unable to query database for CWA: "
+ cwa, e);
}
if (results == null || results.length == 0) {
return Collections.emptyList();
}
List<Geometry> rval = new ArrayList<>(results.length);
for (SpatialQueryResult result : results) {
rval.add(result.geometry);
}
return rval;
}
/**
* Validate bounding box coordinates in attributes. Converts bounding box to
* polygon.
*
* @param bbox
* @return
*/
private static Collection<Geometry> convertBbox(GeoFilterBbox bbox)
throws GeoFilterException {
Coordinate upper = new Coordinate(bbox.getMaxx(), bbox.getMaxy());
Coordinate lower = new Coordinate(bbox.getMinx(), bbox.getMiny());
if (lower.x > upper.x || lower.y > upper.y) {
throw new GeoFilterException(
"Invalid bounding box tag. Minimum coordinate values "
+ "cannot be larger than maximum coordinate values: "
+ lower + " " + upper);
}
if (lower.y < LAT_MIN || upper.y > LAT_MAX) {
throw new GeoFilterException(
"Invalid bounding box tag. Box cannot cross poles: "
+ lower.y + "," + upper.y);
}
List<Geometry> rval = null;
lower.x = normalizeLon(lower.x);
upper.x = normalizeLon(upper.x);
/*
* if normalization switched order, it means it crossed the antimeridian
* and needs to be split into two boxes
*/
if (lower.x > upper.x) {
/* create a new box on the left of the map */
Coordinate leftLower = new Coordinate(LON_MIN, lower.y);
Coordinate leftUpper = new Coordinate(upper.x, upper.y);
/* change the old box to only cover the right of the map */
upper.x = LON_MAX;
Geometry left = createPolygon(leftLower, leftUpper);
Geometry right = createPolygon(lower, upper);
rval = Arrays.asList(left, right);
} else {
rval = Arrays.asList(createPolygon(lower, upper));
}
return rval;
}
/**
* @param lon
* @return longitude normalized to be between -180 and 180
*/
private static double normalizeLon(double lon) {
/* 360 degrees */
final double LON_TOTAL = LON_MAX * 2;
/* account for any far out degrees */
double rval = lon % LON_TOTAL;
/* subtract or add to get in range */
if (rval > LON_MAX) {
rval -= LON_TOTAL;
} else if (rval < LON_MIN) {
rval += LON_TOTAL;
}
return rval;
}
/**
* @param mins
* @param maxes
* @return polygon defining a bounding box using corner points
*/
private static Geometry createPolygon(Coordinate mins, Coordinate maxes) {
Coordinate[] coordinates = new Coordinate[5];
coordinates[0] = new Coordinate(mins.x, mins.y);
coordinates[1] = new Coordinate(mins.x, maxes.y);
coordinates[2] = new Coordinate(maxes.x, maxes.y);
coordinates[3] = new Coordinate(maxes.x, mins.y);
/* polygons need a closed ring */
coordinates[4] = coordinates[0];
return GEOM_FACTORY.createPolygon(coordinates);
}
}

View file

@ -0,0 +1,90 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.filter;
import java.util.Collection;
import java.util.Collections;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
/**
* Geographic filter parsing results. Wraps parsed data and any parsing errors.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 10, 2014 3226 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class GeoFilterResult {
private final Collection<GeoFilterException> errors;
private final Collection<PreparedGeometry> filters;
/**
* @param filters
*/
public GeoFilterResult(Collection<PreparedGeometry> filters) {
this(filters, Collections.<GeoFilterException> emptyList());
}
/**
* @param filters
* @param errors
*/
public GeoFilterResult(Collection<PreparedGeometry> filters,
Collection<GeoFilterException> errors) {
this.filters = filters;
this.errors = errors;
}
/**
* @return true if any errors were encountered during parsing
* @see #getErrors()
*/
public boolean hasErrors() {
return !errors.isEmpty();
}
/**
* @return the errors
* @see #hasErrors()
*/
public Collection<GeoFilterException> getErrors() {
return errors;
}
/**
* @return the filters
*/
public Collection<PreparedGeometry> getFilters() {
return filters;
}
}

View file

@ -0,0 +1,127 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.filter;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.raytheon.uf.common.geospatial.adapter.GeometryAdapter;
import com.vividsolutions.jts.geom.Geometry;
/**
* Root JAXB POJO for geographic filter configuration
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 11, 2014 3226 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
@XmlRootElement(name = "filters")
@XmlAccessorType(XmlAccessType.NONE)
public class GeoFilters {
@XmlElement(name = "wkt")
@XmlJavaTypeAdapter(value = GeometryAdapter.class)
private List<Geometry> geometries;
@XmlElement(name = "cwa")
private List<String> cwas;
@XmlElement(name = "bbox")
private List<GeoFilterBbox> bboxes;
/**
*
*/
public GeoFilters() {
}
/**
* @param geometries
* @param cwas
* @param bboxes
*/
public GeoFilters(List<Geometry> geometries,
List<String> cwas,
List<GeoFilterBbox> bboxes) {
this.geometries = geometries;
this.cwas = cwas;
this.bboxes = bboxes;
}
/**
* @return the geometries
*/
public List<Geometry> getGeometries() {
return geometries;
}
/**
* @param geometries
* the geometries to set
*/
public void setGeometries(List<Geometry> geometries) {
this.geometries = geometries;
}
/**
* @return the cwas
*/
public List<String> getCwas() {
return cwas;
}
/**
* @param cwas
* the cwas to set
*/
public void setCwas(List<String> cwas) {
this.cwas = cwas;
}
/**
* @return the bboxes
*/
public List<GeoFilterBbox> getBboxes() {
return bboxes;
}
/**
* @param bboxes
* the bboxes to set
*/
public void setBboxes(List<GeoFilterBbox> bboxes) {
this.bboxes = bboxes;
}
}

View file

@ -0,0 +1,221 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.filter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.bind.JAXBException;
import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
import com.raytheon.uf.common.localization.ILocalizationFile;
import com.raytheon.uf.common.localization.IPathManager;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
import com.raytheon.uf.common.localization.LocalizationFile;
import com.raytheon.uf.common.localization.PathManagerFactory;
import com.raytheon.uf.common.localization.exception.LocalizationException;
import com.raytheon.uf.common.serialization.SerializationException;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
/**
* Geographic filtering utility for lightning data. Configured in localization.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 10, 2014 3226 bclement Initial creation
* Jan 27, 2016 5237 tgurney Replace LocalizationFile with
* ILocalizationFile
* Jul 13, 2016 5744 mapeters Get filter files from common_static
* instead of edex_static
*
* </pre>
*
* @author bclement
* @see {@link GeoFilterParser}
*/
public class LightningGeoFilter {
private static final IUFStatusHandler log = UFStatus
.getHandler(LightningGeoFilter.class);
public static final String LOCALIZATION_FILTER_DIR = "binlightning"
+ IPathManager.SEPARATOR + "filters";
private static final GeometryFactory GEOM_FACTORY = new GeometryFactory();
/**
* map of lightning source names to filter geometries
*/
private static final Map<String, Collection<PreparedGeometry>> geometryMap = new ConcurrentHashMap<>(
4);
private static volatile boolean initialized = false;
/**
* read filter configuration files from localization. Threadsafe and
* idempotent
*/
private static void initialize() {
synchronized (geometryMap) {
if (!initialized) {
IPathManager pathMgr = PathManagerFactory.getPathManager();
LocalizationFile[] files = pathMgr
.listFiles(
pathMgr.getLocalSearchHierarchy(LocalizationType.COMMON_STATIC),
LOCALIZATION_FILTER_DIR,
new String[] { ".xml" }, true, true);
for (ILocalizationFile file : files) {
Collection<PreparedGeometry> filters = getFilterGeometries(file);
if (!filters.isEmpty()) {
String bareName = getFileNameWithoutExtension(file
.getPath());
geometryMap.put(bareName.toLowerCase(), filters);
}
}
initialized = true;
}
}
}
/**
* Parse filter geometries from filter config file
*
* @param file
* @return empty list on error
*/
private static Collection<PreparedGeometry> getFilterGeometries(
ILocalizationFile file) {
Collection<PreparedGeometry> rval;
try (InputStream in = file.openInputStream()) {
GeoFilterResult result = GeoFilterParser.parse(in);
if (result.hasErrors()) {
for (GeoFilterException e : result.getErrors()) {
log.error(e.getLocalizedMessage(), e);
}
log.warn("Filter parsing included errors, filters will be incomplete");
}
return result.getFilters();
} catch (IOException | JAXBException | LocalizationException
| SerializationException e) {
log.error("Unable to parse filter file: " + file.getPath(), e);
rval = Collections.emptyList();
}
return rval;
}
/**
* Strip the bare file name from path. Removes path and file suffix (if
* either exist)
*
* @param path
* @return
*/
private static String getFileNameWithoutExtension(String path) {
int slashIndex = path.lastIndexOf(IPathManager.SEPARATOR);
int start = slashIndex < 0 ? 0 : slashIndex + 1;
int dotIndex = path.lastIndexOf('.');
if (dotIndex < 0) {
return path.substring(start);
} else {
return path.substring(start, dotIndex);
}
}
/**
* Applies data source filter to strikes. Source is determined from sample
* strike. If no filter exists for source, provided collection is returned.
*
* @param strikes
* @return
*/
public static Collection<LightningStrikePoint> filterStrikes(
Collection<LightningStrikePoint> strikes) {
if (!initialized) {
initialize();
}
Collection<LightningStrikePoint> rval = strikes;
if (!strikes.isEmpty()) {
LightningStrikePoint sample = strikes.iterator().next();
String source = BinLightningRecord.getDataSource(sample);
Collection<PreparedGeometry> filter = geometryMap.get(source
.toLowerCase());
if (filter != null && !filter.isEmpty()) {
rval = new ArrayList<>(strikes.size());
for (LightningStrikePoint strike : strikes) {
if (passesFilter(filter, strike)) {
rval.add(strike);
}
}
}
}
return rval;
}
/**
* @param filter
* @param strike
* @return true if strike is located in any of the filter geometries
*/
private static boolean passesFilter(Collection<PreparedGeometry> filter,
LightningStrikePoint strike) {
boolean passes = false;
Point p = GEOM_FACTORY.createPoint(new Coordinate(
strike.getLongitude(), strike.getLatitude()));
for (PreparedGeometry g : filter) {
/* covers instead of contains to include points at boundary */
if (g.covers(p)) {
passes = true;
break;
}
}
return passes;
}
/**
* Create a BinLightningRecord after applying data source filter to list of
* strikes
*
* @see #filterStrikes(Collection)
* @param strikes
* @return
*/
public static BinLightningRecord createFilteredRecord(
Collection<LightningStrikePoint> strikes) {
return new BinLightningRecord(filterStrikes(strikes));
}
}

View file

@ -0,0 +1,122 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.impl;
import static com.raytheon.edex.plugin.binlightning.impl.IBinLightningDecoder.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.raytheon.edex.plugin.binlightning.BinLightningDecoder;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
/**
* Read from the message data source, isolate and create a decoder for the
* current sub-message. In the event that the message decoder can not be
* created, an instance of LightningErrorDecoder is created.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 20070810 379 jkorman Initial Coding from prototype.
* 20160116 18408 Wufeng Zhou Remove direct dependency on bit shifting decoder
*
*
* </pre>
*
* @author jkorman
* @version 1.0
*/
public class BinLightningFactory
{
private static final IUFStatusHandler logger = UFStatus.getHandler(BinLightningDecoder.class);
/**
* Read from the message data source, isolate and create a decoder for the
* current sub-message. In the event that the message decoder can not be
* created, an instance of LightningErrorDecoder is created.
* @param msgData The message data source.
* @return The decoder instance.
*/
public static IBinLightningDecoder getDecoder(IBinDataSource msgData)
{
IBinLightningDecoder decoder = null;
int count = msgData.getU8();
int decoderType = msgData.getU8();
switch(decoderType)
{
case FLASH_RPT :
{
String className = "com.raytheon.edex.plugin.binlightning.impl.FlashLightningDecoder";
decoder = loadDecoderInstance(className, msgData, count);
break;
}
case RT_FLASH_RPT :
{
String className = "com.raytheon.edex.plugin.binlightning.impl.RTLightningDecoder";
decoder = loadDecoderInstance(className, msgData, count);
break;
}
case OTHER_RPT :
{
// The D2D decoders declare but do not define this message.
decoder = new LightningErrorDecoder(UNIMPLEMENTED_DECODER);
break;
}
case COMM_RPT :
{
// The D2D decoders declare but do not define this message.
decoder = new LightningErrorDecoder(UNIMPLEMENTED_DECODER);
break;
}
default :
{
decoder = new LightningErrorDecoder(UNKNOWN_MESSAGE_TYPE);
break;
}
}
return decoder;
}
private static IBinLightningDecoder loadDecoderInstance(String className, IBinDataSource msgData, int count) {
IBinLightningDecoder decoder = null;
try {
Class<?> clazz = BinLightningFactory.class.getClassLoader().loadClass(className);
Class<?>[] types = {IBinDataSource.class, Integer.TYPE};
Constructor<?> constructor = clazz.getConstructor(types);
Object[] parameters = {msgData, count};
decoder = (IBinLightningDecoder)constructor.newInstance(parameters);
logger.info("Loaded legacy binlightning decoder class " + className);
} catch (ClassNotFoundException | InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
logger.error("Fail to load binlightning decoder class " + className + ". FYI, this is error only if you are authorized: " + e.getMessage());
decoder = new LightningErrorDecoder(UNIMPLEMENTED_DECODER);
}
return decoder;
}
}

View file

@ -0,0 +1,82 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.impl;
/**
* Implementations of this interface are adapters between some data source and
* the using client code. Note that the idea of big/small endian data is not
* addressed in the interface. This matter is specific to the data and is an
* implementation detail.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 27 July 2007 411 jkorman Initial Development
* 20070912 379 jkorman Code review cleanup.
* 26 Sep 2014 3629 mapeters Moved from uf.edex.decodertools.core.
*
* </pre>
*
* @author jkorman
* @version 1
*/
public interface IBinDataSource {
/**
* Are there at least a specified number of bytes left in the data source?
*
* @param count
* Number of bytes to check for.
* @return
*/
public boolean available(int count);
/**
* Get a signed 8 bit value from the data source.
*
* @return A signed 8 bit value.
*/
public int getS8();
/**
* Get an unsigned 8 bit value from the data source.
*
* @return An unsigned 8 bit value.
*/
public int getU8();
/**
* Get an unsigned 16 bit value from the data source.
*
* @return An unsigned 16 bit value.
*/
public int getU16();
/**
* Get an unsigned 32 bit value from the data source.
*
* @return An unsigned 32 bit value.
*/
public long getU32();
}

View file

@ -0,0 +1,75 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.impl;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgStrikeType;
/**
* Declare the interface for binary lightning decoding. The decoders are
* expected to implement an Iterable interface. Data decoding will take place
* during construction of the element.
*
* <pre>
* the recommended constructor for this interface is
*
* @param data An IBinDataSource data source containing the data to be decoded.
* @param count The number of records that this decoder should see.
* <code>public X (IBinDataSource data, int count)</code>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 20070810 379 jkorman Initial Coding from prototype.
* 20070912 379 jkorman Code review cleanup.
* Jun 05, 2014 3226 bclement LightningStikePoint refactor
*
* </pre>
*
* @author jkorman
* @version 1.0
*/
public interface IBinLightningDecoder extends Iterable<LightningStrikePoint>
{
public static final int NO_ERROR = 0;
public static final int NO_TIME_INFO = 1;
public static final int NOT_ENOUGH_DATA = 2;
public static final int UNKNOWN_MESSAGE_TYPE = 98;
public static final int UNIMPLEMENTED_DECODER = 99;
public static final int FLASH_RPT = 0x96;
public static final int RT_FLASH_RPT = 0x97;
public static final int OTHER_RPT = 0xD0;
public static final int COMM_RPT = 0xD1;
public static final LtgStrikeType DEFAULT_FLASH_TYPE = LtgStrikeType.CLOUD_TO_GROUND;
/*
*/
/**
* Get the last error for the instance.
* @return The error code.
*/
public int getError();
}

View file

@ -0,0 +1,121 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.impl;
import java.io.ByteArrayInputStream;
/**
* Wraps a ByteArrayInputStream with access methods specific to binary
* lightning data.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 20070810 379 jkorman Initial Coding from prototype.
*
* </pre>
*
* @author jkorman
* @version 1.0
*/
public class LightningDataSource implements IBinDataSource
{
private ByteArrayInputStream msgData = null;
/**
* Create this data source from supplied byte array data.
*
* @param data
*/
public LightningDataSource(byte[] data)
{
msgData = new ByteArrayInputStream(data);
}
/**
* Are there at least a given number of bytes available in the data source.
* @param A number of bytes to check for.
* @return Are the bytes available?
*/
public boolean available(int count)
{
return (msgData.available() >= count);
}
/**
* Get a signed 8 bit value from the data source. NOTE : This conversion is
* not twos-complement!
* @return A signed 8 bit value as an int.
*/
public int getS8()
{
int s8 = msgData.read();
int sign = (s8 & 0x80);
s8 = s8 & 0x7F;
if(sign > 0)
{
s8 = 0 - s8;
}
return s8;
}
/**
* Get a unsigned 8 bit value from the data source.
* @return An unsigned 8 bit value as an int.
*/
public int getU8()
{
int u8 = msgData.read();
return u8;
}
/**
* Get a unsigned 16 bit value from the data source.
* @return An unsigned 16 bit value as an int.
*/
public int getU16()
{
int u16 = msgData.read();
u16 |= (msgData.read() << 8);
return u16 & 0xFFFF;
}
/**
* Get a unsigned 32 bit value from the data source.
* @return An unsigned 32 bit value as a long.
*/
public long getU32()
{
long u32 = msgData.read();
u32 |= (msgData.read() << 8);
u32 |= (msgData.read() << 16);
u32 |= (msgData.read() << 24);
return u32 & 0xFFFFFFFFL;
}
}

View file

@ -0,0 +1,96 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.impl;
import java.util.Iterator;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
/**
* Mock decoder that is returned from the lightning decoder factory in the event
* that a proper decoder could not be constructed. Instances are created by passing
* various "permanent" error codes that will always be returned by the instance.
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 20070810 379 jkorman Initial Coding from prototype.
*
* </pre>
*
* @author jkorman
* @version 1.0
*/
public class LightningErrorDecoder implements IBinLightningDecoder
{
private final int errorCode;
/**
* Construct an instance of this decoder with a constant error code.
* @param errorCode The constant error code.
*/
public LightningErrorDecoder(int errorCode)
{
this.errorCode = errorCode;
}
/**
* Get the error code for this decoder.
* @return The error code.
*/
@Override
public int getError()
{
return errorCode;
}
/**
* This Iterator always returns, hasNext() = false, next = null.
* @return The "dummy" iterator for this class.
*/
@Override
public Iterator<LightningStrikePoint> iterator()
{
return new Iterator<LightningStrikePoint>()
{
@Override
public boolean hasNext()
{
return false;
}
@Override
public LightningStrikePoint next()
{
return null;
}
@Override
public void remove()
{
}
};
}
}

View file

@ -0,0 +1,23 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
/**
* Contains implementation classes for decoding binary lightning data.
*/
package com.raytheon.edex.plugin.binlightning.impl;

View file

@ -0,0 +1,24 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
/**
* Binary Lightning decoder plugin specific code. These classes interface
* between the edex plugin proxy and the implemented decoder layer.
*/
package com.raytheon.edex.plugin.binlightning;

View file

@ -0,0 +1,184 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.total;
import java.nio.ByteBuffer;
import com.raytheon.uf.common.numeric.UnsignedNumbers;
/**
* ByteBuffer wrapper that keeps track of checksums
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 03, 2014 3226 bclement Initial creation
* Jun 09, 2014 3226 bclement Added ByteBuffer constructor
* Jul 01, 2015 4581 skorolev Added condition in the getSum(int)
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class ChecksumByteBuffer {
private static final int SHORT_SIZE = Short.SIZE / Byte.SIZE;
private static final int INT_SIZE = Integer.SIZE / Byte.SIZE;
private static final int LONG_SIZE = Integer.SIZE / Byte.SIZE;
private final ByteBuffer buff;
private long totalSum;
private long packetSum;
/**
* @see ByteBuffer#wrap(byte[])
* @param data
*/
public ChecksumByteBuffer(byte[] data) {
this.buff = ByteBuffer.wrap(data);
}
/**
* @param buff
*/
public ChecksumByteBuffer(ByteBuffer buff) {
this.buff = buff;
}
/**
* get the sum of the next numberOfBytes worth of data
*
* @param numberOfBytes
* @return
*/
private long getSum(int numberOfBytes) {
int start = buff.position();
int end = start + numberOfBytes;
long rval = 0;
if (buff.remaining() < numberOfBytes) {
throw new IllegalArgumentException("Unable to get checksum for "
+ numberOfBytes + " bytes, only " + buff.remaining()
+ " bytes in buffer.");
}
for (int i = start; i < end; ++i) {
rval += UnsignedNumbers.ubyteToShort(buff.get(i));
}
return rval;
}
/**
* @see ByteBuffer#get()
* @return
*/
public byte get() {
byte rval = buff.get();
short unsignedRval = UnsignedNumbers.ubyteToShort(rval);
totalSum += unsignedRval;
packetSum += unsignedRval;
return rval;
}
/**
* @see ByteBuffer#getShort()
* @return
*/
public short getShort() {
long sum = getSum(SHORT_SIZE);
totalSum += sum;
packetSum += sum;
return buff.getShort();
}
/**
* @see ByteBuffer#getInt()
* @return
*/
public int getInt() {
long sum = getSum(INT_SIZE);
totalSum += sum;
packetSum += sum;
return buff.getInt();
}
/**
* @see ByteBuffer#getLong()
* @return
*/
public long getLong() {
long sum = getSum(LONG_SIZE);
totalSum += sum;
packetSum += sum;
return buff.getLong();
}
/**
* reset the current packet sum to zero
*/
public void resetPacketSum() {
packetSum = 0;
}
/**
* reset all sums to zero
*/
public void resetAllSums() {
resetPacketSum();
totalSum = 0;
}
/**
* @return the totalSum
*/
public long getTotalSum() {
return totalSum;
}
/**
* @return the packetSum
*/
public long getPacketSum() {
return packetSum;
}
/**
* @see ByteBuffer#limit()
* @return
*/
public int size() {
return buff.limit();
}
/**
* @see ByteBuffer#position()
* @return
*/
public int position() {
return buff.position();
}
}

View file

@ -0,0 +1,473 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.plugin.binlightning.total;
import gov.noaa.nws.ost.edex.plugin.binlightning.DecryptedLightningValidator;
import gov.noaa.nws.ost.edex.plugin.binlightning.EncryptedBinLightningCipher;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import com.raytheon.edex.esb.Headers;
import com.raytheon.edex.exception.DecoderException;
import com.raytheon.edex.plugin.binlightning.BinLightningDecoder;
import com.raytheon.edex.plugin.binlightning.filter.LightningGeoFilter;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord;
import com.raytheon.uf.common.dataplugin.binlightning.impl.BaseLightningPoint;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningPulsePoint;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgMsgType;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgPulseType;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgStrikeType;
import com.raytheon.uf.common.numeric.UnsignedNumbers;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.time.util.TimeUtil;
import com.raytheon.uf.common.wmo.WMOHeader;
import com.raytheon.uf.common.wmo.WMOTimeParser;
/**
* Decoder for Earth Networks Total Lightning data
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* May 30, 2014 3226 bclement Initial creation
* Jun 09, 2014 3226 bclement added encryption support
* Jun 10, 2014 3226 bclement added filter support
* Jun 19, 2014 3226 bclement added validator callback
* Jul 07, 2015 4581 skorolev Corrected decodeStrikes to avoid BufferUnderflowException.
* Apr 07, 2016 DR18763 mgamazaychikov Switched to using LightningWMOHeader.
* Apr 21, 2016 DR18849 mgamazaychikov Decrypt all data in decrypt method.
* May 02, 2016 18336 amoore Keep-alive messages should update the legend.
* Jun 10, 2016 DR18939 mgamazaychikov Removed LightningWMOHeader.
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class TotalLightningDecoder {
// flash types
public static final byte CLOUD_TO_GROUND_TYPE = 0x00;
public static final byte CLOUD_TO_CLOUD_TYPE = 0x01;
// pulse types
public static final byte RETURN_STROKE_TYPE = 0x00;
public static final byte NON_RETURN_STROKE_TYPE = 0x01;
public static final byte KEEP_ALIVE_TYPE = 0x09;
// conversions
public static final double LONLAT_SCALE_FACTOR = 0.0000001;
public static final double AMPS_PER_KILO_AMP = 1000.0;
public static final double METERS_PER_KILOMETER = 1000.0;
// constant metadata
public static final String DATA_SOURCE = "ENTLN";
/* in bytes, header is total size of flash and pulses */
private static final int COMBINATION_PACKET_HEADER_SIZE = 2;
/* in bytes, doesn't include checksum */
private static final int FLASH_PACKET_SIZE = 25;
private static final IUFStatusHandler log = UFStatus
.getHandler(TotalLightningDecoder.class);
private static final EncryptedBinLightningCipher CIPHER = new EncryptedBinLightningCipher();
public static final String TOTAL_LIGHTNING_KEYSTORE_PREFIX = "total.lightning";
private static final DecryptedLightningValidator validator = new DecryptedLightningValidator() {
@Override
public boolean isValid(byte[] data) {
return validFlashPacket(data, COMBINATION_PACKET_HEADER_SIZE);
}
};
private static final int SHORT_SIZE = 2;
/**
* Parse total lightning data into BinLightningRecords
*
* @param data
* @param headers
* @return
*/
public PluginDataObject[] decode(byte[] data, Headers headers) {
PluginDataObject[] rval;
WMOHeader wmoHdr = new WMOHeader(data);
String fileName = (String) headers.get(WMOHeader.INGEST_FILE_NAME);
if (wmoHdr.isValid()) {
byte[] pdata = BinLightningDecoder.extractPData(wmoHdr, data);
if (pdata != null) {
try {
rval = decodeInternal(wmoHdr, fileName, pdata);
} catch (Exception e) {
error(e, headers, wmoHdr);
rval = new PluginDataObject[0];
}
} else {
warn("Unable to separate data from headers", fileName, wmoHdr);
rval = new PluginDataObject[0];
}
} else {
warn("Invalid WMO header", fileName, wmoHdr);
rval = new PluginDataObject[0];
}
return rval;
}
/**
* @param data
* @param startIndex
* starting index of flash packet (not combination packet)
* @return true if there if a valid flash packet in data starting at index
*/
private static boolean validFlashPacket(byte[] data, int startIndex) {
/* plus one to include packet checksum */
final int packetWithChecksum = FLASH_PACKET_SIZE + 1;
if (data.length < startIndex + packetWithChecksum) {
return false;
}
ChecksumByteBuffer buff = new ChecksumByteBuffer(ByteBuffer.wrap(data,
startIndex, packetWithChecksum));
for (int i = 0; i < FLASH_PACKET_SIZE; ++i) {
/* build up sum in buffer */
buff.get();
}
return passesCheckSum(buff, false);
}
/**
* Display warning message with file and header names
*
* @param msg
* @param fileName
* @param wmoHdr
*/
private void warn(String msg, String fileName, WMOHeader wmoHdr) {
log.warn(msg + ". File: " + fileName + ", WMO Header: " + wmoHdr);
}
/**
* Display error message with file and header names
*
* @param e
* @param headers
* @param wmoHdr
*/
private void error(Exception e, Headers headers, WMOHeader wmoHdr) {
String fileName = (String) headers.get(WMOHeader.INGEST_FILE_NAME);
log.error(e.getLocalizedMessage() + ". File: " + fileName
+ ", WMO Header: " + wmoHdr, e);
}
/**
* @param wmoHdr
* @param fileName
* @param pdata
* data after WMO header is removed
* @return
* @throws DecoderException
*/
private PluginDataObject[] decodeInternal(WMOHeader wmoHdr,
String fileName, byte[] pdata) throws DecoderException {
byte[] pdataPreDecrypt = pdata;
// determine if the data is encrypted or not based on comparing
// checksums for flash packet
pdata = decrypt(wmoHdr, fileName, pdata);
boolean isDecryptedValid = validFlashPacket(pdata,
COMBINATION_PACKET_HEADER_SIZE);
boolean isPreDecryptedValid = validFlashPacket(pdataPreDecrypt,
COMBINATION_PACKET_HEADER_SIZE);
// assume that all data is encrypted, so decrypt it
if (!isDecryptedValid && isPreDecryptedValid) {
// this means that data is not encrypted, proceed without
// decryption
pdata = pdataPreDecrypt;
}
List<LightningStrikePoint> strikes = decodeStrikes(fileName, pdata);
BinLightningRecord record;
if (!strikes.isEmpty()) {
record = LightningGeoFilter.createFilteredRecord(strikes);
} else {
// keep-alive record
Calendar baseTime = WMOTimeParser.findDataTime(wmoHdr.getYYGGgg(),
fileName);
record = new BinLightningRecord(baseTime, DATA_SOURCE);
}
return new PluginDataObject[] { record };
}
/**
* @param wmoHdr
* @param fileName
* @param pdata
* @return
* @throws DecoderException
*/
private byte[] decrypt(WMOHeader wmoHdr, String fileName,
byte[] pdata) throws DecoderException {
Calendar baseTime = WMOTimeParser.findDataTime(wmoHdr.getYYGGgg(),
fileName);
pdata = EncryptedBinLightningCipher.prepDataForDecryption(pdata,
fileName);
try {
return CIPHER.decryptData(pdata, baseTime.getTime(),
TOTAL_LIGHTNING_KEYSTORE_PREFIX, validator);
} catch (Exception e) {
throw new DecoderException("Problem decrypting total lightning", e);
}
}
/**
* Extract strike data from raw binary, ignoring keep-alive strikes.
*
* @param fileName
* @param pdata
* @return list of lightning strike points, ignoring keep-alive strikes.
* @throws DecoderException
*/
private List<LightningStrikePoint> decodeStrikes(String fileName,
byte[] pdata) throws DecoderException {
List<LightningStrikePoint> rval = new ArrayList<LightningStrikePoint>();
ChecksumByteBuffer buff = new ChecksumByteBuffer(pdata);
while (buff.position() < buff.size()) {
int startingPostion = buff.position();
int totalBytes = 0;
if ((buff.size() - startingPostion) >= SHORT_SIZE) {
totalBytes = UnsignedNumbers.ushortToInt(buff.getShort());
if (totalBytes > (buff.size() - startingPostion)) {
if (validFlashPacket(pdata, buff.position())) {
log.error("Truncated total lightning packet in file: "
+ fileName);
} else {
int extra = buff.size() - startingPostion;
log.warn("Extra data at end of lightning packets: "
+ extra + " bytes");
}
break;
}
} else {
log.warn("Extra data at end of lightning packets: 1 byte");
break;
}
/* start flash packet */
buff.resetPacketSum();
/* discard flash packet size byte */
buff.get();
LtgStrikeType flashType = getStrikeType(buff.get());
LightningStrikePoint strike = new LightningStrikePoint(null,
LtgMsgType.TOTAL_LIGHTNING);
strike.setLightSource(DATA_SOURCE);
strike.setType(flashType);
decodeCommonFields(strike, buff);
int pulseCount = UnsignedNumbers.ubyteToShort(buff.get());
strike.setPulseCount(pulseCount);
ensureCheckSum(buff, false);
List<LightningPulsePoint> pulses = new ArrayList<LightningPulsePoint>(
pulseCount);
for (int i = 0; i < pulseCount; ++i) {
/* discard size of pulse packet (always 26) */
buff.get();
LtgPulseType pulseType = getPulseType(buff.get());
LightningPulsePoint pulse = new LightningPulsePoint(null,
pulseType);
decodeCommonFields(pulse, buff);
/* discard pulse count (already set in strike) */
buff.get();
ensureCheckSum(buff, false);
pulses.add(pulse);
}
strike.setPulses(pulses);
ensureCheckSum(buff, true);
if (strike.getType() != LtgStrikeType.KEEP_ALIVE) {
rval.add(strike);
}
}
return rval;
}
/**
* Extract fields common to both strikes and pulses
*
* @param point
* @param buff
*/
private static void decodeCommonFields(BaseLightningPoint point,
ChecksumByteBuffer buff) {
point.setTime(getTime(buff));
point.setLatitude(getDouble(buff, LONLAT_SCALE_FACTOR));
point.setLongitude(getDouble(buff, LONLAT_SCALE_FACTOR));
point.setStrikeStrength(getKiloAmps(buff.getInt()));
/* discard reserved byte */
buff.get();
point.setElevation(getMeters(buff.getShort()));
point.setSensorCount(UnsignedNumbers.ubyteToShort(buff.get()));
}
/**
* Create calendar from 4 byte UNIX time and 2 byte millisecond addition
*
* @param buff
* @return
*/
private static Calendar getTime(ChecksumByteBuffer buff) {
long unixTime = UnsignedNumbers.uintToLong(buff.getInt());
int additionalMillis = UnsignedNumbers.ushortToInt(buff.getShort());
long totalMillis = (unixTime * TimeUtil.MILLIS_PER_SECOND)
+ additionalMillis;
return TimeUtil.newGmtCalendar(new Date(totalMillis));
}
/**
* @see #passesCheckSum(ChecksumByteBuffer, boolean)
* @param buff
* @param total
* @throws DecoderException
*/
private static void ensureCheckSum(ChecksumByteBuffer buff, boolean total)
throws DecoderException {
if (!passesCheckSum(buff, total)) {
throw new DecoderException("Checksum failed");
}
}
/**
* Ensure data integrity, resets appropriate sum(s) in buffer after check
*
* @param buff
* @param total
* true if total sum should be checked, otherwise checks packet
* sum
* @return true if checksum passes
*/
private static boolean passesCheckSum(ChecksumByteBuffer buff, boolean total) {
long rawsum = total ? buff.getTotalSum() : buff.getPacketSum();
/* convert to overflowed unsigned byte */
rawsum &= 0xFF;
/* checksum algorithm from total lightning spec */
long mungedSum = (256 - rawsum) & 0xFF;
/* get expected after sum so it is not reflected in sum */
long expected = UnsignedNumbers.ubyteToShort(buff.get());
boolean rval = mungedSum == expected;
if (!rval) {
log.debug("Checksum failed: expected " + expected + " got "
+ mungedSum);
}
if (total) {
buff.resetAllSums();
} else {
buff.resetPacketSum();
}
return rval;
}
/**
* Get scaled double from 4 byte integer field
*
* @param buff
* @param scaleFactor
* @return
*/
private static double getDouble(ChecksumByteBuffer buff, double scaleFactor) {
int raw = buff.getInt();
return raw * scaleFactor;
}
/**
* Convert amps to kiloamps
*
* @param amps
* @return
*/
private static double getKiloAmps(int amps) {
return amps / AMPS_PER_KILO_AMP;
}
/**
* Convert kilometers to meters
*
* @param kilometers
* @return
*/
private static double getMeters(short kilometers) {
return kilometers * METERS_PER_KILOMETER;
}
/**
* Map strike byte to internal enum
*
* @param type
* @return
* @throws DecoderException
*/
public static LtgStrikeType getStrikeType(byte type)
throws DecoderException {
switch (type) {
case CLOUD_TO_GROUND_TYPE:
return LtgStrikeType.CLOUD_TO_GROUND;
case CLOUD_TO_CLOUD_TYPE:
return LtgStrikeType.CLOUD_TO_CLOUD;
case KEEP_ALIVE_TYPE:
return LtgStrikeType.KEEP_ALIVE;
}
throw new DecoderException("Unknown flash type: " + type);
}
/**
* Map pulse byte to internal enum
*
* @param type
* @return
* @throws DecoderException
*/
public static LtgPulseType getPulseType(byte type) throws DecoderException {
switch (type) {
case RETURN_STROKE_TYPE:
return LtgPulseType.RETURN_STROKE;
case NON_RETURN_STROKE_TYPE:
return LtgPulseType.NON_RETURN_STROKE;
case KEEP_ALIVE_TYPE:
return LtgPulseType.KEEP_ALIVE;
}
throw new DecoderException("Unknown pulse type: " + type);
}
}

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
This_software_was_developed_and_/_or_modified_by_Raytheon_Company,
pursuant_to_Contract_DG133W-05-CQ-1067_with_the_US_Government.
U.S._EXPORT_CONTROLLED_TECHNICAL_DATA
This_software_product_contains_export-restricted_data_whose
export/transfer/disclosure_is_restricted_by_U.S._law._Dissemination
to_non-U.S._persons_whether_in_the_United_States_or_abroad_requires
an_export_license_or_other_authorization.
Contractor_Name:________Raytheon_Company
Contractor_Address:_____6825_Pine_Street,_Suite_340
________________________Mail_Stop_B8
________________________Omaha,_NE_68106
________________________402.291.0100
See_the_AWIPS_II_Master_Rights_File_("Master_Rights_File.pdf")_for
further_licensing_information.
-->
<!--
This is an absolute override file, indicating that a higher priority
version of the file will completely replace a lower priority version
of the file.
-->
<requestPatterns xmlns:ns2="group">
<regex>^SFUS4[13] KWBC.*</regex>
<regex>^SFPA4[123] KWBC.*</regex>
</requestPatterns>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
This_software_was_developed_and_/_or_modified_by_Raytheon_Company,
pursuant_to_Contract_DG133W-05-CQ-1067_with_the_US_Government.
U.S._EXPORT_CONTROLLED_TECHNICAL_DATA
This_software_product_contains_export-restricted_data_whose
export/transfer/disclosure_is_restricted_by_U.S._law._Dissemination
to_non-U.S._persons_whether_in_the_United_States_or_abroad_requires
an_export_license_or_other_authorization.
Contractor_Name:________Raytheon_Company
Contractor_Address:_____6825_Pine_Street,_Suite_340
________________________Mail_Stop_B8
________________________Omaha,_NE_68106
________________________402.291.0100
See_the_AWIPS_II_Master_Rights_File_("Master_Rights_File.pdf")_for
further_licensing_information.
-->
<!--
This is an absolute override file, indicating that a higher priority
version of the file will completely replace a lower priority version
of the file.
Incremental overrides can also be achieved by creating a new file with
a suffix inserted between the original filename and extension.
-->
<purgeRuleSet>
<defaultRule>
<versionsToKeep>36</versionsToKeep>
<delta>=00-01:00:00</delta>
<round>00-01:00:00</round>
</defaultRule>
</purgeRuleSet>

View file

@ -143,6 +143,27 @@
version="0.0.0"
unpack="false"/>
<plugin
id="com.raytheon.edex.plugin.binlightning"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.raytheon.edex.plugin.binlightning.legacy"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="gov.noaa.nws.ost.edex.plugin.binlightning"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.raytheon.uf.edex.plugin.cwa"
download-size="0"