From c27800076411ca0b1674c7121c3e47e5ac1944f8 Mon Sep 17 00:00:00 2001 From: Brian Clements Date: Mon, 2 Jun 2014 10:43:23 -0500 Subject: [PATCH] Omaha #3226 added total lightning decoder refactored binlightning to support pulse data renamed strikeCount dataset to pulseCount added initial cave support for total lightning Change-Id: I67f8a0f26a615ddefb9ff7f7d7d78bb05f0877f3 Former-commit-id: e2583481a77d08249030838ebd52eed6e54b2986 --- .../lightning/entlnLightningBundleItems.xml | 37 ++ .../lightning/gldLightningBundleItems.xml | 17 +- .../menus/lightning/lightningMenuItems.xml | 5 + .../lightning/nldnLightningBundleItems.xml | 17 +- .../viz/lightning/LightningResource.java | 25 +- .../DR3213/renameLightningStrikeCount.py | 27 ++ .../META-INF/MANIFEST.MF | 1 + .../res/spring/binlightning_ep-ingest.xml | 65 ++-- .../binlightning/BinLightningDecoder.java | 248 ++++++------ .../binlightning/dao/BinLightningDao.java | 50 ++- .../impl/BaseLightningDecoder.java | 25 +- .../impl/FlashLightningDecoder.java | 29 +- .../impl/IBinLightningDecoder.java | 10 +- .../binlightning/impl/RTLightningDecoder.java | 12 +- .../total/ChecksumByteBuffer.java | 171 +++++++++ .../total/TotalLightningDecoder.java | 348 +++++++++++++++++ .../base/distribution/binlightning.xml | 2 +- .../textlightning/TextLightningDecoder.java | 40 +- .../impl/TextLightningParser.java | 13 +- .../binlightning/BinLightningRecord.java | 361 ++++++++++-------- .../binlightning/LightningConstants.java | 66 ++++ .../binlightning/impl/BaseLightningPoint.java | 259 +++++++++++++ .../impl/LightningPulsePoint.java | 67 ++++ .../impl/LightningStrikePoint.java | 111 +++--- .../binlightning/impl/LtgMsgType.java | 46 ++- .../binlightning/impl/LtgPulseType.java | 71 ++++ .../binlightning/impl/LtgStrikeType.java | 41 +- .../uf/common/monitor/scan/ScanUtils.java | 6 +- .../uf/edex/decodertools/core/BasePoint.java | 174 +-------- .../uf/edex/ohd/pproc/MpeLightningSrv.java | 3 +- .../edex/plugin/scan/process/ScanProduct.java | 7 +- .../rsc/lightning/rsc/LightningResource.java | 30 +- .../binlightning/BinLightningDecoderUtil.java | 41 +- 33 files changed, 1724 insertions(+), 701 deletions(-) create mode 100644 cave/com.raytheon.viz.lightning/localization/menus/lightning/entlnLightningBundleItems.xml create mode 100755 deltaScripts/14.4.1/DR3213/renameLightningStrikeCount.py 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.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/LightningConstants.java create mode 100644 edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/BaseLightningPoint.java create mode 100644 edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LightningPulsePoint.java create mode 100644 edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgPulseType.java diff --git a/cave/com.raytheon.viz.lightning/localization/menus/lightning/entlnLightningBundleItems.xml b/cave/com.raytheon.viz.lightning/localization/menus/lightning/entlnLightningBundleItems.xml new file mode 100644 index 0000000000..ef77211bd9 --- /dev/null +++ b/cave/com.raytheon.viz.lightning/localization/menus/lightning/entlnLightningBundleItems.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cave/com.raytheon.viz.lightning/localization/menus/lightning/gldLightningBundleItems.xml b/cave/com.raytheon.viz.lightning/localization/menus/lightning/gldLightningBundleItems.xml index 6249b1564a..022a151e0e 100644 --- a/cave/com.raytheon.viz.lightning/localization/menus/lightning/gldLightningBundleItems.xml +++ b/cave/com.raytheon.viz.lightning/localization/menus/lightning/gldLightningBundleItems.xml @@ -20,27 +20,18 @@ --> - /binlightning/% + menuText="1hr CG stroke plot" id="1HrLightningStrokePlot"> - /binlightning/% + menuText="15min CG stroke plot" id="15MinLightningStrokePlot"> - /binlightning/% + menuText="15min Pos/Neg CG stroke plot" id="15MinPNLightningStrokePlot"> - /binlightning/% + menuText="5min CG stroke plot" id="5MinLightningStrokePlot"> - /binlightning/% \ No newline at end of file diff --git a/cave/com.raytheon.viz.lightning/localization/menus/lightning/lightningMenuItems.xml b/cave/com.raytheon.viz.lightning/localization/menus/lightning/lightningMenuItems.xml index 9b035bb497..7b8e8c08f1 100644 --- a/cave/com.raytheon.viz.lightning/localization/menus/lightning/lightningMenuItems.xml +++ b/cave/com.raytheon.viz.lightning/localization/menus/lightning/lightningMenuItems.xml @@ -29,4 +29,9 @@ + + + + + \ No newline at end of file diff --git a/cave/com.raytheon.viz.lightning/localization/menus/lightning/nldnLightningBundleItems.xml b/cave/com.raytheon.viz.lightning/localization/menus/lightning/nldnLightningBundleItems.xml index 3f426bf0a3..ef77211bd9 100644 --- a/cave/com.raytheon.viz.lightning/localization/menus/lightning/nldnLightningBundleItems.xml +++ b/cave/com.raytheon.viz.lightning/localization/menus/lightning/nldnLightningBundleItems.xml @@ -20,27 +20,18 @@ --> - /binlightning/% + menuText="1hr CG flash plot" id="1HrLightningFlashPlot"> - /binlightning/% + menuText="15min CG flash plot" id="15MinLightningFlashPlot"> - /binlightning/% + menuText="15min Pos/Neg CG flash plot" id="15MinPNLightningFlashPlot"> - /binlightning/% + menuText="5min CG flash plot" id="5MinLightningFlashPlot"> - /binlightning/% \ No newline at end of file diff --git a/cave/com.raytheon.viz.lightning/src/com/raytheon/viz/lightning/LightningResource.java b/cave/com.raytheon.viz.lightning/src/com/raytheon/viz/lightning/LightningResource.java index e87bf037c8..5bdf334bb1 100644 --- a/cave/com.raytheon.viz.lightning/src/com/raytheon/viz/lightning/LightningResource.java +++ b/cave/com.raytheon.viz.lightning/src/com/raytheon/viz/lightning/LightningResource.java @@ -35,6 +35,7 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.raytheon.uf.common.dataplugin.HDF5Util; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord; +import com.raytheon.uf.common.dataplugin.binlightning.LightningConstants; import com.raytheon.uf.common.datastorage.DataStoreFactory; import com.raytheon.uf.common.datastorage.IDataStore; import com.raytheon.uf.common.datastorage.Request; @@ -94,6 +95,7 @@ import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability; * fields when magnification set to 0 * Feb 27, 2013 DCS 152 jgerth/elau Support for WWLLN and multiple sources * Jan 21, 2014 2667 bclement renamed record's lightSource field to source + * Jun 05, 2014 3226 bclement reference datarecords by LightningConstants * * * @@ -637,7 +639,15 @@ public class LightningResource extends recordList.add(rec); } - List times = recordMap.get("obsTime"); + List times = recordMap + .get(LightningConstants.TIME_DATASET); + + List intensities = recordMap + .get(LightningConstants.INTENSITY_DATASET); + List lats = recordMap + .get(LightningConstants.LAT_DATASET); + List lons = recordMap + .get(LightningConstants.LON_DATASET); int k = 0; for (IDataRecord timeRec : times) { @@ -647,12 +657,13 @@ public class LightningResource extends int numRecords = (int) time.getSizes()[0]; long[] timeData = time.getLongData(); - int[] intensityData = ((IntegerDataRecord) recordMap.get( - "intensity").get(k)).getIntData(); - float[] latitudeData = ((FloatDataRecord) recordMap.get( - "latitude").get(k)).getFloatData(); - float[] longitudeData = ((FloatDataRecord) recordMap.get( - "longitude").get(k)).getFloatData(); + + int[] intensityData = ((IntegerDataRecord) intensities + .get(k)).getIntData(); + float[] latitudeData = ((FloatDataRecord) lats.get(k)) + .getFloatData(); + float[] longitudeData = ((FloatDataRecord) lons.get(k)) + .getFloatData(); for (int i = 0; i < numRecords; i++) { diff --git a/deltaScripts/14.4.1/DR3213/renameLightningStrikeCount.py b/deltaScripts/14.4.1/DR3213/renameLightningStrikeCount.py new file mode 100755 index 0000000000..4106ef2397 --- /dev/null +++ b/deltaScripts/14.4.1/DR3213/renameLightningStrikeCount.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +import h5py +import os +import sys + +# multiplicity was incorrectly interpreted as 'stike count' when +# it was the number of strokes (AKA pulses) in the strike (AKA flash) + +LIGHTNING_H5_PATH = '/awips2/edex/data/hdf5/binlightning' +OLD_NAME = 'strikeCount' +NEW_NAME = 'pulseCount' + +for file in os.listdir(LIGHTNING_H5_PATH): + if file.endswith('h5'): + h5file = None + try: + fileName = os.path.join(LIGHTNING_H5_PATH, file) + h5file = h5py.File(fileName, 'r+') + for g in h5file.values(): + if NEW_NAME not in g and OLD_NAME in g: + g[NEW_NAME] = g[OLD_NAME] + except Exception, e: + print "Error renaming strikeCount in file", fileName, ":", e + finally: + if h5file: + h5file.close() diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF index cc77f6f3b2..0929fedcd6 100644 --- a/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF @@ -9,6 +9,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.numeric, com.raytheon.uf.common.status, com.raytheon.uf.common.wmo, gov.noaa.nws.ost.edex.plugin.binlightning, 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 index d8a0ea3c5f..34ca50824d 100644 --- 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 @@ -5,6 +5,8 @@ + + @@ -16,42 +18,37 @@ - - + - - - - binlightning - - - - - - + + + + binlightning + + + + + + + ${in.header.header} regex '^SFPA42 KWBC.*' + + + + + + - - - java.lang.Throwable - - - + + + + + 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 index b291bddb3c..fe33639ffa 100644 --- 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 @@ -47,8 +47,6 @@ 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; @@ -58,17 +56,6 @@ import com.raytheon.uf.edex.decodertools.core.IBinDataSource; /** * AWIPS decoder adapter strategy for binary lightning data.
* - * Normal usage for this adapter is
- * - * BinLightningDecoder dec = new BinLightningDecoder(); - * dec.setMessage(data); - * while(dec.hasNext()) - * { - * BinLightningRecord r = dec.decode(); - * // do something with record. - * } - * dec.dispose(); - * * *
  * 
@@ -98,6 +85,7 @@ import com.raytheon.uf.edex.decodertools.core.IBinDataSource;
  * 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()
  * 
  * 
* @@ -118,7 +106,7 @@ public class BinLightningDecoder extends AbstractDecoder { * 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.STRIKE_CG; + public LtgStrikeType DEFAULT_FLASH_TYPE = LtgStrikeType.CLOUD_TO_GROUND; private String traceId = null; @@ -153,30 +141,37 @@ public class BinLightningDecoder extends AbstractDecoder { 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???) + /* + * 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 = 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()); - } + /* + * 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 encryted data - // - // Preserved the legacy decoding in BinLigntningDecoderUtil.decodeBitShiftedBinLightningData(), and added logic to process - // both encrypted data and legacy data - // + /* + * Modified by Wufeng Zhou to handle both legacy bit-shifted and + * new encryted data + * + * Preserved the legacy decoding in + * BinLigntningDecoderUtil.decodeBitShiftedBinLightningData(), + * and added logic to process both encrypted data and legacy + * data + */ List strikes = decodeBinLightningData( data, pdata, traceId, wmoHdr, baseTime.getTime()); @@ -186,18 +181,14 @@ public class BinLightningDecoder extends AbstractDecoder { return reports; } - // - // Done MOD by Wufeng Zhou - // + /* + * Done MOD by Wufeng Zhou + */ // post processing data - if not keep-alive record BinLightningRecord report = null; if (strikes.size() > 0) { - report = new BinLightningRecord(strikes.size()); - for (LightningStrikePoint strike : strikes) { - report.addStrike(strike); - logger.debug(traceId + "-" + strike); - } + report = new BinLightningRecord(strikes); } else { return new PluginDataObject[0]; } @@ -215,18 +206,8 @@ public class BinLightningDecoder extends AbstractDecoder { + " at " + SDF.format(cStart.getTime())); } } else { - Calendar cStop = report.getStopTime(); - - TimeRange range = new TimeRange(cStart.getTimeInMillis(), - cStop.getTimeInMillis()); - - DataTime dataTime = new DataTime(cStart, range); - report.setDataTime(dataTime); - - if (report != null) { - report.setTraceId(traceId); - reports = new PluginDataObject[] { report }; - } + report.setTraceId(traceId); + reports = new PluginDataObject[] { report }; } } } else { @@ -235,6 +216,23 @@ public class BinLightningDecoder extends AbstractDecoder { return reports; } + /** + * 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 @@ -281,50 +279,52 @@ public class BinLightningDecoder extends AbstractDecoder { 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. + /* + * 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 @@ -343,21 +343,23 @@ public class BinLightningDecoder extends AbstractDecoder { if (needDecrypt) { try { - // NOTE: 11/14/2013 WZ: - // encrypted test data on TNCF (got from Melissa Porricelli) - // seems to have extra 4 bytes (0x0d 0x0d 0x0a 0x03) at the end, - // making the data size not a multiple of 16. However, original - // test data do not have this trailing bytes. while NCEP test - // data has extra 8 trailing bytes. - // Brain Rapp's email on 11/13/2013 confirms that Unidata LDM - // software used by AWIPS II will strips off all SBN protocol - // headers - // that precede the WMO header and adds its own 11 byte header - // like this: "soh cr cr nl 2 5 4 sp cr cr nl". It - // also adds a four byte trailer consisting of "cr cr nl etx" - // (0x0d 0x0d 0x0a 0x03) - // So, it seems necessary to trim trailing bytes if it is not - // multiple of 16, warning messages will be logged though + /* + * NOTE: 11/14/2013 WZ: + * encrypted test data on TNCF (got from Melissa Porricelli) + * seems to have extra 4 bytes (0x0d 0x0d 0x0a 0x03) at the end, + * making the data size not a multiple of 16. However, original + * test data do not have this trailing bytes. while NCEP test + * data has extra 8 trailing bytes. + * Brain Rapp's email on 11/13/2013 confirms that Unidata LDM + * software used by AWIPS II will strips off all SBN protocol + * headers + * that precede the WMO header and adds its own 11 byte header + * like this: "soh cr cr nl 2 5 4 sp cr cr nl". It + * also adds a four byte trailer consisting of "cr cr nl etx" + * (0x0d 0x0d 0x0a 0x03) + * So, it seems necessary to trim trailing bytes if it is not + * multiple of 16, warning messages will be logged though + */ int dataLengthToBeDecrypted = pdata.length; if (pdata.length % 16 != 0) { dataLengthToBeDecrypted = pdata.length @@ -382,8 +384,10 @@ public class BinLightningDecoder extends AbstractDecoder { decodeDone = true; return null; } - // not keep-alive record, then check data validity and decode - // into an ArrayList of strikes + /* + * not keep-alive record, then check data validity and decode + * into an ArrayList of strikes + */ if (BinLightningDecoderUtil .isLightningDataRecords(decryptedData)) { strikes = BinLightningDecoderUtil @@ -418,9 +422,11 @@ public class BinLightningDecoder extends AbstractDecoder { 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 + /* + * 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); strikes = decodeBitShiftedBinLightningData(pdata, wmoHdr); @@ -461,13 +467,15 @@ public class BinLightningDecoder extends AbstractDecoder { switch (decoder.getError()) { case IBinLightningDecoder.NO_ERROR: { for (LightningStrikePoint strike : decoder) { - // 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.) + /* + * 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.) + */ String gld360WMOHeaderString = BinLightningAESKey .getProps().getProperty( "binlightning.gld360WMOHeaderStartString", 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 index 70a9049e4c..e335229465 100644 --- 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 @@ -20,11 +20,17 @@ package com.raytheon.edex.plugin.binlightning.dao; -import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord; +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; @@ -36,6 +42,7 @@ import com.raytheon.uf.edex.database.plugin.PluginDao; * 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 @@ -61,15 +68,38 @@ public class BinLightningDao extends PluginDao { protected IDataStore populateDataStore(IDataStore dataStore, IPersistable obj) throws Exception { BinLightningRecord binLightningRec = (BinLightningRecord) obj; - - for (int i = 0; i < binLightningRec.getDataArrays().length; i++) { - IDataRecord record = DataStoreFactory.createStorageRecord( - binLightningRec.getDataNames()[i], binLightningRec - .getDataURI(), binLightningRec.getDataArrays()[i]); - record.setCorrelationObject(binLightningRec); - dataStore.addDataRecord(record); - } - + 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/impl/BaseLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/BaseLightningDecoder.java index 8672a9bf39..27a6ee8a68 100644 --- a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/BaseLightningDecoder.java +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/BaseLightningDecoder.java @@ -26,7 +26,6 @@ import java.util.List; import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint; import com.raytheon.uf.common.time.util.TimeUtil; -import com.raytheon.uf.edex.decodertools.core.BasePoint; import com.raytheon.uf.edex.decodertools.core.IBinDataSource; /** @@ -42,6 +41,7 @@ import com.raytheon.uf.edex.decodertools.core.IBinDataSource; * 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 @@ -87,13 +87,12 @@ abstract class BaseLightningDecoder implements IBinLightningDecoder /** * 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 BasePoint object with the time fields set to the observation - * time. + * + * @return A Calendar object with the time fields set to the observation + * time. */ - BasePoint parseDate(IBinDataSource msgData) + protected Calendar parseDate(IBinDataSource msgData) { - BasePoint point = new BasePoint(); - //********* Don't reorder these reads!!! int b1 = msgData.getU8(); int b2 = msgData.getU8(); @@ -103,22 +102,18 @@ abstract class BaseLightningDecoder implements IBinLightningDecoder // number of days since BASE_TIME int days = ((word1 & DAYS_MASK) >> DAYS_SHFT); obsTime.add(Calendar.DAY_OF_MONTH, days); - - point.setYear(obsTime.get(Calendar.YEAR)); - //Increment month, Calendar returns 0..11 - point.setMonth(obsTime.get(Calendar.MONTH) + 1); - point.setDay(obsTime.get(Calendar.DAY_OF_MONTH)); int hours = (word1 & HOURS_HI_BIT_MASK) << HOURS_HI_BIT_SHFT; hours += (b2 & HOURS_LO_NYB_MASK) >>> HOURS_LO_NYB_SHFT; - point.setHour(hours); + obsTime.set(Calendar.HOUR, hours); int minutes = (b2 & MIN_P1_MASK) << MIN_P1_SHFT; minutes += (b1 & MIN_P2_MASK) >>> MIN_P2_SHFT; - point.setMinute(minutes); + obsTime.set(Calendar.MINUTE, minutes); - point.setSecond((b1 & SECONDS_MASK)); - return point; + obsTime.set(Calendar.SECOND, (b1 & SECONDS_MASK)); + obsTime.set(Calendar.MILLISECOND, 0); + return obsTime; } /** diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/FlashLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/FlashLightningDecoder.java index 9054a93f02..024e271110 100644 --- a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/FlashLightningDecoder.java +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/FlashLightningDecoder.java @@ -19,9 +19,11 @@ **/ 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.edex.decodertools.core.BasePoint; +import com.raytheon.uf.common.time.util.TimeUtil; import com.raytheon.uf.edex.decodertools.core.IBinDataSource; /** @@ -30,13 +32,14 @@ import com.raytheon.uf.edex.decodertools.core.IBinDataSource; * *
  * 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 */ @@ -62,7 +65,7 @@ public class FlashLightningDecoder extends BaseLightningDecoder { if(msgData.available(TIME_SIZE)) { - BasePoint base = parseDate(msgData); + Calendar baseTime = parseDate(msgData); if(msgData.available(FLASH_MSG_SIZE * count)) { @@ -70,17 +73,23 @@ public class FlashLightningDecoder extends BaseLightningDecoder { double lon = getFlashLon(msgData); double lat = getFlashLat(msgData); - // Create the strike record from the report info and base time information. - LightningStrikePoint strikeData = new LightningStrikePoint(base,lat,lon,LtgMsgType.STRIKE_MSG_FL); - - strikeData.setStrikeStrength(msgData.getS8() * 2.0); + + double strikeStrength = msgData.getS8() * 2.0; // strike count and 1/10s seconds int u8 = msgData.getU8(); - strikeData.setStrikeCount(u8 & 0x0F); - strikeData.setMillis(((u8 & 0xF0) >> 4) * 100); + 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); } } 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 index c4cc6c55ab..09eee9e737 100644 --- 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 @@ -26,22 +26,24 @@ 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 */ @@ -59,7 +61,7 @@ public interface IBinLightningDecoder extends Iterable public static final int OTHER_RPT = 0xD0; public static final int COMM_RPT = 0xD1; - public static final LtgStrikeType DEFAULT_FLASH_TYPE = LtgStrikeType.STRIKE_CG; + public static final LtgStrikeType DEFAULT_FLASH_TYPE = LtgStrikeType.CLOUD_TO_GROUND; /* */ diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/RTLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/RTLightningDecoder.java index cba14edee1..386d0377b9 100644 --- a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/RTLightningDecoder.java +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/impl/RTLightningDecoder.java @@ -19,9 +19,10 @@ **/ 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.edex.decodertools.core.BasePoint; import com.raytheon.uf.edex.decodertools.core.IBinDataSource; /** @@ -37,6 +38,7 @@ import com.raytheon.uf.edex.decodertools.core.IBinDataSource; * 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 @@ -66,7 +68,7 @@ public class RTLightningDecoder extends BaseLightningDecoder { */ private void doDecode(IBinDataSource msgData, int count) { if (msgData.available(TIME_SIZE + (RT_MSG_SIZE * count))) { - BasePoint base = parseDate(msgData); + Calendar baseTime = parseDate(msgData); // for now just consume some data for (int i = 0; i < count; i++) { long part = msgData.getU32(); @@ -79,11 +81,11 @@ public class RTLightningDecoder extends BaseLightningDecoder { double lat = getRTLat(part); int strikeCount = getMult(part); - LightningStrikePoint strikeData = new LightningStrikePoint( - base, lat, lon, LtgMsgType.STRIKE_MSG_RT); + LightningStrikePoint strikeData = new LightningStrikePoint(lat, + lon, baseTime, LtgMsgType.STRIKE_MSG_RT); strikeData.setStrikeStrength(strength); - strikeData.setStrikeCount(strikeCount); + 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 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..eaf4c914f6 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/ChecksumByteBuffer.java @@ -0,0 +1,171 @@ +/** + * 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 3, 2014  3226      bclement     Initial creation
+ * 
+ * 
+ * + * @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); + } + + /** + * 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; + 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..a1899734b4 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java @@ -0,0 +1,348 @@ +/** + * 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.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.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; + +/** + * Decoder for Earth Networks Total Lightning data + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * May 30, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @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"; + + private static final IUFStatusHandler log = UFStatus + .getHandler(TotalLightningDecoder.class); + + /** + * 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(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; + } + + /** + * 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 fileName + * @param pdata + * data after WMO header is removed + * @return + * @throws DecoderException + */ + private PluginDataObject[] decodeInternal(String fileName, byte[] pdata) + throws DecoderException { + List decodeStrikes = decodeStrikes(fileName, + pdata); + BinLightningRecord record = new BinLightningRecord(decodeStrikes); + return new PluginDataObject[] { record }; + } + + + /** + * Extract strike data from raw binary + * + * @param fileName + * @param pdata + * @return + * @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 totalBytes = UnsignedNumbers.ushortToInt(buff.getShort()); + if (totalBytes > (buff.size() - buff.position())) { + log.error("Truncated total lightning packet in file: " + + fileName); + 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); + checkSum(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(); + checkSum(buff, false); + pulses.add(pulse); + } + strike.setPulses(pulses); + checkSum(buff, true); + 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)); + } + + /** + * 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 + * @throws DecoderException + * if check fails + */ + private static void checkSum(ChecksumByteBuffer buff, boolean total) + throws DecoderException { + 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()); + if (mungedSum != expected) { + throw new DecoderException("Checksum failed: expected " + expected + + " got " + mungedSum); + } + if (total) { + buff.resetAllSums(); + } else { + buff.resetPacketSum(); + } + } + + /** + * 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/edex_static/base/distribution/binlightning.xml b/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/edex_static/base/distribution/binlightning.xml index fc4c4a7356..913211e05d 100644 --- a/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/edex_static/base/distribution/binlightning.xml +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/edex_static/base/distribution/binlightning.xml @@ -20,5 +20,5 @@ --> ^SFUS41 KWBC.* - ^SFPA41 KWBC.* + ^SFPA4[12] KWBC.* diff --git a/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/TextLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/TextLightningDecoder.java index 6574715454..6e772c6224 100644 --- a/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/TextLightningDecoder.java +++ b/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/TextLightningDecoder.java @@ -29,11 +29,7 @@ import com.raytheon.edex.plugin.textlightning.impl.TextLightningParser; 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.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.edex.decodertools.time.TimeTools; +import com.raytheon.uf.common.time.util.TimeUtil; /** * Decoder for text lightning data @@ -47,6 +43,7 @@ import com.raytheon.uf.edex.decodertools.time.TimeTools; * Mar 25, 2010 jsanchez Initial creation * Aug 30, 2013 2298 rjpeter Make getPluginName abstract * Feb 12, 2014 2655 njensen Set source + * Jun 05, 2014 3226 bclement LightningStikePoint refactor * * * @@ -57,9 +54,6 @@ import com.raytheon.uf.edex.decodertools.time.TimeTools; public class TextLightningDecoder extends AbstractDecoder implements IBinaryDecoder { - private static final IUFStatusHandler logger = UFStatus - .getHandler(TextLightningDecoder.class); - private String traceId = null; /** @@ -92,37 +86,19 @@ public class TextLightningDecoder extends AbstractDecoder implements BinLightningRecord report = null; if (strikes.size() > 0) { - report = new BinLightningRecord(strikes.size()); - for (LightningStrikePoint strike : strikes) { - report.addStrike(strike); - logger.debug(traceId + "-" + strike); - } + report = new BinLightningRecord(strikes); } else { return new PluginDataObject[0]; } - Calendar c = TimeTools.getSystemCalendar(); - if (c == null) { - throw new DecoderException(traceId + "-Error decoding times"); - } + Calendar c = TimeUtil.newGmtCalendar(); report.setInsertTime(c); - Calendar cStart = report.getStartTime(); - Calendar cStop = report.getStopTime(); + report.setTraceId(traceId); - TimeRange range = new TimeRange(cStart.getTimeInMillis(), - cStop.getTimeInMillis()); - - DataTime dataTime = new DataTime(cStart, range); - report.setDataTime(dataTime); - - if (report != null) { - report.setTraceId(traceId); - - // TODO anyone have any idea what the source should actually be - // named? - report.setSource("text"); - } + // TODO anyone have any idea what the source should actually be + // named? + report.setSource("text"); return new PluginDataObject[] { report }; } diff --git a/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/impl/TextLightningParser.java b/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/impl/TextLightningParser.java index 62e7aa0e5d..cf0bc5aa1e 100644 --- a/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/impl/TextLightningParser.java +++ b/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/impl/TextLightningParser.java @@ -50,6 +50,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * lgtng intensities -999 to * 999 * Feb 12, 2014 2655 njensen Use status handler for logging + * Jun 05, 2014 3226 bclement LightningStikePoint refactor * * * @@ -177,7 +178,7 @@ public class TextLightningParser { Double.parseDouble(latitude), Double.parseDouble(longitude)); strike.setStrikeStrength(Double.parseDouble(strength)); - strike.setStrikeCount(Integer.parseInt(count)); + strike.setPulseCount(Integer.parseInt(count)); strike.setMonth(Integer.parseInt(month)); strike.setDay(Integer.parseInt(day)); strike.setYear(Integer.parseInt(year)); @@ -186,7 +187,7 @@ public class TextLightningParser { strike.setSecond(Integer.parseInt(sec)); strike.setMillis(0); strike.setMsgType(LtgMsgType.STRIKE_MSG_FL); - strike.setType(LtgStrikeType.STRIKE_CG); + strike.setType(LtgStrikeType.CLOUD_TO_GROUND); strike.setLightSource("UNKN"); reports.add(strike); } else { @@ -214,7 +215,7 @@ public class TextLightningParser { Double.parseDouble(latitude), lon); strike.setStrikeStrength(Double .parseDouble(strength)); - strike.setStrikeCount(Integer.parseInt(count)); + strike.setPulseCount(Integer.parseInt(count)); strike.setMonth(Integer.parseInt(month)); strike.setDay(Integer.parseInt(day)); strike.setYear(Integer.parseInt(year) + 2000); @@ -223,7 +224,7 @@ public class TextLightningParser { strike.setSecond(Integer.parseInt(sec)); strike.setMillis(Integer.parseInt(msec) * 10); strike.setMsgType(LtgMsgType.STRIKE_MSG_FL); - strike.setType(LtgStrikeType.STRIKE_CG); + strike.setType(LtgStrikeType.CLOUD_TO_GROUND); strike.setLightSource("UNKN"); reports.add(strike); } else { @@ -250,7 +251,7 @@ public class TextLightningParser { Double.parseDouble(longitude)); strike.setStrikeStrength(Double .parseDouble(strength)); - strike.setStrikeCount(Integer.parseInt(count)); + strike.setPulseCount(Integer.parseInt(count)); strike.setMonth(Integer.parseInt(month)); strike.setDay(Integer.parseInt(day)); strike.setYear(Integer.parseInt(year)); @@ -259,7 +260,7 @@ public class TextLightningParser { strike.setSecond(Integer.parseInt(sec)); strike.setMillis(Integer.parseInt(msec) * 10); strike.setMsgType(LtgMsgType.STRIKE_MSG_FL); - strike.setType(LtgStrikeType.STRIKE_CG); + strike.setType(LtgStrikeType.CLOUD_TO_GROUND); strike.setLightSource(sls); reports.add(strike); } else { diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java index 04fca00046..ead40b953a 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java @@ -19,8 +19,13 @@ **/ package com.raytheon.uf.common.dataplugin.binlightning; +import java.io.FileNotFoundException; import java.util.Calendar; import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; import javax.persistence.Access; import javax.persistence.AccessType; @@ -35,18 +40,17 @@ import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.annotations.DataURI; +import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningPulsePoint; import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint; import com.raytheon.uf.common.dataplugin.persist.IPersistable; import com.raytheon.uf.common.dataplugin.persist.PersistablePluginDataObject; import com.raytheon.uf.common.datastorage.IDataStore; import com.raytheon.uf.common.datastorage.StorageException; -import com.raytheon.uf.common.datastorage.records.ByteDataRecord; -import com.raytheon.uf.common.datastorage.records.FloatDataRecord; import com.raytheon.uf.common.datastorage.records.IDataRecord; -import com.raytheon.uf.common.datastorage.records.IntegerDataRecord; -import com.raytheon.uf.common.datastorage.records.LongDataRecord; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; +import com.raytheon.uf.common.time.DataTime; +import com.raytheon.uf.common.time.TimeRange; import com.raytheon.uf.common.time.util.TimeUtil; /** @@ -82,6 +86,8 @@ import com.raytheon.uf.common.time.util.TimeUtil; * Oct 22, 2013 2361 njensen Removed XML annotations * Jan 21, 2014 2667 bclement renamed record's lightSource field to source * May 14, 2014 2536 bclement removed TimeTools usage + * Jun 05, 2014 3226 bclement moved data arrays into map for easier management + * replaced addStrike() with List constructor, added pulses * * * @@ -106,43 +112,12 @@ public class BinLightningRecord extends PersistablePluginDataObject implements public static final String PLUGIN_NAME = "binlightning"; + // Data store data items @Transient - private long[] obsTimes = null; + private final Map strikeDataArrays = new TreeMap(); @Transient - private float[] latitudes = null; - - @Transient - private float[] longitudes = null; - - @Transient - private int[] intensities = null; - - @Transient - private byte[] msgTypes = null; - - @Transient - private byte[] strikeTypes = null; - - @Transient - private byte[] strikeCounts = null; - - @Transient - private Object[] dataArrays = null; - - // Data store data item names - @Transient - private final String[] dataNames = { "obsTime", "latitude", "longitude", - "intensity", "msgType", "strikeType", "strikeCount", }; - - @Transient - private int insertIndex = 0; - - @Transient - private long startTimeMillis = Long.MAX_VALUE; - - @Transient - private long stopTimeMillis = Long.MIN_VALUE; + private final Map pulseDataArrays = new TreeMap(); // Persisted value - Earliest strike time in the collection. @DataURI(position = 1) @@ -177,63 +152,148 @@ public class BinLightningRecord extends PersistablePluginDataObject implements * * @param uri * The dataURI - * @param tableDef - * The table definition associated with this class */ public BinLightningRecord(String uri) { super(uri); } /** - * Construct an empty lightning record. + * Construct a lightning record from a list of strikes + * + * @param strikes */ - public BinLightningRecord(int arraysSize) { - obsTimes = new long[arraysSize]; - latitudes = new float[arraysSize]; - longitudes = new float[arraysSize]; + public BinLightningRecord(final List strikes) { + final int arraysSize = strikes.size(); + long[] obsTimes = new long[arraysSize]; + float[] latitudes = new float[arraysSize]; + float[] longitudes = new float[arraysSize]; + int[] intensities = new int[arraysSize]; + byte[] msgTypes = new byte[arraysSize]; + byte[] strikeTypes = new byte[arraysSize]; + byte[] pulseCounts = new byte[arraysSize]; + int[] pulseIndexes = new int[arraysSize]; + int[] heights = new int[arraysSize]; + int[] sensorCounts = new int[arraysSize]; - intensities = new int[arraysSize]; - msgTypes = new byte[arraysSize]; - strikeTypes = new byte[arraysSize]; - strikeCounts = new byte[arraysSize]; - dataArrays = new Object[] { obsTimes, latitudes, longitudes, - intensities, msgTypes, strikeTypes, strikeCounts, }; - insertIndex = 0; + strikeDataArrays.put(LightningConstants.TIME_DATASET, obsTimes); + strikeDataArrays.put(LightningConstants.LAT_DATASET, latitudes); + strikeDataArrays.put(LightningConstants.LON_DATASET, longitudes); + strikeDataArrays.put(LightningConstants.INTENSITY_DATASET, intensities); + strikeDataArrays.put(LightningConstants.MSG_TYPE_DATASET, msgTypes); + strikeDataArrays.put(LightningConstants.STRIKE_TYPE_DATASET, + strikeTypes); + strikeDataArrays + .put(LightningConstants.PULSE_COUNT_DATSET, pulseCounts); + strikeDataArrays.put(LightningConstants.PULSE_INDEX_DATASET, + pulseIndexes); + strikeDataArrays.put(LightningConstants.HEIGHT_DATASET, heights); + strikeDataArrays.put(LightningConstants.SENSOR_COUNT_DATASET, + sensorCounts); + + if (arraysSize > 0) { + LightningStrikePoint sample = strikes.get(0); + setDataSource(sample); + } + + long startTimeMillis = Long.MAX_VALUE; + long stopTimeMillis = Long.MIN_VALUE; + + int pulseDataCount = 0; + + final Iterator iter = strikes.iterator(); + for (int i = 0; i < arraysSize; ++i) { + LightningStrikePoint strike = iter.next(); + Calendar c = strike.getTime(); + + long obsTimeMillis = c.getTimeInMillis(); + + startTimeMillis = Math.min(startTimeMillis, obsTimeMillis); + stopTimeMillis = Math.max(stopTimeMillis, obsTimeMillis); + + obsTimes[i] = obsTimeMillis; + latitudes[i] = (float) strike.getLatitude(); + longitudes[i] = (float) strike.getLongitude(); + + intensities[i] = (int) Math.round(strike.getStrikeStrength()); + msgTypes[i] = (byte) strike.getMsgType().getId(); + strikeTypes[i] = (byte) strike.getType().getId(); + /* some types have pulse counts but no pulse data */ + pulseCounts[i] = (byte) strike.getPulseCount(); + heights[i] = (int) Math.round(strike.getElevation()); + sensorCounts[i] = strike.getSensorCount(); + + List pulses = strike.getPulses(); + if (pulses != null && !pulses.isEmpty()) { + pulseIndexes[i] = pulseDataCount; + pulseDataCount += pulses.size(); + if (pulseCounts[i] != pulses.size()) { + pulseCounts[i] = (byte) pulses.size(); + } + } + } + + if (pulseDataCount > 0) { + /* at least one of the strikes had pulse data */ + setPulseData(strikes, pulseDataCount); + } + startTime = TimeUtil.newGmtCalendar(new Date(startTimeMillis)); + stopTime = TimeUtil.newGmtCalendar(new Date(stopTimeMillis)); + + TimeRange range = new TimeRange(startTime, stopTime); + setDataTime(new DataTime(startTime, range)); } - // /** - // * Construct an empty lightning record. - // * - // * @param message - // * Lightning data report. - // */ - // public BinLightningRecord(String message) { - // super(message); - // } - /** - * Track the current persistence time for the data set. + * @param strikes + * @param pulseDataCount + * total number of pulses for all strikes */ - @SuppressWarnings("unused") - private void updatePersistenceTime() { - if ((startTimeMillis != Long.MAX_VALUE) - && (stopTimeMillis != Long.MIN_VALUE)) { - persistTime = (startTimeMillis + stopTimeMillis) / 2; - setPersistenceTime(new Date(persistTime)); - } else { - setPersistenceTime(TimeUtil.newGmtCalendar().getTime()); - persistTime = getInsertTime().getTimeInMillis(); + private void setPulseData(final List strikes, + final int pulseDataCount) { + long[] pulseTimes = new long[pulseDataCount]; + float[] pulseLats = new float[pulseDataCount]; + float[] pulseLons = new float[pulseDataCount]; + int[] pulseIntensities = new int[pulseDataCount]; + byte[] pulseTypes = new byte[pulseDataCount]; + int[] pulseHeights = new int[pulseDataCount]; + int[] pulseSensorCounts = new int[pulseDataCount]; + + pulseDataArrays.put(LightningConstants.TIME_DATASET, pulseTimes); + pulseDataArrays.put(LightningConstants.LAT_DATASET, pulseLats); + pulseDataArrays.put(LightningConstants.LON_DATASET, pulseLons); + pulseDataArrays.put(LightningConstants.INTENSITY_DATASET, + pulseIntensities); + pulseDataArrays.put(LightningConstants.PULSE_TYPE_DATASET, pulseTypes); + pulseDataArrays.put(LightningConstants.HEIGHT_DATASET, pulseHeights); + pulseDataArrays.put(LightningConstants.SENSOR_COUNT_DATASET, + pulseSensorCounts); + + int index = 0; + for (LightningStrikePoint strike : strikes) { + List pulses = strike.getPulses(); + if (pulses != null && !pulses.isEmpty()) { + for (LightningPulsePoint pulse : pulses) { + pulseTimes[index] = pulse.getTime().getTimeInMillis(); + pulseLats[index] = (float) pulse.getLatitude(); + pulseLons[index] = (float) pulse.getLongitude(); + pulseIntensities[index] = (int) Math.round(pulse + .getStrikeStrength()); + pulseTypes[index] = pulse.getType().getId(); + pulseHeights[index] = (int) Math + .round(pulse.getElevation()); + pulseSensorCounts[index] = pulse.getSensorCount(); + ++index; + } + } } } /** - * Add a strike report to the record collection. + * Extract data source from strike * * @param strike - * A strike report to add. */ - public void addStrike(LightningStrikePoint strike) { - // jjg add + private void setDataSource(LightningStrikePoint strike) { if (source == null) { if (strike.getLightSource() == null) { source = "NLDN"; @@ -249,47 +309,6 @@ public class BinLightningRecord extends PersistablePluginDataObject implements source = "UNKN"; } } - // end - - if (insertIndex < obsTimes.length) { - long t1 = startTimeMillis; - - Calendar c = TimeUtil.newGmtCalendar(strike.getYear(), - strike.getMonth(), strike.getDay()); - - c.set(Calendar.HOUR_OF_DAY, strike.getHour()); - c.set(Calendar.MINUTE, strike.getMinute()); - c.set(Calendar.SECOND, strike.getSecond()); - c.set(Calendar.MILLISECOND, strike.getMillis()); - - long obsTimeMillis = c.getTimeInMillis(); - - startTimeMillis = Math.min(startTimeMillis, obsTimeMillis); - stopTimeMillis = Math.max(stopTimeMillis, obsTimeMillis); - - obsTimes[insertIndex] = obsTimeMillis; - latitudes[insertIndex] = (float) strike.getLatitude(); - longitudes[insertIndex] = (float) strike.getLongitude(); - - intensities[insertIndex] = Math.round((float) strike - .getStrikeStrength()); - msgTypes[insertIndex] = (byte) strike.getMsgType().ordinal(); - strikeTypes[insertIndex] = (byte) strike.getType().ordinal(); - strikeCounts[insertIndex] = (byte) strike.getStrikeCount(); - insertIndex++; - // only update the times if they have changed! - if (t1 != startTimeMillis) { - startTime = TimeUtil.newGmtCalendar(new Date(startTimeMillis)); - } - if (t1 != stopTimeMillis) { - stopTime = TimeUtil.newGmtCalendar(new Date(stopTimeMillis)); - } - - // updatePersistenceTime(); - } else { - throw new ArrayIndexOutOfBoundsException(String.format( - "index greater than length [%d]", insertIndex)); - } } /** @@ -349,25 +368,14 @@ public class BinLightningRecord extends PersistablePluginDataObject implements setInsertTime(insert_time); } - public String[] getDataNames() { - return dataNames; - } - - public Object[] getDataArrays() { - return dataArrays; - } - - public void setDataArrays(Object[] dataArrays) { - this.dataArrays = dataArrays; - } - /** * gets the obsTimes * * @return */ public long[] getObsTimes() { - return obsTimes; + Object obj = strikeDataArrays.get(LightningConstants.TIME_DATASET); + return obj != null ? (long[]) obj : new long[0]; } /** @@ -376,7 +384,8 @@ public class BinLightningRecord extends PersistablePluginDataObject implements * @return */ public float[] getLatitudes() { - return latitudes; + Object obj = strikeDataArrays.get(LightningConstants.LAT_DATASET); + return obj != null ? (float[]) obj : new float[0]; } /** @@ -385,7 +394,8 @@ public class BinLightningRecord extends PersistablePluginDataObject implements * @return */ public float[] getLongitudes() { - return longitudes; + Object obj = strikeDataArrays.get(LightningConstants.LON_DATASET); + return obj != null ? (float[]) obj : new float[0]; } /** @@ -394,7 +404,8 @@ public class BinLightningRecord extends PersistablePluginDataObject implements * @return */ public int[] getIntensities() { - return intensities; + Object obj = strikeDataArrays.get(LightningConstants.INTENSITY_DATASET); + return obj != null ? (int[]) obj : new int[0]; } /** @@ -403,7 +414,8 @@ public class BinLightningRecord extends PersistablePluginDataObject implements * @return */ public byte[] getMsgTypes() { - return msgTypes; + Object obj = strikeDataArrays.get(LightningConstants.MSG_TYPE_DATASET); + return obj != null ? (byte[]) obj : new byte[0]; } /** @@ -412,7 +424,9 @@ public class BinLightningRecord extends PersistablePluginDataObject implements * @return */ public byte[] getStrikeTypes() { - return strikeTypes; + Object obj = strikeDataArrays + .get(LightningConstants.STRIKE_TYPE_DATASET); + return obj != null ? (byte[]) obj : new byte[0]; } /** @@ -420,8 +434,10 @@ public class BinLightningRecord extends PersistablePluginDataObject implements * * @return */ - public byte[] getStrikeCounts() { - return strikeCounts; + public byte[] getPulseCounts() { + Object obj = strikeDataArrays + .get(LightningConstants.PULSE_COUNT_DATSET); + return obj != null ? (byte[]) obj : new byte[0]; } /** @@ -449,42 +465,55 @@ public class BinLightningRecord extends PersistablePluginDataObject implements */ public void retrieveFromDataStore(IDataStore dataStore) throws StorageException { + retrieveFromDataStore(dataStore, false); + } + /** + * Sets the data arrays from the store. + * + * @param dataStore + * @param includePulses + * extract pulse data if true + */ + public void retrieveFromDataStore(IDataStore dataStore, + boolean includePulses) throws StorageException { try { - IDataRecord[] dataRec = dataStore.retrieve(getDataURI()); - dataArrays = new Object[dataRec.length]; - for (int i = 0; i < dataRec.length; i++) { - if (dataRec[i].getName().equals("obsTime")) { - obsTimes = ((LongDataRecord) dataRec[i]).getLongData(); - dataArrays[i] = obsTimes; - } - if (dataRec[i].getName().equals("latitude")) { - latitudes = ((FloatDataRecord) dataRec[i]).getFloatData(); - dataArrays[i] = latitudes; - } else if (dataRec[i].getName().equals("longitude")) { - longitudes = ((FloatDataRecord) dataRec[i]).getFloatData(); - dataArrays[i] = longitudes; - } else if (dataRec[i].getName().equals("intensity")) { - intensities = ((IntegerDataRecord) dataRec[i]).getIntData(); - dataArrays[i] = intensities; - } else if (dataRec[i].getName().equals("msgType")) { - msgTypes = ((ByteDataRecord) dataRec[i]).getByteData(); - dataArrays[i] = msgTypes; - } else if (dataRec[i].getName().equals("strikeType")) { - strikeTypes = ((ByteDataRecord) dataRec[i]).getByteData(); - dataArrays[i] = strikeTypes; - } else if (dataRec[i].getName().equals("strikeCount")) { - strikeCounts = ((ByteDataRecord) dataRec[i]).getByteData(); - dataArrays[i] = strikeCounts; + IDataRecord[] dataRecs = dataStore.retrieve(getDataURI()); + for (IDataRecord record : dataRecs) { + strikeDataArrays.put(record.getName(), record.getDataObject()); + } + if (includePulses) { + String pulseGroup = getDataURI() + DataURI.SEPARATOR + + LightningConstants.PULSE_HDF5_GROUP_SUFFIX; + try { + IDataRecord[] pulseRecords = dataStore.retrieve(pulseGroup); + for (IDataRecord record : pulseRecords) { + pulseDataArrays.put(record.getName(), + record.getDataObject()); + } + } catch (Exception e) { + /* FIXME better way to find out if group doesn't exist */ } } - setDataArrays(dataArrays); - - } catch (Exception se) { - se.printStackTrace(); + } catch (FileNotFoundException e) { + throw new StorageException(e.getLocalizedMessage(), null, e); } } + /** + * @return the strikeDataArrays + */ + public Map getStrikeDataArrays() { + return strikeDataArrays; + } + + /** + * @return the pulseDataArrays + */ + public Map getPulseDataArrays() { + return pulseDataArrays; + } + @Override @Column @Access(AccessType.PROPERTY) diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/LightningConstants.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/LightningConstants.java new file mode 100644 index 0000000000..28a1787038 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/LightningConstants.java @@ -0,0 +1,66 @@ +/** + * 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.uf.common.dataplugin.binlightning; + +/** + * Constants used for lightning data such as dataset names in HDF5 + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * May 30, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class LightningConstants { + + public static final String PULSE_HDF5_GROUP_SUFFIX = "pulse"; + + // HDF5 dataset names + + public static final String TIME_DATASET = "obsTime"; + + public static final String LAT_DATASET = "latitude"; + + public static final String LON_DATASET = "longitude"; + + public static final String INTENSITY_DATASET = "intensity"; + + public static final String MSG_TYPE_DATASET = "msgType"; + + public static final String STRIKE_TYPE_DATASET = "strikeType"; + + public static final String PULSE_COUNT_DATSET = "pulseCount"; + + public static final String PULSE_INDEX_DATASET = "pulseIndex"; + + public static final String PULSE_TYPE_DATASET = "pulseType"; + + public static final String HEIGHT_DATASET = "height"; + + public static final String SENSOR_COUNT_DATASET = "sensorCount"; + +} diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/BaseLightningPoint.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/BaseLightningPoint.java new file mode 100644 index 0000000000..f336e392b5 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/BaseLightningPoint.java @@ -0,0 +1,259 @@ +/** + * 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.uf.common.dataplugin.binlightning.impl; + +import java.util.Calendar; + +import com.raytheon.uf.common.time.util.TimeUtil; +import com.vividsolutions.jts.geom.Coordinate; + +/** + * Lightning information common to strikes and pulses + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 3, 2014  3226      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class BaseLightningPoint { + + private final Coordinate point; + + private Calendar time; + + // Lightning strike strength and polarity + private double strikeStrength; + + private int sensorCount; + + /** + * + */ + public BaseLightningPoint() { + this(TimeUtil.newGmtCalendar()); + } + + /** + * @param time + */ + public BaseLightningPoint(Calendar time) { + this(new Coordinate(), time); + } + + /** + * @param point + */ + public BaseLightningPoint(Coordinate point, Calendar time) { + this.point = point; + this.time = time; + } + + /** + * @param latitude + * @param longitude + */ + public BaseLightningPoint(double latitude, double longitude, Calendar time) { + this(new Coordinate(longitude, latitude), time); + } + + /** + * Set the year part of the date. + * + * @param year + * Year. + */ + public void setYear(int year) { + this.time.set(Calendar.YEAR, year); + } + + /** + * Set the day of the month part of the date. + * + * @param day + * Day of month [1..12]. + */ + public void setMonth(int month) { + this.time.set(Calendar.MONTH, month - 1); + } + + /** + * Set the day of the month part of the date. + * + * @param day + * Day of month [1..12]. + */ + public void setDay(int day) { + this.time.set(Calendar.DAY_OF_MONTH, day); + } + + /** + * Set the hour part of the time. + * + * @param hour + * Hour of the day [0..23]. + */ + public void setHour(int hour) { + this.time.set(Calendar.HOUR, hour); + } + + /** + * Set the minute of the hour. + * + * @param minute + * The minute of the hour [0..59] + */ + public void setMinute(int minute) { + this.time.set(Calendar.MINUTE, minute); + } + + /** + * Set the second of the minute. + * + * @param second + * The second of the minute [0..59] + */ + public void setSecond(int second) { + this.time.set(Calendar.SECOND, second); + } + + /** + * Set the milliseconds part of the time. + * + * @param millis + * Milliseconds [0..999]. + */ + public void setMillis(int millis) { + this.time.set(Calendar.MILLISECOND, millis); + } + + /** + * @return the time + */ + public Calendar getTime() { + return time; + } + + /** + * @param time + * the time to set + */ + public void setTime(Calendar time) { + this.time = time; + } + + /** + * Get the strike strength. + * + * @return The strike strength. + */ + public double getStrikeStrength() { + return strikeStrength; + } + + /** + * Set the strike strength. + * + * @return The strike strength. + */ + public void setStrikeStrength(double strikeStrength) { + this.strikeStrength = strikeStrength; + } + + /** + * @return the sensorCount + */ + public int getSensorCount() { + return sensorCount; + } + + /** + * @param sensorCount + * the sensorCount to set + */ + public void setSensorCount(int sensorCount) { + this.sensorCount = sensorCount; + } + + /** + * Get the latitude of this point. + * + * @return The latitude. + */ + public double getLatitude() { + return point.y; + } + + /** + * Set the latitude for this point. + * + * @param latitude + * The latitude. + */ + public void setLatitude(double latitude) { + this.point.y = latitude; + } + + /** + * Get the longitude of this point. + * + * @return The longitude. + */ + public double getLongitude() { + return point.x; + } + + /** + * Set the longitude for this point. + * + * @param longitude + * The longitude. + */ + public void setLongitude(double longitude) { + this.point.x = longitude; + } + + /** + * Get the elevation of this point. No units are specified. + * + * @return The elevation of this point. + */ + public double getElevation() { + return this.point.z; + } + + /** + * Set the elevation of this point. No units are specified. + * + * @param elevation + * The elevation of this point. + */ + public void setElevation(double elevation) { + this.point.z = elevation; + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LightningPulsePoint.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LightningPulsePoint.java new file mode 100644 index 0000000000..60cf9a34fa --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LightningPulsePoint.java @@ -0,0 +1,67 @@ +/** + * 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.uf.common.dataplugin.binlightning.impl; + +import java.util.Calendar; + +/** + * Lightning pulse information + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 3, 2014  3226      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class LightningPulsePoint extends BaseLightningPoint { + + private final LtgPulseType type; + + public LightningPulsePoint(LtgPulseType type) { + super(); + this.type = type; + } + + public LightningPulsePoint(Calendar time, LtgPulseType type) { + super(time); + this.type = type; + } + + public LightningPulsePoint(double latitude, double longitude, + Calendar time, LtgPulseType type) { + super(latitude, longitude, time); + this.type = type; + } + + /** + * @return the type + */ + public LtgPulseType getType() { + return type; + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LightningStrikePoint.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LightningStrikePoint.java index 4d6f6071bb..27ab0445ce 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LightningStrikePoint.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LightningStrikePoint.java @@ -19,53 +19,59 @@ **/ package com.raytheon.uf.common.dataplugin.binlightning.impl; -import com.raytheon.uf.edex.decodertools.core.BasePoint; +import java.util.Calendar; +import java.util.List; + +import com.raytheon.uf.common.time.util.TimeUtil; /** * Record implementation for the Binary Lightning data decoder. * *
- *
+ * 
  * SOFTWARE HISTORY
- *
+ * 
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
  * 20070810            379 jkorman     Initial Coding from prototype.
  * 20130227        DCS 152 jgerth      Support for WWLLN and multiple sources
+ * Jun 3, 2014  3226      bclement     refactor to support pulse data
  * 
  * 
- * + * * @author jkorman * @version 1.0 */ -public class LightningStrikePoint extends BasePoint +public class LightningStrikePoint extends BaseLightningPoint { private LtgStrikeType type; private LtgMsgType msgType; - // Lightning strike strength and polarity - private double strikeStrength; - - // Number of strikes for this record. - private int strikeCount; + // Number of pulses for this record. + private int pulseCount; // JJG - Lightning data source private String lightSource; + + private List pulses; /** * Construct a LightningStrikePoint using given data. - * @param base The base point which should contain a valid time. - * @param latitude The latitude of the strike. - * @param longitude The longitude of the strike. - * @param type The strike message type. + * + * @param latitude + * The latitude of the strike. + * @param longitude + * The longitude of the strike. + * @param time + * @param type + * The strike message type. */ - public LightningStrikePoint(BasePoint base, double latitude, double longitude, LtgMsgType type) + public LightningStrikePoint(double latitude, double longitude, + Calendar time, LtgMsgType type) { - super(base); - setLatitude(latitude); - setLongitude(longitude); + super(latitude, longitude, time); setElevation(0); this.msgType = type; } @@ -77,44 +83,37 @@ public class LightningStrikePoint extends BasePoint */ public LightningStrikePoint(double latitude, double longitude) { - super(latitude,longitude); + super(latitude, longitude, TimeUtil.newGmtCalendar()); setElevation(0); } /** - * Get the strike count. - * @return The strike count. + * @param time + * @param type */ - public int getStrikeCount() - { - return strikeCount; + public LightningStrikePoint(Calendar time, LtgMsgType type) { + super(time); + this.msgType = type; } /** - * Set the strike count. - * @return The strike count. + * Get the pulse count. + * + * @return The pulse count. */ - public void setStrikeCount(int strikeCount) + public int getPulseCount() { - this.strikeCount = strikeCount; + return pulseCount; } /** - * Get the strike strength. - * @return The strike strength. + * Set the pulse count. + * + * @return The pulse count. */ - public double getStrikeStrength() + public void setPulseCount(int pulseCount) { - return strikeStrength; - } - - /** - * Set the strike strength. - * @return The strike strength. - */ - public void setStrikeStrength(double strikeStrength) - { - this.strikeStrength = strikeStrength; + this.pulseCount = pulseCount; } /** @@ -189,13 +188,22 @@ public class LightningStrikePoint extends BasePoint { buffer = new StringBuilder(); } - buffer.append(String.format("%4d%02d%02d",getYear(),getMonth(),getDay())); - buffer.append(String.format("%02d%02d%02d",getHour(),getMinute(),getSecond())); + Calendar obsTime = getTime(); + int year = obsTime.get(Calendar.YEAR); + int month = obsTime.get(Calendar.MONTH) + 1; + int day = obsTime.get(Calendar.DAY_OF_MONTH); + int hour = obsTime.get(Calendar.HOUR); + int minute = obsTime.get(Calendar.MINUTE); + int second = obsTime.get(Calendar.SECOND); + int millis = obsTime.get(Calendar.MILLISECOND); + buffer.append(String.format("%4d%02d%02d", year, month, day)); + buffer.append(String.format("%02d%02d%02d", hour, minute, second)); buffer.append(String.format(" %9.5f %10.5f ",getLatitude(), getLongitude() )); buffer.append(String.format("%2s %2s ",msgType.getType(),type.getType())); - buffer.append(String.format("%4.0f %02d %02d", strikeStrength,(getMillis() / 100),strikeCount)); + buffer.append(String.format("%4.0f %02d %02d", getStrikeStrength(), + (millis / 100), pulseCount)); return buffer; } @@ -213,5 +221,20 @@ public class LightningStrikePoint extends BasePoint { return toString(null).toString(); } + + /** + * @return the pulses + */ + public List getPulses() { + return pulses; + } + + /** + * @param pulses + * the pulses to set + */ + public void setPulses(List pulses) { + this.pulses = pulses; + } } diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgMsgType.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgMsgType.java index 8173dfe783..47015a0229 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgMsgType.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgMsgType.java @@ -23,32 +23,51 @@ package com.raytheon.uf.common.dataplugin.binlightning.impl; * Enums for the lightning message type, Flash or RT Flash. * *
- *
+ * 
  * SOFTWARE HISTORY
- *
+ * 
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
  * 20070810            379 jkorman     Initial Coding from prototype.
+ * Jun 3, 2014  3226      bclement     added id, added TOTAL_LIGHTNING
  * 
  * 
- * + * * @author jkorman * @version 1.0 */ public enum LtgMsgType { - STRIKE_MSG_FL("FL"), - STRIKE_MSG_RT("RT"); + STRIKE_MSG_FL((byte) 0, "FL"), STRIKE_MSG_RT((byte) 1, "RT"), TOTAL_LIGHTNING( + (byte) 2, "TL"); - private final String strikeType; + private final String msgType; + + private final byte id; /** * Construct the type. - * @param type Lightning strike type. + * + * @param type + * Lightning message type. */ - private LtgMsgType(String type) + private LtgMsgType(byte id, String type) { - strikeType = type; + this.msgType = type; + this.id = id; + } + + /** + * @param id + * @return null if no type found for id + */ + public static LtgMsgType getById(byte id) { + for (LtgMsgType type : LtgMsgType.values()) { + if (type.id == id) { + return type; + } + } + return null; } /** @@ -57,6 +76,13 @@ public enum LtgMsgType */ public String getType() { - return strikeType; + return msgType; + } + + /** + * @return unique id for type + */ + public byte getId() { + return this.id; } } diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgPulseType.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgPulseType.java new file mode 100644 index 0000000000..2591bd5983 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgPulseType.java @@ -0,0 +1,71 @@ +/** + * 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.uf.common.dataplugin.binlightning.impl; + +/** + * Stroke type enum for lightning pulses. Mainly categorizes between return and + * non-return strokes. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 3, 2014  3226      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public enum LtgPulseType { + + RETURN_STROKE((byte) 0), NON_RETURN_STROKE((byte) 1), KEEP_ALIVE((byte) 9); + + private final byte id; + + /** + * @param id + */ + private LtgPulseType(byte id) { + this.id = id; + } + + /** + * @param id + * @return null if no type is found for id + */ + public static LtgPulseType getById(byte id) { + for (LtgPulseType type : LtgPulseType.values()) { + if (type.id == id) { + return type; + } + } + return null; + } + + /** + * @return unique id for this type + */ + public byte getId() { + return this.id; + } +} diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgStrikeType.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgStrikeType.java index aa2c50765b..b217ca1953 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgStrikeType.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/impl/LtgStrikeType.java @@ -23,34 +23,52 @@ package com.raytheon.uf.common.dataplugin.binlightning.impl; * Enums for the lightning strike type, cloud-to-cloud or cloud-to-ground. * *
- *
+ * 
  * SOFTWARE HISTORY
- *
+ * 
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
  * 20070810            379 jkorman     Initial Coding from prototype.
  * 20130425        DCS 112 Wufeng Zhou Added STRIKE_TF (for Total Flash) in definition for Total Flash 
+ * Jun 3, 2014  3226       bclement    added id, changed to more descriptive names, added KEEP_ALIVE
  * 
  * 
- * + * * @author jkorman * @version 1.0 */ public enum LtgStrikeType { - STRIKE_CC("CC"), - STRIKE_CG("CG"), - STRIKE_TF("TF"); + CLOUD_TO_CLOUD((byte)0, "CC"), CLOUD_TO_GROUND((byte)1, "CG"), TOTAL_FLASH((byte)2, "TF"), KEEP_ALIVE((byte)9, + ""); private final String strikeType; + + private final byte id; /** * Construct the type. + * @param id unique id for type * @param type Lightning strike type. */ - private LtgStrikeType(String type) + private LtgStrikeType(byte id, String type) { - strikeType = type; + this.id = id; + this.strikeType = type; + } + + /** + * @param id + * unique id for type + * @return null if no type is found for id + */ + public static LtgStrikeType getById(byte id) { + for (LtgStrikeType type : LtgStrikeType.values()) { + if (type.id == id) { + return type; + } + } + return null; } /** @@ -61,4 +79,11 @@ public enum LtgStrikeType { return strikeType; } + + /** + * @return id + */ + public int getId() { + return this.id; + } } diff --git a/edexOsgi/com.raytheon.uf.common.monitor/src/com/raytheon/uf/common/monitor/scan/ScanUtils.java b/edexOsgi/com.raytheon.uf.common.monitor/src/com/raytheon/uf/common/monitor/scan/ScanUtils.java index 649cc1f4f5..9c98131123 100644 --- a/edexOsgi/com.raytheon.uf.common.monitor/src/com/raytheon/uf/common/monitor/scan/ScanUtils.java +++ b/edexOsgi/com.raytheon.uf.common.monitor/src/com/raytheon/uf/common/monitor/scan/ScanUtils.java @@ -76,6 +76,8 @@ import com.vividsolutions.jts.io.WKTWriter; * 12/20/2013 DR 16894 gzhang Fixed getZRvalue2() bias issue. * 05/12/2014 3133 njensen Extracted getLightningRecord * 05/13/2014 3133 njensen Moved convertStrankValue here from ScanConfig + * Jun 05, 2014 3226 bclement BinLightning refactor + * compare lightning strike type by id instead of ordinal * * * @author dhladky @@ -1292,7 +1294,7 @@ public class ScanUtils { (double) rec.getLatitudes()[i], (double) rec.getLongitudes()[i], rec.getIntensities()[i], rec.getStrikeTypes()[i], - rec.getMsgTypes()[i], rec.getStrikeCounts()[i]); + rec.getMsgTypes()[i], rec.getPulseCounts()[i]); strikeList.add(strike); } @@ -1311,7 +1313,7 @@ public class ScanUtils { if (ls.getIntensity() > 0) { totalPosStrikes++; } - if (ls.getStrikeType() == LtgStrikeType.STRIKE_CG.ordinal()) { + if (ls.getStrikeType() == LtgStrikeType.CLOUD_TO_GROUND.getId()) { totalCGStrikes++; } totalStrikes++; diff --git a/edexOsgi/com.raytheon.uf.edex.decodertools/src/com/raytheon/uf/edex/decodertools/core/BasePoint.java b/edexOsgi/com.raytheon.uf.edex.decodertools/src/com/raytheon/uf/edex/decodertools/core/BasePoint.java index 8d3a361a84..0f283a451c 100644 --- a/edexOsgi/com.raytheon.uf.edex.decodertools/src/com/raytheon/uf/edex/decodertools/core/BasePoint.java +++ b/edexOsgi/com.raytheon.uf.edex.decodertools/src/com/raytheon/uf/edex/decodertools/core/BasePoint.java @@ -19,12 +19,14 @@ **/ package com.raytheon.uf.edex.decodertools.core; +import com.vividsolutions.jts.geom.Coordinate; + + /** - * BasePoint provides a simple 4D (latitude, longitude, elevation, and time) - * base class for observation data. Sub-classes of BasePoint are responsible for - * adding additional checking or using specific units as required. An example - * would be adding a accessors for a Calendar to expose the date information. - * Most attributes of this class do not have units. + * BasePoint provides a simple 3D (latitude, longitude, elevation) base class + * for observation data. Sub-classes of BasePoint are responsible for adding + * additional checking or using specific units as required. Most attributes of + * this class do not have units. * *
  * 
@@ -35,11 +37,14 @@ package com.raytheon.uf.edex.decodertools.core;
  * 27 July 2007        411 jkorman     Initial Development
  * 20070911            379 jkorman     Added comments.
  * 20070912            379 jkorman     Code review cleanup.
+ * Jun 05, 2014 3226       bclement    deprecated class, removed time
  * 
* + * @deprecated use {@link Coordinate} instead * @author jkorman * @version 1 */ +@Deprecated public class BasePoint { private double latitude; @@ -47,20 +52,6 @@ public class BasePoint { private double elevation; - private int year; - - private int month; - - private int day; - - private int hour; - - private int minute; - - private int second; - - private int millis; - /** * Create an empty basepoint instance. */ @@ -74,16 +65,9 @@ public class BasePoint { * A basepoint to copy. */ public BasePoint(BasePoint point) { - latitude = point.latitude; - longitude = point.longitude; - elevation = point.elevation; - year = point.year; - month = point.month; - day = point.day; - hour = point.hour; - minute = point.minute; - second = point.second; - millis = point.millis; + this.latitude = point.latitude; + this.longitude = point.longitude; + this.elevation = point.elevation; } /** @@ -154,136 +138,4 @@ public class BasePoint { this.elevation = elevation; } - /** - * Get the year part of the date. - * - * @return The year. - */ - public int getYear() { - return year; - } - - /** - * Set the year part of the date. - * - * @param year - * Year. - */ - public void setYear(int year) { - this.year = year; - } - - /** - * Get the month part of the date. - * - * @return The month of the year. - */ - public int getMonth() { - return month; - } - - /** - * Set the day of the month part of the date. - * - * @param day - * Day of month [1..12]. - */ - public void setMonth(int month) { - this.month = month; - } - - /** - * Get the day part of the month. - * - * @return The day of the month. - */ - public int getDay() { - return day; - } - - /** - * Set the day of the month part of the date. - * - * @param day - * Day of month [1..12]. - */ - public void setDay(int day) { - this.day = day; - } - - /** - * Get the hours part of the day. - * - * @return The hours part of the day. - */ - public int getHour() { - return hour; - } - - /** - * Set the hour part of the time. - * - * @param hour - * Hour of the day [0..23]. - */ - public void setHour(int hour) { - this.hour = hour; - } - - /** - * Get the minutes part of the hour. - * - * @return The minutes part of the hour. - */ - public int getMinute() { - return minute; - } - - /** - * Set the minute of the hour. - * - * @param minute - * The minute of the hour [0..59] - */ - public void setMinute(int minute) { - this.minute = minute; - } - - /** - * Get the seconds part of the minute. - * - * @return The seconds part of the minute. - */ - public int getSecond() { - return second; - } - - /** - * Set the second of the minute. - * - * @param second - * The second of the minute [0..59] - */ - public void setSecond(int second) { - this.second = second; - } - - /** - * Get the milliseconds part of the time. - * - * @return The milliseconds part of the time. - */ - public int getMillis() { - return millis; - } - - /** - * Set the milliseconds part of the time. - * - * @param millis - * Milliseconds [0..999]. - */ - public void setMillis(int millis) { - this.millis = millis; - } } diff --git a/edexOsgi/com.raytheon.uf.edex.ohd/src/com/raytheon/uf/edex/ohd/pproc/MpeLightningSrv.java b/edexOsgi/com.raytheon.uf.edex.ohd/src/com/raytheon/uf/edex/ohd/pproc/MpeLightningSrv.java index a95c4619d9..0d891b3b2d 100644 --- a/edexOsgi/com.raytheon.uf.edex.ohd/src/com/raytheon/uf/edex/ohd/pproc/MpeLightningSrv.java +++ b/edexOsgi/com.raytheon.uf.edex.ohd/src/com/raytheon/uf/edex/ohd/pproc/MpeLightningSrv.java @@ -56,6 +56,7 @@ import com.vividsolutions.jts.geom.Coordinate; * Jan 10, 2013 1448 bgonzale Added app context check in runOnSchedule(). * Jan 18, 2013 1469 bkowal Removed the hdf5 data directory. * Mar 28, 2014 2952 mpduff Changed to use UFStatus for logging. + * Jun 05, 2014 3226 bclement BinLightning refactor * * * @@ -138,7 +139,7 @@ public class MpeLightningSrv { float[] latitudes = ltngRec.getLatitudes(); float[] longitudes = ltngRec.getLongitudes(); long[] obstimes = ltngRec.getObsTimes(); - byte[] strikes = ltngRec.getStrikeCounts(); + byte[] strikes = ltngRec.getPulseCounts(); // convert latitude and longitude to grid coordinate HRAP hrap = HRAP.getInstance(); diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.scan/src/com/raytheon/uf/edex/plugin/scan/process/ScanProduct.java b/edexOsgi/com.raytheon.uf.edex.plugin.scan/src/com/raytheon/uf/edex/plugin/scan/process/ScanProduct.java index 9a2fde5bb6..883c59aead 100644 --- a/edexOsgi/com.raytheon.uf.edex.plugin.scan/src/com/raytheon/uf/edex/plugin/scan/process/ScanProduct.java +++ b/edexOsgi/com.raytheon.uf.edex.plugin.scan/src/com/raytheon/uf/edex/plugin/scan/process/ScanProduct.java @@ -58,6 +58,7 @@ import com.vividsolutions.jts.geom.GeometryFactory; * ------------ ---------- ----------- -------------------------- * 05/07/2009 2037 dhladky Initial Creation. * Apr 30, 2014 2060 njensen Updates for removal of grid dataURI column + * Jun 05, 2014 3226 bclement compare lightning strike type by id instead of ordinal * * * @@ -292,7 +293,7 @@ public abstract class ScanProduct implements Serializable { rec.getIntensities()[i], rec.getStrikeTypes()[i], rec.getMsgTypes()[i], - rec.getStrikeCounts()[i]); + rec.getPulseCounts()[i]); strikeList.add(strike); } @@ -310,8 +311,8 @@ public abstract class ScanProduct implements Serializable { if (ls.getIntensity() > 0) { totalPosStrikes++; } - if (ls.getStrikeType() == LtgStrikeType.STRIKE_CG - .ordinal()) { + if (ls.getStrikeType() == LtgStrikeType.CLOUD_TO_GROUND + .getId()) { totalCGStrikes++; } totalStrikes++; diff --git a/ncep/gov.noaa.nws.ncep.viz.rsc.lightning/src/gov/noaa/nws/ncep/viz/rsc/lightning/rsc/LightningResource.java b/ncep/gov.noaa.nws.ncep.viz.rsc.lightning/src/gov/noaa/nws/ncep/viz/rsc/lightning/rsc/LightningResource.java index 750734a74d..dd7fb62d8e 100644 --- a/ncep/gov.noaa.nws.ncep.viz.rsc.lightning/src/gov/noaa/nws/ncep/viz/rsc/lightning/rsc/LightningResource.java +++ b/ncep/gov.noaa.nws.ncep.viz.rsc.lightning/src/gov/noaa/nws/ncep/viz/rsc/lightning/rsc/LightningResource.java @@ -3,8 +3,8 @@ package gov.noaa.nws.ncep.viz.rsc.lightning.rsc; import gov.noaa.nws.ncep.viz.common.ui.NmapCommon; import gov.noaa.nws.ncep.viz.resources.AbstractNatlCntrsResource; import gov.noaa.nws.ncep.viz.resources.INatlCntrsResource; -import gov.noaa.nws.ncep.viz.resources.colorBar.ColorBarResourceData; import gov.noaa.nws.ncep.viz.resources.colorBar.ColorBarResource; +import gov.noaa.nws.ncep.viz.resources.colorBar.ColorBarResourceData; import gov.noaa.nws.ncep.viz.ui.display.ColorBar; import gov.noaa.nws.ncep.viz.ui.display.NCMapDescriptor; @@ -18,6 +18,7 @@ import java.util.List; import com.raytheon.uf.common.dataplugin.HDF5Util; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord; +import com.raytheon.uf.common.dataplugin.binlightning.LightningConstants; import com.raytheon.uf.common.dataquery.requests.RequestConstraint; import com.raytheon.uf.common.datastorage.DataStoreFactory; import com.raytheon.uf.common.datastorage.IDataStore; @@ -42,15 +43,14 @@ import com.raytheon.uf.viz.core.drawables.IWireframeShape; import com.raytheon.uf.viz.core.drawables.PaintProperties; import com.raytheon.uf.viz.core.drawables.ResourcePair; import com.raytheon.uf.viz.core.exception.VizException; -import com.raytheon.uf.viz.core.map.MapDescriptor; import com.raytheon.uf.viz.core.rsc.LoadProperties; import com.raytheon.uf.viz.core.rsc.ResourceProperties; import com.raytheon.uf.viz.core.rsc.ResourceType; /** - * LigntningResource - Display Lightning data. - * * + * LigntningResource - Display Lightning data. * + * *
  * SOFTWARE HISTORY
  * Date         Ticket#    Engineer    Description
@@ -65,10 +65,11 @@ import com.raytheon.uf.viz.core.rsc.ResourceType;
  *  05/23/12      785      Q. Zhou      Added getName for legend.
  *  12/19/2012    #960     Greg Hull    override propertiesChanged() to update colorBar.
  *  05/07/2013    #993     Greg Hull	change key for strikeMap from URI to the HDF5 group
- *  
+ *  Jun 05, 2014  3226     bclement     reference datarecords by LightningConstants
+ * 
  * 
* - * @author ghull + * @author ghull * @version 1.0 */ public class LightningResource extends AbstractNatlCntrsResource @@ -366,20 +367,17 @@ public class LightningResource extends AbstractNatlCntrsResource latestStrike ) { -// latestStrike = strikeInfo.obsTime; -// } -// } else if( hdf5Rec.getName().equals("strikeCount") ) { -// strikeInfo.lon = ((FloatDataRecord) hdf5Rec).getFloatData()[s]; - } else if( hdf5Rec.getName().equals("msgType") ) { + } else if (name + .equals(LightningConstants.MSG_TYPE_DATASET)) { // ignore } } diff --git a/ost/gov.noaa.nws.ost.edex.plugin.binlightning/src/gov/noaa/nws/ost/edex/plugin/binlightning/BinLightningDecoderUtil.java b/ost/gov.noaa.nws.ost.edex.plugin.binlightning/src/gov/noaa/nws/ost/edex/plugin/binlightning/BinLightningDecoderUtil.java index 200b80e5bb..ceb2651f70 100644 --- a/ost/gov.noaa.nws.ost.edex.plugin.binlightning/src/gov/noaa/nws/ost/edex/plugin/binlightning/BinLightningDecoderUtil.java +++ b/ost/gov.noaa.nws.ost.edex.plugin.binlightning/src/gov/noaa/nws/ost/edex/plugin/binlightning/BinLightningDecoderUtil.java @@ -16,7 +16,6 @@ import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgMsgType; 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.edex.decodertools.core.BasePoint; /** * BinLigntningDecoderUtil @@ -38,6 +37,7 @@ import com.raytheon.uf.edex.decodertools.core.BasePoint; * moved from com.raytheon.edex.plugin.binlightning to gov.noaa.nws.ost.edex.plugin.binlightning * moved decodeBinLightningData() and decodeBitShiftedBinLightningData() * to BinLightningDecoder to solve circular dependency + * Jun 05, 2014 3226 bclement LightningStrikePoint refactor * * * @author Wufeng Zhou @@ -132,20 +132,16 @@ public class BinLightningDecoderUtil { // int reserved = buffer.getShort() & 0xffff; // Create the strike record from the report info and base time information. - BasePoint base = new BasePoint(lat, lon); Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(epochTime); - base.setYear(cal.get(Calendar.YEAR)); - base.setMonth(cal.get(Calendar.MONTH) + 1); - base.setDay(cal.get(Calendar.DAY_OF_MONTH)); - base.setHour(cal.get(Calendar.HOUR_OF_DAY)); - base.setMinute(cal.get(Calendar.MINUTE)); - base.setSecond(cal.get(Calendar.SECOND)); - base.setMillis(cal.get(Calendar.MILLISECOND)); - // new spec does not seem to have lightning message type indicator such as FL (Flash Lightning) or RT (Real Time flash lightning) - // The source of lightning data in the vendor specific data bytes (byte 16-17) may related to this (???), - // and it is used here for now. 04/182/013 Wufeng Zhou + /* + * new spec does not seem to have lightning message type indicator + * such as FL (Flash Lightning) or RT (Real Time flash lightning) + * The source of lightning data in the vendor specific data bytes + * (byte 16-17) may related to this (???), + * and it is used here for now. 04/182/013 Wufeng Zhou + */ /** 05/02/2013, found DSI-9603 Spec (http://www1.ncdc.noaa.gov/pub/data/documentlibrary/tddoc/td9603.pdf) on NLDN lightning data format, * on Message Type and Stroke Type: * POS: 37-38 Message Type @@ -165,19 +161,24 @@ public class BinLightningDecoderUtil { msgType = LtgMsgType.STRIKE_MSG_RT; } - LightningStrikePoint lsp = new LightningStrikePoint(base, lat, lon, msgType); - LtgStrikeType ltgStrikeType = LtgStrikeType.STRIKE_CG; // default ?? + LightningStrikePoint lsp = new LightningStrikePoint(lat, lon, cal, + msgType); + LtgStrikeType ltgStrikeType = LtgStrikeType.CLOUD_TO_GROUND; // default ?? if (strokeType == 0x0000) { - ltgStrikeType = LtgStrikeType.STRIKE_CG; + ltgStrikeType = LtgStrikeType.CLOUD_TO_GROUND; } else if (strokeType == 0x00ff) { - ltgStrikeType = LtgStrikeType.STRIKE_CC; + ltgStrikeType = LtgStrikeType.CLOUD_TO_CLOUD; } else if (strokeType == 0xffff) { - ltgStrikeType = LtgStrikeType.STRIKE_TF; + ltgStrikeType = LtgStrikeType.TOTAL_FLASH; } lsp.setType(ltgStrikeType); - // as of OB13.3 for World Wide Lightning Location Network (WWLLN) data (decoded by textlightning though, not this bin lightning decoder), - // added lightning source field in LightningStrikePoint, as well as column in binlightning database table defaults to NLDN + /* + * as of OB13.3 for World Wide Lightning Location Network (WWLLN) + * data (decoded by textlightning though, not this bin lightning + * decoder), added lightning source field in LightningStrikePoint, + * as well as column in binlightning database table defaults to NLDN + */ if (vendor == ((short)0x0001)) { // CONUS source lsp.setLightSource("NLDN"); } else if (vendor == ((short)0x0002)) { // long range source, i.e., GLD360. @@ -185,7 +186,7 @@ public class BinLightningDecoderUtil { lsp.setLightSource("GLD"); } - lsp.setStrikeCount(strokeMultiplicity); + lsp.setPulseCount(strokeMultiplicity); lsp.setStrikeStrength(strokeKiloAmps); // stroke duration does not seem to be used