From 154776639c397b64ec1a43ee4973b2cbbe07a83f Mon Sep 17 00:00:00 2001 From: mjames-upc Date: Mon, 16 Jul 2018 11:57:40 -0600 Subject: [PATCH] readd binlightning plugins --- .../.classpath | 7 + .../.project | 28 + .../META-INF/MANIFEST.MF | 8 + .../build.properties | 4 + .../impl/BaseLightningDecoder.java | 155 +++++ .../impl/FlashLightningDecoder.java | 132 ++++ .../binlightning/impl/RTLightningDecoder.java | 174 +++++ .../.classpath | 7 + .../.project | 28 + .../META-INF/MANIFEST.MF | 32 + .../build.properties | 6 + .../com.raytheon.edex.plugin.binlightning.ecl | 0 .../res/spring/binlightning-common.xml | 18 + .../res/spring/binlightning_ep-ingest.xml | 56 ++ .../binlightning/BinLightningDecoder.java | 655 ++++++++++++++++++ .../binlightning/dao/BinLightningDao.java | 105 +++ .../binlightning/filter/GeoFilterBbox.java | 136 ++++ .../filter/GeoFilterException.java | 70 ++ .../binlightning/filter/GeoFilterParser.java | 295 ++++++++ .../binlightning/filter/GeoFilterResult.java | 90 +++ .../binlightning/filter/GeoFilters.java | 127 ++++ .../filter/LightningGeoFilter.java | 221 ++++++ .../impl/BinLightningFactory.java | 122 ++++ .../binlightning/impl/IBinDataSource.java | 82 +++ .../impl/IBinLightningDecoder.java | 75 ++ .../impl/LightningDataSource.java | 121 ++++ .../impl/LightningErrorDecoder.java | 96 +++ .../binlightning/impl/package-info.java | 23 + .../plugin/binlightning/package-info.java | 24 + .../total/ChecksumByteBuffer.java | 184 +++++ .../total/TotalLightningDecoder.java | 473 +++++++++++++ .../base/distribution/binlightning.xml | 30 + .../base/purge/binlightningPurgeRules.xml | 36 + .../feature.xml | 21 + 34 files changed, 3641 insertions(+) create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/.classpath create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/.project create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/META-INF/MANIFEST.MF create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/build.properties create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/BaseLightningDecoder.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/FlashLightningDecoder.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/RTLightningDecoder.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/.classpath create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/.project create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/build.properties create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/com.raytheon.edex.plugin.binlightning.ecl create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/res/spring/binlightning-common.xml create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/res/spring/binlightning_ep-ingest.xml create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/BinLightningDecoder.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/dao/BinLightningDao.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterBbox.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterException.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterParser.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterResult.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilters.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/LightningGeoFilter.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/BinLightningFactory.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/IBinDataSource.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/IBinLightningDecoder.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/LightningDataSource.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/LightningErrorDecoder.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/package-info.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/package-info.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/ChecksumByteBuffer.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/utility/common_static/base/distribution/binlightning.xml create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/utility/common_static/base/purge/binlightningPurgeRules.xml diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/.classpath b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/.classpath new file mode 100644 index 0000000000..098194ca4b --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/.project b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/.project new file mode 100644 index 0000000000..b0d710aa27 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/.project @@ -0,0 +1,28 @@ + + + com.raytheon.edex.plugin.binlightning.legacy + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..adc21b9440 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/META-INF/MANIFEST.MF @@ -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 diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/build.properties b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/build.properties new file mode 100644 index 0000000000..34d2e4d2da --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/BaseLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/BaseLightningDecoder.java new file mode 100644 index 0000000000..592bb990ff --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/BaseLightningDecoder.java @@ -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. + * + *
+ * 
+ * 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
+ * 
+ * + * @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 strikes = new ArrayList(); + + /** + * 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 iterator() + { + return strikes.iterator(); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/FlashLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/FlashLightningDecoder.java new file mode 100644 index 0000000000..10e407327c --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/FlashLightningDecoder.java @@ -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. + * + *
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * 20070810            379 jkorman     Initial Coding from prototype.
+ * Jun 05, 2014 3226       bclement    LightningStikePoint refactor
+ * 
+ * 
+ * + * @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; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/RTLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/RTLightningDecoder.java new file mode 100644 index 0000000000..662e480cfa --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning.legacy/src/com/raytheon/edex/plugin/binlightning/impl/RTLightningDecoder.java @@ -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. + * + *
+ * 
+ * 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
+ * 
+ * + * @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; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/.classpath b/edexOsgi/com.raytheon.edex.plugin.binlightning/.classpath new file mode 100644 index 0000000000..1fa3e6803d --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/.project b/edexOsgi/com.raytheon.edex.plugin.binlightning/.project new file mode 100644 index 0000000000..fcb9c99beb --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/.project @@ -0,0 +1,28 @@ + + + com.raytheon.edex.plugin.binlightning + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..0f46d7e326 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF @@ -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" diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/build.properties b/edexOsgi/com.raytheon.edex.plugin.binlightning/build.properties new file mode 100644 index 0000000000..20a5272307 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/build.properties @@ -0,0 +1,6 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + utility/,\ + .,\ + res/ diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/com.raytheon.edex.plugin.binlightning.ecl b/edexOsgi/com.raytheon.edex.plugin.binlightning/com.raytheon.edex.plugin.binlightning.ecl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/res/spring/binlightning-common.xml b/edexOsgi/com.raytheon.edex.plugin.binlightning/res/spring/binlightning-common.xml new file mode 100644 index 0000000000..447574701f --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/res/spring/binlightning-common.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/res/spring/binlightning_ep-ingest.xml b/edexOsgi/com.raytheon.edex.plugin.binlightning/res/spring/binlightning_ep-ingest.xml new file mode 100644 index 0000000000..b8c4aad5a3 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/res/spring/binlightning_ep-ingest.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + binlightning + + + + + + + ${in.header.header} regex '^SFPA42 KWBC.*' + + + + + + + + + + + + java.lang.Throwable + + + + + + + \ No newline at end of file diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/BinLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/BinLightningDecoder.java new file mode 100644 index 0000000000..db0b7becec --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/BinLightningDecoder.java @@ -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.
+ * + * + *
+ * 
+ * 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.
+ * 
+ * 
+ * + * @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 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 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 records, Calendar baseTime) + throws DecoderException { + Calendar c = TimeUtil.newCalendar(baseTime); + if (c == null) { + throw new DecoderException(traceId + " - Error decoding times"); + } + ArrayList 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 checkBinRange( + BinLightningRecord record, + Collection strikes) { + Collection 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 rebin( + Collection strikes) { + Map> 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 bin = binMap.get(key); + if (bin == null) { + bin = new ArrayList<>(strikes.size()); + binMap.put(key, bin); + } + bin.add(strike); + } + Collection rval = new ArrayList<>(binMap.size()); + for (Entry> e : binMap + .entrySet()) { + Collection 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 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 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 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 decodeBitShiftedBinLightningData( + byte[] pdata, WMOHeader wmoHdr) { + List 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; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/dao/BinLightningDao.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/dao/BinLightningDao.java new file mode 100644 index 0000000000..e335229465 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/dao/BinLightningDao.java @@ -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 + * + *
+ * 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
+ * 
+ * + * @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 strikeDataArrays = binLightningRec + .getStrikeDataArrays(); + populateFromMap(dataStore, obj, binLightningRec.getDataURI(), + strikeDataArrays); + Map 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 data) + throws StorageException { + for (Entry e : data.entrySet()) { + String name = e.getKey(); + Object dataArray = e.getValue(); + IDataRecord record = DataStoreFactory.createStorageRecord(name, + group, dataArray); + record.setCorrelationObject(obj); + dataStore.addDataRecord(record); + } + } +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterBbox.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterBbox.java new file mode 100644 index 0000000000..a801e56c8f --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterBbox.java @@ -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 + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 11, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @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; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterException.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterException.java new file mode 100644 index 0000000000..7b1522c171 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterException.java @@ -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 + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 10, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @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); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterParser.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterParser.java new file mode 100644 index 0000000000..a2118b1181 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterParser.java @@ -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.
+ *
+ * Example file: + * + *
+ * {@code
+ * 
+ * 
+ *     
+ *     
+ *     
+ *     OAX
+ *     DMX
+ *     FSD
+ *     
+ *     POLYGON ((-101.24 41.4, -100.6 41.15, -101.01 40.75, -101.24 41.4))
+ * 
+ * }
+ * 
+ * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 10, 2014 3226       bclement    Initial creation
+ * Jul 08, 2016 5744       mapeters    Added example file to javadoc
+ * 
+ * 
+ * + * @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 geoms = new ArrayList<>(); + List errors = new ArrayList<>(); + List bboxes = filters.getBboxes(); + if (bboxes != null && !bboxes.isEmpty()) { + for (GeoFilterBbox bbox : bboxes) { + try { + geoms.addAll(convertBbox(bbox)); + } catch (GeoFilterException e) { + errors.add(e); + } + } + } + List cwas = filters.getCwas(); + if (cwas != null && !cwas.isEmpty()) { + for (String cwa : cwas) { + try { + geoms.addAll(getCWA(cwa)); + } catch (GeoFilterException e) { + errors.add(e); + } + } + } + List geometries = filters.getGeometries(); + if (geometries != null) { + geoms.addAll(geometries); + } + List 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 getCWA(String cwa) + throws GeoFilterException { + Map 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 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 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 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); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterResult.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterResult.java new file mode 100644 index 0000000000..97572d1620 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterResult.java @@ -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. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 10, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class GeoFilterResult { + + private final Collection errors; + + private final Collection filters; + + + /** + * @param filters + */ + public GeoFilterResult(Collection filters) { + this(filters, Collections. emptyList()); + } + + /** + * @param filters + * @param errors + */ + public GeoFilterResult(Collection filters, + Collection 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 getErrors() { + return errors; + } + + /** + * @return the filters + */ + public Collection getFilters() { + return filters; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilters.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilters.java new file mode 100644 index 0000000000..fbaf5c4f80 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilters.java @@ -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 + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 11, 2014 3226      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +@XmlRootElement(name = "filters") +@XmlAccessorType(XmlAccessType.NONE) +public class GeoFilters { + + @XmlElement(name = "wkt") + @XmlJavaTypeAdapter(value = GeometryAdapter.class) + private List geometries; + + @XmlElement(name = "cwa") + private List cwas; + + @XmlElement(name = "bbox") + private List bboxes; + + /** + * + */ + public GeoFilters() { + } + + /** + * @param geometries + * @param cwas + * @param bboxes + */ + public GeoFilters(List geometries, + List cwas, + List bboxes) { + this.geometries = geometries; + this.cwas = cwas; + this.bboxes = bboxes; + } + + /** + * @return the geometries + */ + public List getGeometries() { + return geometries; + } + + /** + * @param geometries + * the geometries to set + */ + public void setGeometries(List geometries) { + this.geometries = geometries; + } + + /** + * @return the cwas + */ + public List getCwas() { + return cwas; + } + + /** + * @param cwas + * the cwas to set + */ + public void setCwas(List cwas) { + this.cwas = cwas; + } + + /** + * @return the bboxes + */ + public List getBboxes() { + return bboxes; + } + + /** + * @param bboxes + * the bboxes to set + */ + public void setBboxes(List bboxes) { + this.bboxes = bboxes; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/LightningGeoFilter.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/LightningGeoFilter.java new file mode 100644 index 0000000000..f8e611fedd --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/LightningGeoFilter.java @@ -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. + * + *
+ * 
+ * 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
+ * 
+ * 
+ * + * @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> 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 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 getFilterGeometries( + ILocalizationFile file) { + Collection 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 filterStrikes( + Collection strikes) { + if (!initialized) { + initialize(); + } + Collection rval = strikes; + if (!strikes.isEmpty()) { + LightningStrikePoint sample = strikes.iterator().next(); + String source = BinLightningRecord.getDataSource(sample); + Collection 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 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 strikes) { + return new BinLightningRecord(filterStrikes(strikes)); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/BinLightningFactory.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/BinLightningFactory.java new file mode 100644 index 0000000000..affa69df7f --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/BinLightningFactory.java @@ -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. + * + *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * 20070810            379 jkorman     Initial Coding from prototype.
+ * 20160116          18408 Wufeng Zhou  Remove direct dependency on bit shifting decoder
+ * 
+ * 
+ * 
+ * + * @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; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/IBinDataSource.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/IBinDataSource.java new file mode 100644 index 0000000000..6b5672aa95 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/IBinDataSource.java @@ -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. + * + *
+ * 
+ * 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.
+ * 
+ * 
+ * + * @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(); + +} \ No newline at end of file diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/IBinLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/IBinLightningDecoder.java new file mode 100644 index 0000000000..09eee9e737 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/IBinLightningDecoder.java @@ -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. + * + *
+ * 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.
+ * public X (IBinDataSource data, int count)
+ * 
+ * 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
+ * 
+ * 
+ * + * @author jkorman + * @version 1.0 + */ +public interface IBinLightningDecoder extends Iterable +{ + 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(); + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/LightningDataSource.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/LightningDataSource.java new file mode 100644 index 0000000000..1daeea465e --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/LightningDataSource.java @@ -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. + * + *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * 20070810            379 jkorman     Initial Coding from prototype.
+ * 
+ * 
+ * + * @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; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/LightningErrorDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/LightningErrorDecoder.java new file mode 100644 index 0000000000..62654038ef --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/LightningErrorDecoder.java @@ -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. + *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * 20070810            379 jkorman     Initial Coding from prototype.
+ * 
+ * 
+ * + * @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 iterator() + { + return new Iterator() + { + + @Override + public boolean hasNext() + { + return false; + } + + @Override + public LightningStrikePoint next() + { + return null; + } + + @Override + public void remove() + { + } + }; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/package-info.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/package-info.java new file mode 100644 index 0000000000..a1224385e1 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/package-info.java @@ -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; \ No newline at end of file diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/package-info.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/package-info.java new file mode 100644 index 0000000000..92118a398f --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/package-info.java @@ -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; \ No newline at end of file diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/ChecksumByteBuffer.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/ChecksumByteBuffer.java new file mode 100644 index 0000000000..65302c0bbc --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/ChecksumByteBuffer.java @@ -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 + * + *
+ * 
+ * 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)
+ * 
+ * 
+ * + * @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(); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java new file mode 100644 index 0000000000..d168d026ab --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java @@ -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 + * + *
+ * 
+ * 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.
+ * 
+ * 
+ * + * @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 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 decodeStrikes(String fileName, + byte[] pdata) throws DecoderException { + List rval = new ArrayList(); + 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 pulses = new ArrayList( + 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); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/common_static/base/distribution/binlightning.xml b/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/common_static/base/distribution/binlightning.xml new file mode 100644 index 0000000000..7cc8122f66 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/common_static/base/distribution/binlightning.xml @@ -0,0 +1,30 @@ + + + + + + ^SFUS4[13] KWBC.* + ^SFPA4[123] KWBC.* + diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/common_static/base/purge/binlightningPurgeRules.xml b/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/common_static/base/purge/binlightningPurgeRules.xml new file mode 100644 index 0000000000..c9775923f6 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/common_static/base/purge/binlightningPurgeRules.xml @@ -0,0 +1,36 @@ + + + + + + + 36 + =00-01:00:00 + 00-01:00:00 + + diff --git a/edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml b/edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml index 632582f73c..d1171834ee 100644 --- a/edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml +++ b/edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml @@ -143,6 +143,27 @@ version="0.0.0" unpack="false"/> + + + + + +